Audio-Nama-1.216/0000755000175000017500000000000013544212627012502 5ustar jrothjrothAudio-Nama-1.216/MANIFEST0000644000175000017500000000410513544211573013632 0ustar jrothjrothBUILD Changes COPYING inc/Module/Install.pm inc/Module/Install/Base.pm inc/Module/Install/Can.pm inc/Module/Install/Fetch.pm inc/Module/Install/Makefile.pm inc/Module/Install/Metadata.pm inc/Module/Install/Scripts.pm inc/Module/Install/Win32.pm inc/Module/Install/WriteAll.pm Makefile.PL MANIFEST This list of files MYMETA.yml MYMETA.json README script/nama t/01_symbols.t t/02_assign.t t/04_object.t t/06_latency.t t/11_mark.t t/12_nama.t t/data/fake_effects_cache.json lib/Audio/Nama.pm lib/Audio/Nama/Globals.pm lib/Audio/Nama/EcasoundRun.pm lib/Audio/Nama/Modes.pm lib/Audio/Nama/Fade.pm lib/Audio/Nama/MuteSoloFade.pm lib/Audio/Nama/EcasoundSetup.pm lib/Audio/Nama/Lat.pm lib/Audio/Nama/ChainSetup.pm lib/Audio/Nama/Latency.pm lib/Audio/Nama/Edit.pm lib/Audio/Nama/Log.pm lib/Audio/Nama/Wav.pm lib/Audio/Nama/Engine.pm lib/Audio/Nama/CacheTrack.pm lib/Audio/Nama/Mix.pm lib/Audio/Nama/Text.pm lib/Audio/Nama/Object.pm lib/Audio/Nama/Grammar.pm lib/Audio/Nama/Jack.pm lib/Audio/Nama/WavModify.pm lib/Audio/Nama/Midi.pm lib/Audio/Nama/Bunch.pm lib/Audio/Nama/AnalyseLV2.pm lib/Audio/Nama/Mark.pm lib/Audio/Nama/Terminal.pm lib/Audio/Nama/Custom.pm lib/Audio/Nama/TrackWaveform.pm lib/Audio/Nama/EcasoundCleanup.pm lib/Audio/Nama/Help.pm lib/Audio/Nama/Project.pm lib/Audio/Nama/Waveform.pm lib/Audio/Nama/TrackRegion.pm lib/Audio/Nama/Config.pm lib/Audio/Nama/TrackEffect.pm lib/Audio/Nama/Graph.pm lib/Audio/Nama/Persistence.pm lib/Audio/Nama/EngineSetup.pm lib/Audio/Nama/TrackIO.pm lib/Audio/Nama/Bus.pm lib/Audio/Nama/TrackComment.pm lib/Audio/Nama/Insert.pm lib/Audio/Nama/Regions.pm lib/Audio/Nama/Git.pm lib/Audio/Nama/Sequence.pm lib/Audio/Nama/StatusSnapshot.pm lib/Audio/Nama/TrackUtils.pm lib/Audio/Nama/TrackLatency.pm lib/Audio/Nama/Assign.pm lib/Audio/Nama/Initializations.pm lib/Audio/Nama/Util.pm lib/Audio/Nama/EffectChain.pm lib/Audio/Nama/Options.pm lib/Audio/Nama/Track.pm lib/Audio/Nama/BusUtil.pm lib/Audio/Nama/EffectNickname.pm lib/Audio/Nama/Memoize.pm lib/Audio/Nama/Wavinfo.pm lib/Audio/Nama/Effect.pm lib/Audio/Nama/Graphical.pm lib/Audio/Nama/EffectsRegistry.pm lib/Audio/Nama/IO.pm Audio-Nama-1.216/BUILD0000644000175000017500000000322013544211573013260 0ustar jrothjrothBUILD INSTRUCTIONS You may decide to clone the Nama's github repository and from source rather than installing from CPAN. It is easier to browse or hack on Nama this way. Functionality is separated into a number of files, and you will see $::package_var instead of $Audio::Nama::package_var. You can get also updates more quickly and can share patches with other developers. Procedure For typical build and test: cpan Text::Template git-clone git://github.com/bolangi/nama.git cd nama/src ./build ./ui To install the module, do as usual: cd .. perl Makefile.PL make install How it works The build script creates the perl modules for the distribution under the nama/lib directory using *.p, *.pl, *.t and other files in the nama/src directory. build looks into the *.p files for lines that look like: [% somefile.pl %] This notation is analogous to the C-preprocessor #include directive: somefile.pl gets included in the source at that point. Some of these include lines are more complicated: [% qx(./strip_comments ./grammar_body) %] Here the preprocessor runs the script strip_comments on grammar_body, removing text that would choke the parser generator. Build provides a few parameters to the preprocessing script preproc, which uses the Text::Template to perform most of the required substitutions. To see the names of the files and scripts used to build the modules type: ls *.p grep '\[%' * # shows all include directives Audio-Nama-1.216/COPYING0000644000175000017500000010451313114426705013536 0ustar jrothjroth 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 . Audio-Nama-1.216/MYMETA.json0000644000175000017500000000376013544212614014373 0ustar jrothjroth{ "abstract" : "unknown", "author" : [ "Joel Roth, " ], "dynamic_config" : 0, "generated_by" : "ExtUtils::MakeMaker version 7.1, CPAN::Meta::Converter version 2.150001", "license" : [ "open_source" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : "2" }, "name" : "Audio-Nama", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "6.59" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "runtime" : { "requires" : { "AnyEvent" : "5.0", "AnyEvent::TermKey" : "0", "Data::Dumper::Concise" : "0", "Data::Section::Simple" : "0", "Event" : "0", "File::Copy" : "0", "File::Copy::Link" : "0", "File::Find::Rule" : "0", "File::HomeDir" : "0", "File::Slurp" : "0", "File::Temp" : "0", "Git::Repository" : "0", "Graph" : "0", "IO::Select" : "0", "IO::Socket" : "0", "IPC::Open3" : "0", "JSON::XS" : "0", "List::MoreUtils" : "0", "List::Util" : "0", "Log::Log4perl" : "0", "Modern::Perl" : "0", "Module::Load::Conditional" : "0", "Parse::RecDescent" : "0", "Role::Tiny" : "0", "SUPER" : "0", "Term::ReadLine::Gnu" : "0", "Test2::Bundle::More" : "0", "Text::Diff" : "0", "Text::Format" : "0", "Text::Template" : "0", "Time::HiRes" : "0", "Try::Tiny" : "0", "YAML::Tiny" : "0", "autodie" : "0", "perl" : "5.010001" } } }, "release_status" : "stable", "version" : "1.216" } Audio-Nama-1.216/t/0000755000175000017500000000000013544212627012745 5ustar jrothjrothAudio-Nama-1.216/t/01_symbols.t0000644000175000017500000000332613544212613015121 0ustar jrothjrothuse Test2::Bundle::More; use strict; use Audio::Nama::Globals qw($ui); use Audio::Nama::Log; Audio::Nama::Log::initialize_logger(); is($ui, 'bullwinkle', 'global variable import'); package Foo; use Audio::Nama::Globals qw(:all); main::is($ui, 'bullwinkle', 'global variable-all-tag import'); package main; use Audio::Nama::Assign qw(:all); # `make test'. After `make install' it should work as `perl 1.t' #diag ("TESTING $0\n"); my @test_classes = qw( :: main:: main); # SKIP_PREPROC use vars qw( $foo @face $name %dict); my @var_list = qw( $foo @face $name %dict); my $struct2 = { '$foo' => 2, '$name' => 'John', '@face' => [1,5,7,12], '%dict' => {fruit => 'melon'} }; my $struct = { foo => 2, name => 'John', face => [1,5,7,12], dict => {fruit => 'melon'} }; for my $c (@test_classes) { #diag ("testing for class $c"); assign (data => $struct, class => $c, vars => \@var_list); my $serialized = serialize( class => $c, vars => \@var_list); my $expected = < 2, name => undef, face => [], dict => {}, }; #diag("scalar array: ",scalar @face, " scalar hash: ", scalar %dict); assign (data => $nulls, class => 'main', vars => \@var_list); is( scalar @face, 0, "Null array assignment"); is( scalar %dict, 0, "Null hash assignment"); done_testing(); __END__Audio-Nama-1.216/t/11_mark.t0000644000175000017500000000040613544212613014360 0ustar jrothjrothuse Test2::Bundle::More; use strict; use Audio::Nama::Mark; $ENV{NAMA_VERBOSE_TEST_OUTPUT} and diag ("TESTING $0\n"); my $mark = Audio::Nama::Mark->new( name => 'thebeginning'); is( ref $mark , 'Audio::Nama::Mark', "Object creation"); done_testing(); __END__Audio-Nama-1.216/t/06_latency.t0000644000175000017500000000112513544212613015070 0ustar jrothjrothuse Test2::Bundle::More; use strict; use Audio::Nama::Lat; my $lat = Audio::Nama::Lat->new(4,8); my $lat2 = Audio::Nama::Lat->new(16,32); is(ref $lat, 'Audio::Nama::Lat', "Latency object instantiation"); is("$lat","4 8","Stringify object"); is($lat->min, 4, "Min latency accessor"); is_deeply( $lat->add_latency($lat2), Audio::Nama::Lat->new(20,40), "Latency addition"); is_deeply( Audio::Nama::Lat->new(20,40), ($lat + $lat2), "Latency addition, overloading '+' operator"); is(do{ eval {Audio::Nama::Lat->new(1,0)}; defined $@}, 1, "Exception on Max greater than Min"); done_testing(); __END__Audio-Nama-1.216/t/12_nama.t0000644000175000017500000006155513544212613014357 0ustar jrothjrothpackage Audio::Nama; use Audio::Nama; use Test2::Bundle::More; use File::Path qw(make_path remove_tree); use File::Slurp; use Cwd; use strict; use warnings; no warnings qw(uninitialized); our ($expected_setup_lines); $ENV{NAMA_VERBOSE_TEST_OUTPUT} and diag ("TESTING $0\n"); $ENV{NAMA_VERBOSE_TEST_OUTPUT} and diag("working directory: ",cwd); our $test_dir = "/tmp/nama-test"; $fx_cache->{fake} = read_file("t/data/fake_effects_cache.json"); cleanup_dirs(); setup_dirs(); sub cleanup_dirs { chdir('..'), remove_tree($test_dir) if -e $test_dir } sub setup_dirs{ make_path("$test_dir/test/.wav", "$test_dir/untitled/.wav") } $ENV{NAMA_VERBOSE_TEST_OUTPUT} and diag( qx(find $test_dir) ); apply_test_args(); $ENV{NAMA_VERBOSE_TEST_OUTPUT} and diag "options: @ARGV"; bootstrap_environment(); $config->{use_git} = 0; $ENV{NAMA_VERBOSE_TEST_OUTPUT} and diag "Check representative variable from default .namarc"; is( $config->{mix_to_disk_format}, "s16_le,N,44100,i", "Read mix_to_disk_format"); # object id => type mappings # my @id_to_type = ( 1 => 'soundcard', Fluidsynth => 'jack_client', "MPlayer [20120]:out_0" => 'jack_client', "drumkit.ports" => 'jack_ports_list', manual => 'jack_manual', jack => 'jack_manual', bus => 'bus', null => 'null', "loop,16" => 'loop', "loop,Main" => 'loop', ); while( my($dest,$type) = splice @id_to_type, 0,2){ is( dest_type($dest), $type, "$dest => $type"); } my $test_project = 'test'; load_project(name => $test_project, create => 1); $ENV{NAMA_VERBOSE_TEST_OUTPUT} and diag("project project dir: ".project_dir()); $ENV{NAMA_VERBOSE_TEST_OUTPUT} and diag("project project wav dir: ".this_wav_dir()); #diag(map{ $_->dump} values %Audio::Nama::Track::by_index ); is( project_dir(), "$test_dir/$test_project", "establish project directory"); is( ref $bn{Main}, q(Audio::Nama::SubBus), 'Bus initializtion'); force_jack(); ### Unit Tests for Audio::Nama::IO.pm my @io_test_data = split "\n\n", my $yaml = q(--- - class: from_null ecs_string: -i:null - class: to_null ecs_string: -o:null - class: to_wav args: name: sax width: 1 full_path: /foo/.wav/sax_1.wav ecs_string: -f:s16_le,1,44100,i -o:/foo/.wav/sax_1.wav - class: from_wav args: playat_output: playat,5 select_output: select,1,4 modifiers: full_path: test_dir/sax_1.wav ecs_string: -i:playat,5,select,1,4,test_dir/sax_1.wav - class: from_loop args: endpoint: sax_in ecs_string: -i:loop,sax_in - class: to_loop args: endpoint: sax_out ecs_string: -o:loop,sax_out - class: to_alsa_soundcard_device ecs_string: -o:alsa,default - class: from_alsa_soundcard_device ecs_string: -i:alsa,default - class: from_soundcard args: width: 1 source_id: 2 source_type: soundcard ecs_string: -i:jack_multi,system:capture_2 - class: to_soundcard args: width: 2 send_id: 5 send_type: soundcard ecs_string: -o:jack_multi,system:playback_5,system:playback_6 - class: to_jack_port args: width: 1 port_name: sax ecs_string: -f:f32_le,1,44100 -o:jack,,sax_out - class: from_jack_port args: port_name: sax width: 2 ecs_string: -f:f32_le,2,44100 -i:jack,,sax_in - class: from_jack_client args: source_id: Horgand source_type: jack_client ecs_string: -i:jack,Horgand - class: to_jack_client args: send_id: system send_type: jack_client ecs_string: -o:jack,system - class: to_jack_multi args: width: 2 send_id: system send_type: jack_multi ecs_string: -o:jack_multi,system:playback_1,system:playback_2 - class: from_jack_multi args: width: 2 source_id: Horgand source_type: jack_client ecs_string: -i:jack_multi,Horgand:out_1,Horgand:out_2 ...); my @test = @{yaml_in($yaml)}; my $i; for (@test) { my %t = %$_; $i++; $ENV{NAMA_VERBOSE_TEST_OUTPUT} and diag "IO.pm unit test $i"; my $class = "Audio::Nama::IO::$t{class}"; my $io = $class->new(%{$t{args}}); my @keys = sort grep{ $_ ne 'class'} keys %t; is( $io->ecs_string, $t{ecs_string}, "$t{class} ecs_string"); } force_alsa(); nama_cmd('add sax'); like(ref $this_track, qr/Track/, "track creation"); is( $this_track->name, 'sax', "current track assignment"); my ($vol_id) = $this_track->vol; ok( (defined $vol_id and $Audio::Nama::Effect::by_id{$vol_id}) , "apply volume control"); nama_cmd('add_effect time_reverb3'); like( this_op_o()->code, qr/time_reverb3/, "apply preset"); is (this_op_o()->track_effect_index, 0, "positioned before vol/pan faders"); nama_cmd('add_effect decimator 1 2'); like( this_op_o()->code, qr/decimator/, "apply LADSPA effect"); is( this_op_o()->track_effect_index, 1, "position before faders, after other effects"); nama_cmd('vol -2'); is( $this_track->vol_o->params->[0], -2, "modify effect" ); nama_cmd(join " ", 'position_effect', this_op_o()->id, 'ZZZ'); is( $this_track->ops->[-1], this_op_o()->id, 'position effect at end, using ZZZ pseudo-id'); nama_cmd(join " ", 'position_effect', this_op_o()->id, $vol_id); is( $this_track->ops->[this_op_o()->track_effect_index + 1], $vol_id, "position effect before another effect"); my $op_id = this_op_o()->id; nama_cmd("remove_effect $op_id"); ok( (not grep { $_ eq $op_id } @{$this_track->ops}), 'remove effect'); nama_cmd('source 2'); is( $this_track->source_type, 'soundcard', "set soundcard input"); is( $this_track->source_id, 2, "set input channel"); nama_cmd('send 5'); # track sax, source 2, send 5 is( $this_track->send_type, 'soundcard', 'set soundcard output'); is( $this_track->send_id, 5, 'set soundcard output'); # this is ALSA dependent (i.e. no JACK running) my $io = Audio::Nama::IO->new(track => 'sax'); like( ref $io, qr/IO$/, 'IO base class object'); $io = Audio::Nama::IO::to_alsa_soundcard_device->new(track => 'sax'); is($io->ecs_string, '-o:alsa,default', 'IO to_alsa_soundcard_device 1'); is($io->ecs_extra, ' -chmove:1,5', 'IO to_alsa_soundcard_device 2'); $io = Audio::Nama::IO::to_soundcard->new(track => 'sax'); is($io->ecs_string, '-o:alsa,default', 'IO to_soundcard 1'); is($io->ecs_extra, ' -chmove:1,5', 'IO to_soundcard 2'); force_jack(); $io = Audio::Nama::IO::from_soundcard->new(track => 'sax'); like (ref $io, qr/from_jack_multi/, 'sound system ALSA/JACK detection: input'); is($io->ecs_string, '-i:jack_multi,system:capture_2', 'IO from_soundcard: jack 1'); is($io->ecs_extra, '-chcopy:1,2', 'IO from_soundcard: jack 2'); $io = Audio::Nama::IO::to_soundcard->new(track => 'sax'); like (ref $io, qr/to_jack_multi/, 'sound system ALSA/JACK detection: output'); is($io->ecs_string, '-o:jack_multi,system:playback_5', 'IO to_soundcard: jack 1'); ok(! $io->ecs_extra, 'IO to_soundcard: jack 2'); $io = Audio::Nama::IO::to_null->new(track => 'sax', device_id => 'alsa,default'); is($io->device_id, 'alsa,default', 'value overrides method call'); nama_cmd("sax; source Horgand; gen"); like( Audio::Nama::ChainSetup::ecasound_chain_setup(), qr/Horgand/, 'set JACK client as input'); nama_cmd("sax; source jack; gen"); like( Audio::Nama::ChainSetup::ecasound_chain_setup(), qr/jack,,sax_in/, 'set JACK port for manual input'); nama_cmd("sax; rec; source 2"); force_alsa(); nama_cmd('3; nosend; gen'); $expected_setup_lines = < $playat, region_start => $region_start, region_end => $region_end, edit_play_start => $edit_play_start, edit_play_end => $edit_play_end, setup_length => $length, }; is( Audio::Nama::edit_case($args), $case, "$index: $case $comment"); is( Audio::Nama::new_playat($args), $new_playat, "$index: new_playat: $case"); is( Audio::Nama::new_region_start($args), $new_region_start, "$index: new_region_start: $case"); is( Audio::Nama::new_region_end($args), $new_region_end, "$index: new_region_end: $case"); } } load_project(name => "test_project-convert51", create => 1); my $script = < "test_project-crossover", create => 1); $script = < "$test_project-sendbus-cooked", create => 1); do_script(' add mic add guitar for 3 4; mon add_submix_cooked ear 7 '); $expected_setup_lines = < "add_submix_raw", create => 1); nama_cmd("add_tracks mic guitar; for 3 4; mon;; 4 source 2; stereo; add_submix_raw raw-user 7"); $expected_setup_lines = < "$test_project-add_insert_post", create => 1); nama_cmd("add sax; mon; gen"); nama_cmd("add_insert post jconvolver; gen"); $expected_setup_lines = < "add_insert_pre", create => 1); nama_cmd("add sax; mon; add_insert pre jconvolver; gen"); $expected_setup_lines = < "add_insert_via_soundcard-postfader", create => 1); nama_cmd("add sax; mon; source 2; add_insert post 5; gen"); $expected_setup_lines = < "add_insert_via_soundcard_pre", create => 1); nama_cmd("add sax; mon; source 2; add_insert pre 5; gen"); $expected_setup_lines = < "midi", create => 1); #add_midi_track('synth'); sub gen_alsa { force_alsa(); nama_cmd('gen')} sub gen_jack { force_jack(); nama_cmd('gen')} sub force_alsa { $config->{opts}->{A} = 1; $config->{opts}->{J} = 0; $jack->{jackd_running} = 0; } sub force_jack{ $config->{opts}->{A} = 0; $config->{opts}->{J} = 1; $jack->{jackd_running} = 1; } sub setup_content { my @lines = split "\n", shift; my %setup; for (@lines){ next unless /^-a:/; s/\s*$//; $setup{$_}++; } \%setup; } sub check_setup { my $test_name = shift; is( json_out(setup_content(Audio::Nama::ChainSetup::ecasound_chain_setup())), json_out(setup_content($expected_setup_lines)), $test_name); } cleanup_dirs(); done_testing(); __END__Audio-Nama-1.216/t/02_assign.t0000644000175000017500000000341213544212613014712 0ustar jrothjrothuse Test2::Bundle::More; use strict; use Audio::Nama::Assign qw(:all); use Audio::Nama::Log; Audio::Nama::Log::initialize_logger(); # `make test'. After `make install' it should work as `perl 1.t' $ENV{NAMA_VERBOSE_TEST_OUTPUT} and diag ("TESTING $0\n"); my @test_classes = qw( :: main:: main); # SKIP_PREPROC use vars qw( $foo @face $name %dict); my @var_list = qw( $foo @face $name %dict); my $struct2 = { '$foo' => 2, '$name' => 'John', '@face' => [1,5,7,12], '%dict' => {fruit => 'melon'} }; my $struct = { foo => 2, name => 'John', face => [1,5,7,12], dict => {fruit => 'melon'} }; $ENV{NAMA_VERBOSE_TEST_OUTPUT} and diag("Serializing, storing$ENV{NAMA_VERBOSE_TEST_OUTPUT} recalling data"); for my $c (@test_classes) { $ENV{NAMA_VERBOSE_TEST_OUTPUT} and diag ("testing for package $c"); assign (data => $struct, class => $c, vars => \@var_list); #assign($struct, @var_list); #print json_out(\%dict); #print json_out($struct); my $serialized = serialize( class => $c, vars => \@var_list); my $expected = < 2, name => undef, face => [], dict => {}, }; #diag("scalar array: ",scalar @face, " scalar hash: ", scalar %dict); assign (data => $nulls, class => 'main', vars => \@var_list); is( scalar @face, 0, "Null array assignment"); is( scalar %dict, 0, "Null hash assignment"); done_testing(); __END__Audio-Nama-1.216/t/04_object.t0000644000175000017500000000103513544212613014675 0ustar jrothjrothuse Test2::Bundle::More; use strict; $ENV{NAMA_VERBOSE_TEST_OUTPUT} and diag ("TESTING $0\n"); $ENV{NAMA_VERBOSE_TEST_OUTPUT} and diag "testing trivial class Apple"; package Apple; our @ISA; use Audio::Nama::Object qw(color); package main; my $apple = Apple->new(color => 'green'); is( ref $apple, 'Apple', "instantiation") ; is( $apple->color, 'green', "accessor" ); $apple->set( color => 'red' ); is( $apple->color, 'red', "mutator" ); #$apple->color = 'blue'; #is( $apple->color, 'blue', "lvalue" ); done_testing() __END__Audio-Nama-1.216/t/data/0000755000175000017500000000000013544212627013656 5ustar jrothjrothAudio-Nama-1.216/t/data/fake_effects_cache.json0000644000175000017500000136235013114426705020310 0ustar jrothjroth{ "fx_cache" : { "full_label_to_index" : { "" : 0, "chcopy" : 27, "chmix" : 32, "chmove" : 29, "chmute" : 30, "chorder" : 26, "eS" : 1, "ea" : 2, "eac" : 4, "eadb" : 3, "eal" : 5, "eaw" : 6, "ec" : 7, "eca" : 8, "eemb" : 9, "eemp" : 10, "eemt" : 11, "ef1" : 12, "ef3" : 13, "ef4" : 14, "efa" : 15, "efb" : 16, "efc" : 17, "efh" : 18, "efi" : 19, "efl" : 20, "efr" : 21, "efs" : 22, "ei" : 23, "el:Ambisonics-11-cube-decoder" : 238, "el:Ambisonics-11-hexagon-decoder" : 237, "el:Ambisonics-11-mono-panner" : 233, "el:Ambisonics-11-rotator" : 235, "el:Ambisonics-11-square-decoder" : 236, "el:Ambisonics-11-stereo-panner" : 234, "el:Ambisonics-21-panner" : 142, "el:Ambisonics-21-rotator" : 143, "el:Ambisonics-22-panner" : 144, "el:Ambisonics-22-rotator" : 145, "el:Ambisonics-31-panner" : 178, "el:Ambisonics-31-rotator" : 179, "el:Ambisonics-33-panner" : 180, "el:Ambisonics-33-rotator" : 181, "el:AmpVTS" : 98, "el:AutoFilter" : 107, "el:CEO" : 118, "el:CabinetIV" : 99, "el:ChorusI" : 105, "el:Click" : 117, "el:Compress" : 95, "el:CompressX2" : 96, "el:Eq10" : 109, "el:Eq10X2" : 110, "el:Eq4p" : 111, "el:Fractal" : 116, "el:G2reverb" : 192, "el:Narrower" : 113, "el:NoiseGate" : 94, "el:Parametric1" : 128, "el:PhaserII" : 106, "el:Plate" : 100, "el:PlateX2" : 101, "el:Saturate" : 102, "el:Scape" : 108, "el:Sin" : 114, "el:Spice" : 103, "el:SpiceX2" : 104, "el:ToneStack" : 97, "el:Tricardioid-to-AMB" : 227, "el:UHJ-decoder" : 230, "el:UHJ-encoder" : 229, "el:Virtualmic" : 228, "el:White" : 115, "el:Wider" : 112, "el:alias" : 169, "el:allpass_c" : 222, "el:allpass_l" : 221, "el:allpass_n" : 220, "el:amPitchshift" : 156, "el:amp" : 149, "el:amp_mono" : 59, "el:amp_stereo" : 60, "el:analogueOsc" : 54, "el:artificialLatency" : 135, "el:autoPhaser" : 154, "el:bandpass_a_iir" : 48, "el:bandpass_iir" : 134, "el:bodeShifter" : 74, "el:bodeShifterCV" : 213, "el:butthigh_iir" : 66, "el:buttlow_iir" : 65, "el:bwxover_iir" : 64, "el:chebstortion" : 214, "el:comb" : 209, "el:combSplitter" : 218, "el:comb_c" : 71, "el:comb_l" : 70, "el:comb_n" : 69, "el:const" : 150, "el:crossoverDist" : 232, "el:dcRemove" : 177, "el:decay" : 63, "el:decimator" : 138, "el:declip" : 133, "el:delay_5s" : 146, "el:delay_c" : 88, "el:delay_l" : 87, "el:delay_n" : 86, "el:delayorama" : 68, "el:diode" : 242, "el:divider" : 216, "el:djFlanger" : 166, "el:dj_eq" : 165, "el:dj_eq_mono" : 164, "el:dysonCompress" : 167, "el:fadDelay" : 121, "el:fastLookaheadLimiter" : 90, "el:flanger" : 196, "el:fmOsc" : 83, "el:foldover" : 183, "el:fourByFourPole" : 153, "el:foverdrive" : 67, "el:freqTracker" : 171, "el:gate" : 162, "el:giantFlange" : 141, "el:gong" : 159, "el:gongBeater" : 85, "el:gsm" : 174, "el:gverb" : 170, "el:hardLimiter" : 184, "el:harmonicGen" : 127, "el:hermesFilter" : 73, "el:highpass_iir" : 190, "el:hilbert" : 49, "el:hpf" : 206, "el:imp" : 189, "el:impulse_fc" : 91, "el:inv" : 55, "el:invada_hp_mono_filter_module_0_1" : 51, "el:invada_hp_stereo_filter_module_0_1" : 53, "el:invada_lp_mono_filter_module_0_1" : 50, "el:invada_lp_stereo_filter_module_0_1" : 52, "el:invada_mono_compressor_module_0_1" : 160, "el:invada_mono_reverbER_module_0_1" : 187, "el:invada_mono_tube_module_0_1" : 131, "el:invada_stereo_compressor_module_0_1" : 161, "el:invada_stereo_input_module_0_1" : 148, "el:invada_stereo_tube_module_0_1" : 132, "el:invada_sum_reverbER_module_0_1" : 188, "el:karaoke" : 176, "el:lcrDelay" : 61, "el:lfoPhaser" : 152, "el:lowpass_iir" : 92, "el:lpf" : 205, "el:lsFilter" : 185, "el:matrixMSSt" : 130, "el:matrixSpatialiser" : 186, "el:matrixStMS" : 151, "el:mbeq" : 139, "el:modDelay" : 217, "el:multivoiceChorus" : 82, "el:noise_white" : 241, "el:notch_iir" : 226, "el:pitchScale" : 191, "el:pitchScaleHQ" : 231, "el:plate" : 199, "el:pointerCastDistortion" : 172, "el:rateShifter" : 147, "el:retroFlange" : 126, "el:revdelay" : 175, "el:ringmod_1i1o1l" : 208, "el:ringmod_2i1o" : 207, "el:satanMaximiser" : 136, "el:sc1" : 182, "el:sc2" : 219, "el:sc3" : 72, "el:sc4" : 58, "el:sc4m" : 84, "el:se4" : 155, "el:shaper" : 211, "el:sifter" : 225, "el:sinCos" : 224, "el:sine_faaa" : 75, "el:sine_faac" : 76, "el:sine_fcaa" : 77, "el:sine_fcac" : 78, "el:singlePara" : 137, "el:sinusWavewrapper" : 168, "el:smoothDecimate" : 123, "el:split" : 198, "el:stepMuxer" : 163, "el:surroundEncoder" : 215, "el:svf" : 140, "el:tap_autopan" : 195, "el:tap_chorusflanger" : 124, "el:tap_deesser" : 212, "el:tap_doubler" : 158, "el:tap_dynamics_m" : 201, "el:tap_dynamics_st" : 204, "el:tap_equalizer" : 129, "el:tap_equalizer_bw" : 173, "el:tap_limiter" : 79, "el:tap_pinknoise" : 239, "el:tap_pitch" : 93, "el:tap_reflector" : 240, "el:tap_reverb" : 197, "el:tap_rotspeak" : 80, "el:tap_sigmoid" : 62, "el:tap_stereo_echo" : 122, "el:tap_tremolo" : 56, "el:tap_tubewarmth" : 223, "el:tap_vibrato" : 89, "el:tapeDelay" : 210, "el:transient" : 119, "el:triplePara" : 200, "el:valve" : 57, "el:valveRect" : 157, "el:vynil" : 125, "el:waveTerrain" : 120, "el:xfade" : 202, "el:xfade4" : 203, "el:zita-reverb" : 193, "el:zita-reverb-amb" : 194, "el:zm1" : 81, "elv2:http://gareus.org/oss/lv2/b_overdrive" : 307, "elv2:http://gareus.org/oss/lv2/b_reverb" : 308, "elv2:http://gareus.org/oss/lv2/b_whirl#extended" : 309, "elv2:http://gareus.org/oss/lv2/b_whirl#simple" : 310, "elv2:http://hyperglitch.com/dev/VocProc" : 311, "elv2:http://plugin.org.uk/swh-plugins/alaw" : 312, "elv2:http://plugin.org.uk/swh-plugins/alias" : 313, "elv2:http://plugin.org.uk/swh-plugins/allpass_c" : 314, "elv2:http://plugin.org.uk/swh-plugins/allpass_l" : 315, "elv2:http://plugin.org.uk/swh-plugins/allpass_n" : 316, "elv2:http://plugin.org.uk/swh-plugins/amPitchshift" : 317, "elv2:http://plugin.org.uk/swh-plugins/amp" : 318, "elv2:http://plugin.org.uk/swh-plugins/analogueOsc" : 319, "elv2:http://plugin.org.uk/swh-plugins/artificialLatency" : 320, "elv2:http://plugin.org.uk/swh-plugins/autoPhaser" : 321, "elv2:http://plugin.org.uk/swh-plugins/bandpass_a_iir" : 322, "elv2:http://plugin.org.uk/swh-plugins/bandpass_iir" : 323, "elv2:http://plugin.org.uk/swh-plugins/bodeShifter" : 324, "elv2:http://plugin.org.uk/swh-plugins/bodeShifterCV" : 325, "elv2:http://plugin.org.uk/swh-plugins/butthigh_iir" : 326, "elv2:http://plugin.org.uk/swh-plugins/buttlow_iir" : 327, "elv2:http://plugin.org.uk/swh-plugins/bwxover_iir" : 328, "elv2:http://plugin.org.uk/swh-plugins/chebstortion" : 329, "elv2:http://plugin.org.uk/swh-plugins/comb" : 330, "elv2:http://plugin.org.uk/swh-plugins/combSplitter" : 331, "elv2:http://plugin.org.uk/swh-plugins/comb_c" : 332, "elv2:http://plugin.org.uk/swh-plugins/comb_l" : 333, "elv2:http://plugin.org.uk/swh-plugins/comb_n" : 334, "elv2:http://plugin.org.uk/swh-plugins/const" : 335, "elv2:http://plugin.org.uk/swh-plugins/crossoverDist" : 336, "elv2:http://plugin.org.uk/swh-plugins/dcRemove" : 337, "elv2:http://plugin.org.uk/swh-plugins/decay" : 338, "elv2:http://plugin.org.uk/swh-plugins/decimator" : 339, "elv2:http://plugin.org.uk/swh-plugins/declip" : 340, "elv2:http://plugin.org.uk/swh-plugins/delay_c" : 341, "elv2:http://plugin.org.uk/swh-plugins/delay_l" : 342, "elv2:http://plugin.org.uk/swh-plugins/delay_n" : 343, "elv2:http://plugin.org.uk/swh-plugins/delayorama" : 344, "elv2:http://plugin.org.uk/swh-plugins/diode" : 345, "elv2:http://plugin.org.uk/swh-plugins/divider" : 346, "elv2:http://plugin.org.uk/swh-plugins/djFlanger" : 347, "elv2:http://plugin.org.uk/swh-plugins/dj_eq" : 348, "elv2:http://plugin.org.uk/swh-plugins/dj_eq_mono" : 349, "elv2:http://plugin.org.uk/swh-plugins/dysonCompress" : 350, "elv2:http://plugin.org.uk/swh-plugins/fadDelay" : 351, "elv2:http://plugin.org.uk/swh-plugins/fastLookaheadLimiter" : 352, "elv2:http://plugin.org.uk/swh-plugins/flanger" : 353, "elv2:http://plugin.org.uk/swh-plugins/fmOsc" : 354, "elv2:http://plugin.org.uk/swh-plugins/foldover" : 355, "elv2:http://plugin.org.uk/swh-plugins/fourByFourPole" : 356, "elv2:http://plugin.org.uk/swh-plugins/foverdrive" : 357, "elv2:http://plugin.org.uk/swh-plugins/freqTracker" : 358, "elv2:http://plugin.org.uk/swh-plugins/gate" : 359, "elv2:http://plugin.org.uk/swh-plugins/giantFlange" : 360, "elv2:http://plugin.org.uk/swh-plugins/gong" : 361, "elv2:http://plugin.org.uk/swh-plugins/gongBeater" : 362, "elv2:http://plugin.org.uk/swh-plugins/gverb" : 363, "elv2:http://plugin.org.uk/swh-plugins/hardLimiter" : 364, "elv2:http://plugin.org.uk/swh-plugins/harmonicGen" : 365, "elv2:http://plugin.org.uk/swh-plugins/hermesFilter" : 366, "elv2:http://plugin.org.uk/swh-plugins/highpass_iir" : 367, "elv2:http://plugin.org.uk/swh-plugins/hilbert" : 368, "elv2:http://plugin.org.uk/swh-plugins/impulse_fc" : 369, "elv2:http://plugin.org.uk/swh-plugins/inv" : 370, "elv2:http://plugin.org.uk/swh-plugins/karaoke" : 371, "elv2:http://plugin.org.uk/swh-plugins/lcrDelay" : 372, "elv2:http://plugin.org.uk/swh-plugins/lfoPhaser" : 373, "elv2:http://plugin.org.uk/swh-plugins/lookaheadLimiter" : 374, "elv2:http://plugin.org.uk/swh-plugins/lookaheadLimiterConst" : 375, "elv2:http://plugin.org.uk/swh-plugins/lowpass_iir" : 376, "elv2:http://plugin.org.uk/swh-plugins/lsFilter" : 377, "elv2:http://plugin.org.uk/swh-plugins/matrixMSSt" : 378, "elv2:http://plugin.org.uk/swh-plugins/matrixSpatialiser" : 379, "elv2:http://plugin.org.uk/swh-plugins/matrixStMS" : 380, "elv2:http://plugin.org.uk/swh-plugins/mbeq" : 381, "elv2:http://plugin.org.uk/swh-plugins/modDelay" : 382, "elv2:http://plugin.org.uk/swh-plugins/multivoiceChorus" : 383, "elv2:http://plugin.org.uk/swh-plugins/pitchScaleHQ" : 384, "elv2:http://plugin.org.uk/swh-plugins/plate" : 385, "elv2:http://plugin.org.uk/swh-plugins/pointerCastDistortion" : 386, "elv2:http://plugin.org.uk/swh-plugins/rateShifter" : 387, "elv2:http://plugin.org.uk/swh-plugins/retroFlange" : 388, "elv2:http://plugin.org.uk/swh-plugins/revdelay" : 389, "elv2:http://plugin.org.uk/swh-plugins/ringmod_1i1o1l" : 390, "elv2:http://plugin.org.uk/swh-plugins/ringmod_2i1o" : 391, "elv2:http://plugin.org.uk/swh-plugins/satanMaximiser" : 392, "elv2:http://plugin.org.uk/swh-plugins/sc1" : 393, "elv2:http://plugin.org.uk/swh-plugins/sc2" : 394, "elv2:http://plugin.org.uk/swh-plugins/sc3" : 395, "elv2:http://plugin.org.uk/swh-plugins/sc4" : 396, "elv2:http://plugin.org.uk/swh-plugins/se4" : 397, "elv2:http://plugin.org.uk/swh-plugins/shaper" : 398, "elv2:http://plugin.org.uk/swh-plugins/sifter" : 399, "elv2:http://plugin.org.uk/swh-plugins/sinCos" : 400, "elv2:http://plugin.org.uk/swh-plugins/singlePara" : 401, "elv2:http://plugin.org.uk/swh-plugins/sinusWavewrapper" : 402, "elv2:http://plugin.org.uk/swh-plugins/smoothDecimate" : 403, "elv2:http://plugin.org.uk/swh-plugins/split" : 404, "elv2:http://plugin.org.uk/swh-plugins/surroundEncoder" : 405, "elv2:http://plugin.org.uk/swh-plugins/svf" : 406, "elv2:http://plugin.org.uk/swh-plugins/tapeDelay" : 407, "elv2:http://plugin.org.uk/swh-plugins/transient" : 408, "elv2:http://plugin.org.uk/swh-plugins/triplePara" : 409, "elv2:http://plugin.org.uk/swh-plugins/ulaw" : 410, "elv2:http://plugin.org.uk/swh-plugins/valve" : 411, "elv2:http://plugin.org.uk/swh-plugins/valveRect" : 412, "elv2:http://plugin.org.uk/swh-plugins/vynil" : 413, "elv2:http://plugin.org.uk/swh-plugins/waveTerrain" : 414, "elv2:http://plugin.org.uk/swh-plugins/xfade" : 415, "elv2:http://plugin.org.uk/swh-plugins/xfade4" : 416, "elv2:http://plugin.org.uk/swh-plugins/zm1" : 417, "enm" : 24, "epp" : 25, "erc" : 28, "erm" : 31, "etc" : 33, "etd" : 34, "ete" : 35, "etf" : 36, "etl" : 37, "etm" : 38, "etp" : 39, "etr" : 40, "ev" : 41, "evp" : 42, "ezf" : 43, "ezx" : 44, "gc" : 45, "ge" : 46, "gm" : 47, "kf" : 299, "kl" : 301, "kl2" : 302, "klg" : 303, "km" : 304, "kog" : 300, "kos" : 305, "ksv" : 306, "pn:dyn_compress_brutal" : 243, "pn:dyn_compress_hard" : 244, "pn:dyn_compress_infinite" : 245, "pn:dyn_compress_medium" : 246, "pn:dyn_compress_soft" : 247, "pn:dyn_compress_supersoft" : 248, "pn:eq_template" : 249, "pn:eq_template2" : 250, "pn:f_bandpass" : 251, "pn:f_filtertest" : 252, "pn:f_high_and_low" : 253, "pn:f_highpass" : 254, "pn:f_inverse_comb" : 255, "pn:f_lowp_sine" : 256, "pn:f_lowp_sine2" : 257, "pn:f_lowpass" : 258, "pn:f_rejectband" : 259, "pn:f_res_bandpass" : 260, "pn:f_res_lowpass" : 261, "pn:f_resonator" : 262, "pn:f_two_filters" : 263, "pn:f_two_filters_pareq" : 264, "pn:gate_crop" : 265, "pn:gate_noisegate_1" : 266, "pn:gate_noisegate_delanalog" : 267, "pn:gate_threshold" : 268, "pn:lad_hermes" : 269, "pn:lad_metronome" : 270, "pn:lad_oscillator_stack" : 271, "pn:lad_oscillator_test" : 272, "pn:lad_sc4" : 273, "pn:lad_sc4_rg" : 274, "pn:metronome" : 275, "pn:time_chorus1" : 276, "pn:time_delay1" : 277, "pn:time_delay2" : 278, "pn:time_flanger1" : 279, "pn:time_phaser1" : 280, "pn:time_reverb1" : 281, "pn:time_reverb2" : 282, "pn:time_reverb3" : 283, "pn:time_reverb4" : 284, "pn:time_wicked_dub" : 285, "pn:var_aw" : 286, "pn:var_aw_custom" : 287, "pn:var_aw_ksv" : 288, "pn:var_aw_tri" : 289, "pn:var_aw_tri_custom" : 290, "pn:var_chipmunk" : 291, "pn:var_dali" : 292, "pn:var_molten_tape" : 293, "pn:var_paralmadness" : 294, "pn:var_parchip" : 295, "pn:var_stretched_tape" : 296, "pn:var_sweeping_pan" : 297, "pn:var_switching_pan" : 298 }, "ladspa" : {}, "ladspa_label_to_unique_id" : {}, "ladspa_sorted" : [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 153, 128, 160, 161, 187, 188, 51, 53, 50, 52, 148, 131, 132, 156, 238, 237, 233, 235, 236, 234, 142, 143, 144, 145, 178, 179, 180, 181, 169, 222, 221, 220, 54, 135, 216, 154, 136, 74, 213, 98, 107, 118, 99, 105, 117, 95, 96, 109, 110, 111, 116, 113, 94, 106, 100, 101, 102, 108, 114, 103, 104, 97, 115, 112, 214, 209, 218, 71, 70, 69, 150, 202, 203, 232, 177, 165, 164, 166, 138, 133, 68, 242, 167, 63, 83, 90, 67, 196, 183, 121, 171, 66, 65, 174, 170, 162, 141, 48, 134, 64, 190, 92, 85, 159, 184, 127, 73, 231, 49, 189, 55, 176, 61, 152, 185, 226, 186, 130, 151, 217, 59, 198, 139, 82, 91, 191, 199, 172, 147, 126, 175, 208, 207, 182, 219, 72, 58, 84, 155, 225, 146, 206, 205, 149, 88, 87, 86, 224, 75, 76, 77, 78, 137, 168, 123, 140, 163, 60, 192, 215, 195, 124, 212, 201, 204, 129, 173, 158, 239, 93, 240, 197, 80, 79, 62, 122, 56, 223, 89, 210, 227, 119, 200, 230, 229, 157, 57, 228, 125, 120, 211, 241, 81, 193, 194 ], "lv2_help" : { "elv2:http://gareus.org/oss/lv2/b_overdrive" : "Name: B Organ Overdrive\nURI: http://gareus.org/oss/lv2/b_overdrive\nClass: Distortion\nAuthor: Robin Gareus\nLatency: no\nPorts: \"In\" Input, Audio\n\t\"Out\" Output, Audio\n\t\"Bias\" Input, Control, 0 to 1, default 0.87399\n\t\"Feedback\" Input, Control, 0 to 1, default 0.5821\n\t\"SagToBias\" Input, Control, 0 to 1, default 0.188\n\t\"Postdiff feedback\" Input, Control, 0 to 1, default 1\n\t\"Global feedback\" Input, Control, 0 to 1, default 0.5826\n\t\"Input Gain\" Input, Control, 0 to 1, default 0.3567\n\t\"Output Gain\" Input, Control, 0 to 1, default 0.07873\n\n", "elv2:http://gareus.org/oss/lv2/b_reverb" : "Name: B Organ Reverb\nURI: http://gareus.org/oss/lv2/b_reverb\nClass: Spatial\nAuthor: Robin Gareus\nLatency: no\nPorts: \"In\" Input, Audio\n\t\"Out\" Output, Audio\n\t\"Dry/Wet\" Input, Control, 0 to 1, default 0.3\n\t\"Input Gain\" Input, Control, 0 to 1, default 0.025\n\n", "elv2:http://gareus.org/oss/lv2/b_synth" : "Name: setBfree DSP Tonewheel Organ\nURI: http://gareus.org/oss/lv2/b_synth\nClass: Instrument\nAuthor: Robin Gareus\nLatency: no\nPorts: \"MIDI In\" Input, \n\t\"MIDI Out\" Output, \n\t\"Left output\" Output, Audio\n\t\"Right Output\" Output, Audio\n\n", "elv2:http://gareus.org/oss/lv2/b_whirl#extended" : "Name: B Organ Whirl Speaker Extended Version\nURI: http://gareus.org/oss/lv2/b_whirl#extended\nClass: Simulator\nAuthor: Robin Gareus\nLatency: no\nPorts: \"In\" Input, Audio\n\t\"Left output\" Output, Audio\n\t\"Right output\" Output, Audio\n\t\"Motors (horn, drum speed: off/slow/fast)\" Input, Control, 0 to 8, default 4, 9-way Selector\n\t\"Horn Level [dB]\" Input, Control, -20 to 20, default 0\n\t\"Drum Level [dB]\" Input, Control, -20 to 20, default 0\n\t\"Drum Stereo Width\" Input, Control, 0 to 1, default 1, 2-way Selector\n\t\"Horn Speed Slow [rpm]\" Input, Control, 10 to 200, default 40.32\n\t\"Horn Speed Fast [rpm]\" Input, Control, 100 to 1000, default 423.359985\n\t\"Horn Acceleration [s]\" Input, Control, 0.001 to 10, default 0.161\n\t\"Horn Deceleration [s]\" Input, Control, 0.001 to 10, default 0.321\n\t\"Horn Brake\" Input, Control, 0 to 1, default 0, 2-way Selector\n\t\"Horn Filter-1 Type:\" Input, Control, 0 to 8, default 0, 9-way Selector\n\t\"Horn Filter-1 Frequency [Hz]\" Input, Control, 250 to 8000, default 4500\n\t\"Horn Filter-1 Quality\" Input, Control, 0.01 to 6, default 2.7456\n\t\"Horn Filter-1 Gain (shelf) [dB]\" Input, Control, -48 to 48, default -38.9291\n\t\"Horn Filter-2 Type:\" Input, Control, 0 to 8, default 7, 9-way Selector\n\t\"Horn Filter-2 Frequency [Hz]\" Input, Control, 250 to 8000, default 300\n\t\"Horn Filter-2 Quality\" Input, Control, 0.01 to 6, default 1\n\t\"Horn Filter-2 Gain (shelf) [dB]\" Input, Control, -48 to 48, default -30\n\t\"Drum Speed Slow [rpm]\" Input, Control, 5 to 100, default 36\n\t\"Drum Speed Fast [rpm]\" Input, Control, 60 to 600, default 357.299988\n\t\"Drum Acceleration [s]\" Input, Control, 0.01 to 20, default 4.127\n\t\"Drum Deceleration [s]\" Input, Control, 0.01 to 20, default 1.371\n\t\"Drum Brake Position\" Input, Control, 0 to 1, default 0, 2-way Selector\n\t\"Drum Filter Type:\" Input, Control, 0 to 8, default 8, 9-way Selector\n\t\"Drum Filter Frequency [Hz]\" Input, Control, 50 to 8000, default 811.969482\n\t\"Drum Filter Quality\" Input, Control, 1.6016 to 6, default 1\n\t\"Drum Filter Gain (shelf) [dB]\" Input, Control, -48 to 48, default -38.9291\n\n", "elv2:http://gareus.org/oss/lv2/b_whirl#simple" : "Name: B Organ Whirl Speaker\nURI: http://gareus.org/oss/lv2/b_whirl#simple\nClass: Simulator\nAuthor: Robin Gareus\nLatency: no\nPorts: \"In\" Input, Audio\n\t\"Left output\" Output, Audio\n\t\"Right output\" Output, Audio\n\t\"Motors (horn, drum speed: off/slow/fast)\" Input, Control, 0 to 8, default 4, 9-way Selector\n\t\"Horn Level [dB]\" Input, Control, -20 to 20, default 0\n\t\"Drum Level [dB]\" Input, Control, -20 to 20, default 0\n\t\"Drum Stereo Width\" Input, Control, 0 to 1, default 1, 2-way Selector\n\n", "elv2:http://hyperglitch.com/dev/VocProc" : "Name: VocProc\nURI: http://hyperglitch.com/dev/VocProc\nClass: Pitch Shifter\nAuthor: Igor Brkic\nLatency: no\nPorts: \"Voice input\" Input, Audio\n\t\"Carrier input\" Input, Audio\n\t\"Output\" Output, Audio\n\t\"Pitch Factor\" Input, Control, -12 to 12, default 0\n\t\"Robotize/Whisperize\" Input, Control, 0 to 1, default 0\n\t\"formant correction/vocoder\" Input, Control, 0 to 1, default 0, 2-way Selector\n\t\"0 - formant correction, 1 - vocoder\" Input, Control, 0 to 1, default 0, 2-way Selector\n\t\"Automatic pitch correction\" Input, Control, 0 to 1, default 0, 2-way Selector\n\t\"Threshold\" Input, Control, 0 to 1, default 0\n\t\"Attack\" Input, Control, 0 to 1, default 0\n\t\"Transpose\" Input, Control, -12 to 12, default 0\n\t\"C\" Input, Control, 0 to 1, default 1, 2-way Selector\n\t\"C#\" Input, Control, 0 to 1, default 1, 2-way Selector\n\t\"D\" Input, Control, 0 to 1, default 1, 2-way Selector\n\t\"D#\" Input, Control, 0 to 1, default 1, 2-way Selector\n\t\"E\" Input, Control, 0 to 1, default 1, 2-way Selector\n\t\"F\" Input, Control, 0 to 1, default 1, 2-way Selector\n\t\"F#\" Input, Control, 0 to 1, default 1, 2-way Selector\n\t\"G\" Input, Control, 0 to 1, default 1, 2-way Selector\n\t\"G#\" Input, Control, 0 to 1, default 1, 2-way Selector\n\t\"A\" Input, Control, 0 to 1, default 1, 2-way Selector\n\t\"A#\" Input, Control, 0 to 1, default 1, 2-way Selector\n\t\"B\" Input, Control, 0 to 1, default 1, 2-way Selector\n\t\"Offset from tone\" Output, Control, -100 to 100, default 0\n\n", "elv2:http://plugin.org.uk/swh-plugins/alaw" : "Name: A-Law Compressor\nURI: http://plugin.org.uk/swh-plugins/alaw\nClass: Dynamics\nAuthor: Steve Harris\nLatency: no\nPorts: \"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/alias" : "Name: Aliasing\nURI: http://plugin.org.uk/swh-plugins/alias\nClass: Distortion\nAuthor: Steve Harris\nLatency: no\nPorts: \"Aliasing level\" Input, Control, 0 to 1, default 0\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/allpass_c" : "Name: Allpass delay line, cubic spline interpolation\nURI: http://plugin.org.uk/swh-plugins/allpass_c\nClass: Delay\nAuthor: Steve Harris\nLatency: no\nPorts: \"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\t\"Max Delay (s)\" Input, Control, 0 to 10, default 1\n\t\"Delay Time (s)\" Input, Control, 0 to 10, default 0\n\t\"Decay Time (s)\" Input, Control, 0 to 10, default 0\n\n", "elv2:http://plugin.org.uk/swh-plugins/allpass_l" : "Name: Allpass delay line, linear interpolation\nURI: http://plugin.org.uk/swh-plugins/allpass_l\nClass: Delay\nAuthor: Steve Harris\nLatency: no\nPorts: \"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\t\"Max Delay (s)\" Input, Control, 0 to 10, default 1\n\t\"Delay Time (s)\" Input, Control, 0 to 10, default 0\n\t\"Decay Time (s)\" Input, Control, 0 to 10, default 0\n\n", "elv2:http://plugin.org.uk/swh-plugins/allpass_n" : "Name: Allpass delay line, noninterpolating\nURI: http://plugin.org.uk/swh-plugins/allpass_n\nClass: Delay\nAuthor: Steve Harris\nLatency: no\nPorts: \"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\t\"Max Delay (s)\" Input, Control, 0 to 10, default 1\n\t\"Delay Time (s)\" Input, Control, 0 to 10, default 0\n\t\"Decay Time (s)\" Input, Control, 0 to 10, default 0\n\n", "elv2:http://plugin.org.uk/swh-plugins/amPitchshift" : "Name: AM pitchshifter\nURI: http://plugin.org.uk/swh-plugins/amPitchshift\nClass: Pitch Shifter\nAuthor: Steve Harris\nLatency: yes, reported by port 4\nPorts: \"Pitch shift\" Input, Control, 0.25 to 4, default 1\n\t\"Buffer size\" Input, Control, 1 to 7, default 4\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\t\"latency\" Output, Control\n\n", "elv2:http://plugin.org.uk/swh-plugins/amp" : "Name: Simple amplifier\nURI: http://plugin.org.uk/swh-plugins/amp\nClass: Amplifier\nAuthor: Steve Harris\nLatency: no\nPorts: \"Amps gain (dB)\" Input, Control, -70 to 70, default 0\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/analogueOsc" : "Name: Analogue Oscillator\nURI: http://plugin.org.uk/swh-plugins/analogueOsc\nClass: Oscillator\nAuthor: Steve Harris\nLatency: no\nPorts: \"Waveform (1=sin, 2=tri, 3=squ, 4=saw)\" Input, Control, 1 to 4, default 1\n\t\"Frequency (Hz)\" Input, Control, 0.000001 to 0.499, default 440\n\t\"Warmth\" Input, Control, 0 to 1, default 0\n\t\"Instability\" Input, Control, 0 to 1, default 0\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/artificialLatency" : "Name: Artificial latency\nURI: http://plugin.org.uk/swh-plugins/artificialLatency\nClass: Utility\nAuthor: Steve Harris\nLatency: yes, reported by port 3\nPorts: \"Delay (ms)\" Input, Control, 0 to 10000, default 2500\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\t\"latency\" Output, Control\n\n", "elv2:http://plugin.org.uk/swh-plugins/autoPhaser" : "Name: Auto phaser\nURI: http://plugin.org.uk/swh-plugins/autoPhaser\nClass: Phaser\nAuthor: Steve Harris\nLatency: no\nPorts: \"Attack time (s)\" Input, Control, 0 to 1, default 0.25\n\t\"Decay time (s)\" Input, Control, 0 to 1, default 0.25\n\t\"Modulation depth\" Input, Control, 0 to 1, default 0.25\n\t\"Feedback\" Input, Control, -1 to 1, default 0\n\t\"Spread (octaves)\" Input, Control, 0 to 2, default 1\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/bandpass_a_iir" : "Name: Glame Bandpass Analog Filter\nURI: http://plugin.org.uk/swh-plugins/bandpass_a_iir\nClass: Bandpass\nAuthor: Steve Harris\nLatency: no\nPorts: \"Center Frequency (Hz)\" Input, Control, 0.0001 to 0.45, default 0.112575\n\t\"Bandwidth (Hz)\" Input, Control, 0.0001 to 0.45, default 0.22505\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/bandpass_iir" : "Name: Glame Bandpass Filter\nURI: http://plugin.org.uk/swh-plugins/bandpass_iir\nClass: Bandpass\nAuthor: Steve Harris\nLatency: no\nPorts: \"Center Frequency (Hz)\" Input, Control, 0.0001 to 0.45, default 0.22505\n\t\"Bandwidth (Hz)\" Input, Control, 0.0001 to 0.45, default 0.22505\n\t\"Stages(2 poles per stage)\" Input, Control, 1 to 10, default 1\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/bodeShifter" : "Name: Bode frequency shifter\nURI: http://plugin.org.uk/swh-plugins/bodeShifter\nClass: Spectral\nAuthor: Steve Harris\nLatency: yes, reported by port 4\nPorts: \"Frequency shift\" Input, Control, 0 to 5000, default 0\n\t\"Input\" Input, Audio\n\t\"Down out\" Output, Audio\n\t\"Up out\" Output, Audio\n\t\"latency\" Output, Control\n\n", "elv2:http://plugin.org.uk/swh-plugins/bodeShifterCV" : "Name: Bode frequency shifter (CV)\nURI: http://plugin.org.uk/swh-plugins/bodeShifterCV\nClass: Spectral\nAuthor: Steve Harris\nLatency: yes, reported by port 8\nPorts: \"Base shift\" Input, Control, 0 to 5000, default 0\n\t\"Mix (-1=down, +1=up)\" Input, Control, -1 to 1, default 0\n\t\"Input\" Input, Audio\n\t\"CV Attenuation\" Input, Control, 0 to 1, default 1\n\t\"Shift CV\" Input, Audio\n\t\"Down out\" Output, Audio\n\t\"Up out\" Output, Audio\n\t\"Mix out\" Output, Audio\n\t\"latency\" Output, Control\n\n", "elv2:http://plugin.org.uk/swh-plugins/butthigh_iir" : "Name: GLAME Butterworth Highpass\nURI: http://plugin.org.uk/swh-plugins/butthigh_iir\nClass: Highpass\nAuthor: Steve Harris\nLatency: no\nPorts: \"Cutoff Frequency (Hz)\" Input, Control, 0.0001 to 0.45, default 0.112575\n\t\"Resonance\" Input, Control, 0.1 to 1.41, default 0.755\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/buttlow_iir" : "Name: GLAME Butterworth Lowpass\nURI: http://plugin.org.uk/swh-plugins/buttlow_iir\nClass: Lowpass\nAuthor: Steve Harris\nLatency: no\nPorts: \"Cutoff Frequency (Hz)\" Input, Control, 0.0001 to 0.45, default 0.112575\n\t\"Resonance\" Input, Control, 0.1 to 1.41, default 0.755\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/bwxover_iir" : "Name: Glame Butterworth X-over Filter\nURI: http://plugin.org.uk/swh-plugins/bwxover_iir\nClass: Bandpass\nAuthor: Steve Harris\nLatency: no\nPorts: \"Cutoff Frequency (Hz)\" Input, Control, 0.0001 to 0.45, default 0.112575\n\t\"Resonance\" Input, Control, 0.1 to 1.41, default 0.755\n\t\"Input\" Input, Audio\n\t\"LP-Output\" Output, Audio\n\t\"HP-Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/chebstortion" : "Name: Chebyshev distortion\nURI: http://plugin.org.uk/swh-plugins/chebstortion\nClass: Distortion\nAuthor: Steve Harris\nLatency: no\nPorts: \"Distortion\" Input, Control, 0 to 3, default 0\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/comb" : "Name: Comb Filter\nURI: http://plugin.org.uk/swh-plugins/comb\nClass: Comb\nAuthor: Steve Harris\nLatency: no\nPorts: \"Band separation (Hz)\" Input, Control, 16 to 640, default 172\n\t\"Feedback\" Input, Control, -0.99 to 0.99, default 0\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/combSplitter" : "Name: Comb Splitter\nURI: http://plugin.org.uk/swh-plugins/combSplitter\nClass: Comb\nAuthor: Steve Harris\nLatency: no\nPorts: \"Band separation (Hz)\" Input, Control, 16 to 640, default 172\n\t\"Input\" Input, Audio\n\t\"Output 1\" Output, Audio\n\t\"Output 2\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/comb_c" : "Name: Comb delay line, cubic spline interpolation\nURI: http://plugin.org.uk/swh-plugins/comb_c\nClass: Delay\nAuthor: Steve Harris\nLatency: no\nPorts: \"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\t\"Max Delay (s)\" Input, Control, 0 to 10, default 1\n\t\"Delay Time (s)\" Input, Control, 0 to 10, default 0\n\t\"Decay Time (s)\" Input, Control, 0 to 10, default 0\n\n", "elv2:http://plugin.org.uk/swh-plugins/comb_l" : "Name: Comb delay line, linear interpolation\nURI: http://plugin.org.uk/swh-plugins/comb_l\nClass: Delay\nAuthor: Steve Harris\nLatency: no\nPorts: \"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\t\"Max Delay (s)\" Input, Control, 0 to 10, default 1\n\t\"Delay Time (s)\" Input, Control, 0 to 10, default 0\n\t\"Decay Time (s)\" Input, Control, 0 to 10, default 0\n\n", "elv2:http://plugin.org.uk/swh-plugins/comb_n" : "Name: Comb delay line, noninterpolating\nURI: http://plugin.org.uk/swh-plugins/comb_n\nClass: Delay\nAuthor: Steve Harris\nLatency: no\nPorts: \"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\t\"Max Delay (s)\" Input, Control, 0 to 10, default 1\n\t\"Delay Time (s)\" Input, Control, 0 to 10, default 0\n\t\"Decay Time (s)\" Input, Control, 0 to 10, default 0\n\n", "elv2:http://plugin.org.uk/swh-plugins/const" : "Name: Constant Signal Generator\nURI: http://plugin.org.uk/swh-plugins/const\nClass: Generator\nAuthor: Steve Harris\nLatency: no\nPorts: \"Signal amplitude\" Input, Control, -1 to 1.1, default 0\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/crossoverDist" : "Name: Crossover distortion\nURI: http://plugin.org.uk/swh-plugins/crossoverDist\nClass: Distortion\nAuthor: Steve Harris\nLatency: no\nPorts: \"Crossover amplitude\" Input, Control, 0 to 0.1, default 0\n\t\"Smoothing\" Input, Control, 0 to 1, default 1\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/dcRemove" : "Name: DC Offset Remover\nURI: http://plugin.org.uk/swh-plugins/dcRemove\nClass: Highpass\nAuthor: Steve Harris\nLatency: no\nPorts: \"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/decay" : "Name: Exponential signal decay\nURI: http://plugin.org.uk/swh-plugins/decay\nClass: Utility\nAuthor: Steve Harris\nLatency: no\nPorts: \"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\t\"Decay Time (s)\" Input, Control, 0 to 10, default 1\n\n", "elv2:http://plugin.org.uk/swh-plugins/decimator" : "Name: Decimator\nURI: http://plugin.org.uk/swh-plugins/decimator\nClass: Distortion\nAuthor: Steve Harris\nLatency: no\nPorts: \"Bit depth\" Input, Control, 1 to 24, default 24\n\t\"Sample rate (Hz)\" Input, Control, 0.001 to 1, default 1\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/declip" : "Name: Declipper\nURI: http://plugin.org.uk/swh-plugins/declip\nClass: Waveshaper\nAuthor: Steve Harris\nLatency: no\nPorts: \"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/delay_c" : "Name: Simple delay line, cubic spline interpolation\nURI: http://plugin.org.uk/swh-plugins/delay_c\nClass: Delay\nAuthor: Steve Harris\nLatency: no\nPorts: \"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\t\"Max Delay (s)\" Input, Control, 0 to 10, default 1\n\t\"Delay Time (s)\" Input, Control, 0 to 10, default 0\n\n", "elv2:http://plugin.org.uk/swh-plugins/delay_l" : "Name: Simple delay line, linear interpolation\nURI: http://plugin.org.uk/swh-plugins/delay_l\nClass: Delay\nAuthor: Steve Harris\nLatency: no\nPorts: \"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\t\"Max Delay (s)\" Input, Control, 0 to 10, default 1\n\t\"Delay Time (s)\" Input, Control, 0 to 10, default 0\n\n", "elv2:http://plugin.org.uk/swh-plugins/delay_n" : "Name: Simple delay line, noninterpolating\nURI: http://plugin.org.uk/swh-plugins/delay_n\nClass: Delay\nAuthor: Steve Harris\nLatency: no\nPorts: \"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\t\"Max Delay (s)\" Input, Control, 0 to 10, default 1\n\t\"Delay Time (s)\" Input, Control, 0 to 10, default 0\n\n", "elv2:http://plugin.org.uk/swh-plugins/delayorama" : "Name: Delayorama\nURI: http://plugin.org.uk/swh-plugins/delayorama\nClass: Delay\nAuthor: Steve Harris\nLatency: no\nPorts: \"Random seed\" Input, Control, 0 to 1000, default 0\n\t\"Input gain (dB)\" Input, Control, -96 to 24, default 0\n\t\"Feedback (%)\" Input, Control, 0 to 100, default 0\n\t\"Number of taps\" Input, Control, 2 to 128, default 2\n\t\"First delay (s)\" Input, Control, 0 to 5, default 0\n\t\"Delay range (s)\" Input, Control, 0.0001 to 6, default 6\n\t\"Delay change\" Input, Control, 0.2 to 5, default 1\n\t\"Delay random (%)\" Input, Control, 0 to 100, default 0\n\t\"Amplitude change\" Input, Control, 0.2 to 5, default 1\n\t\"Amplitude random (%)\" Input, Control, 0 to 100, default 0\n\t\"Dry/wet mix\" Input, Control, 0 to 1, default 1\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/diode" : "Name: Diode Processor\nURI: http://plugin.org.uk/swh-plugins/diode\nClass: Distortion\nAuthor: Steve Harris\nLatency: no\nPorts: \"Mode (0 for none, 1 for half wave, 2 for full wave)\" Input, Control, 0 to 3, default 0\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/divider" : "Name: Audio Divider (Suboctave Generator)\nURI: http://plugin.org.uk/swh-plugins/divider\nClass: Generator\nAuthor: Steve Harris\nLatency: no\nPorts: \"Denominator\" Input, Control, 1 to 8, default 1\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/djFlanger" : "Name: DJ flanger\nURI: http://plugin.org.uk/swh-plugins/djFlanger\nClass: Flanger\nAuthor: Steve Harris\nLatency: no\nPorts: \"LFO sync\" Input, Control\n\t\"LFO period (s)\" Input, Control, 0.1 to 32, default 1\n\t\"LFO depth (ms)\" Input, Control, 1 to 5, default 4\n\t\"Feedback (%)\" Input, Control, -100 to 100, default 0\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/dj_eq" : "Name: DJ EQ\nURI: http://plugin.org.uk/swh-plugins/dj_eq\nClass: Equaliser\nAuthor: Steve Harris\nLatency: yes, reported by port 7\nPorts: \"Lo gain (dB)\" Input, Control, -70 to 6, default 0\n\t\"Mid gain (dB)\" Input, Control, -70 to 6, default 0\n\t\"Hi gain (dB)\" Input, Control, -70 to 6, default 0\n\t\"Input L\" Input, Audio\n\t\"Input R\" Input, Audio\n\t\"Output L\" Output, Audio\n\t\"Output R\" Output, Audio\n\t\"latency\" Output, Control\n\n", "elv2:http://plugin.org.uk/swh-plugins/dj_eq_mono" : "Name: DJ EQ (mono)\nURI: http://plugin.org.uk/swh-plugins/dj_eq_mono\nClass: Equaliser\nAuthor: Steve Harris\nLatency: yes, reported by port 5\nPorts: \"Lo gain (dB)\" Input, Control, -70 to 6, default 0\n\t\"Mid gain (dB)\" Input, Control, -70 to 6, default 0\n\t\"Hi gain (dB)\" Input, Control, -70 to 6, default 0\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\t\"latency\" Output, Control\n\n", "elv2:http://plugin.org.uk/swh-plugins/dysonCompress" : "Name: Dyson compressor\nURI: http://plugin.org.uk/swh-plugins/dysonCompress\nClass: Compressor\nAuthor: Steve Harris\nLatency: no\nPorts: \"Peak limit (dB)\" Input, Control, -30 to 0, default 0\n\t\"Release time (s)\" Input, Control, 0 to 1, default 0.25\n\t\"Fast compression ratio\" Input, Control, 0 to 1, default 0.5\n\t\"Compression ratio\" Input, Control, 0 to 1, default 0.5\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/fadDelay" : "Name: Fractionally Addressed Delay Line\nURI: http://plugin.org.uk/swh-plugins/fadDelay\nClass: Delay\nAuthor: Steve Harris\nLatency: no\nPorts: \"Delay (seconds)\" Input, Control, 0.1 to 10, default 1\n\t\"Feedback (dB)\" Input, Control, -70 to 0, default 0\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/fastLookaheadLimiter" : "Name: Fast Lookahead limiter\nURI: http://plugin.org.uk/swh-plugins/fastLookaheadLimiter\nClass: Limiter\nAuthor: Steve Harris\nLatency: yes, reported by port 8\nPorts: \"Input gain (dB)\" Input, Control, -20 to 20, default 0\n\t\"Limit (dB)\" Input, Control, -20 to 0, default 0\n\t\"Release time (s)\" Input, Control, 0.01 to 2, default 0.5075\n\t\"Attenuation (dB)\" Output, Control, 0 to 70\n\t\"Input 1\" Input, Audio\n\t\"Input 2\" Input, Audio\n\t\"Output 1\" Output, Audio\n\t\"Output 2\" Output, Audio\n\t\"latency\" Output, Control\n\n", "elv2:http://plugin.org.uk/swh-plugins/flanger" : "Name: Flanger\nURI: http://plugin.org.uk/swh-plugins/flanger\nClass: Flanger\nAuthor: Steve Harris\nLatency: no\nPorts: \"Delay base (ms)\" Input, Control, 0.1 to 25, default 6.325\n\t\"Max slowdown (ms)\" Input, Control, 0 to 10, default 2.5\n\t\"LFO frequency (Hz)\" Input, Control, 0.05 to 100, default 25.0375\n\t\"Feedback\" Input, Control, -1 to 1, default 0\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/fmOsc" : "Name: FM Oscillator\nURI: http://plugin.org.uk/swh-plugins/fmOsc\nClass: Oscillator\nAuthor: Steve Harris\nLatency: no\nPorts: \"Waveform (1=sin, 2=tri, 3=squ, 4=saw)\" Input, Control, 1 to 4, default 1\n\t\"Frequency (Hz)\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/foldover" : "Name: Foldover distortion\nURI: http://plugin.org.uk/swh-plugins/foldover\nClass: Distortion\nAuthor: Steve Harris\nLatency: no\nPorts: \"Drive\" Input, Control, 0 to 1, default 0\n\t\"Skew\" Input, Control, 0 to 1, default 0\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/fourByFourPole" : "Name: 4 x 4 pole allpass\nURI: http://plugin.org.uk/swh-plugins/fourByFourPole\nClass: Allpass\nAuthor: Steve Harris\nLatency: no\nPorts: \"Frequency 1\" Input, Control, 1 to 20000, default 5000.75\n\t\"Feedback 1\" Input, Control, -1 to 1, default 0\n\t\"Frequency 2\" Input, Control, 1 to 20000, default 10000.5\n\t\"Feedback 2\" Input, Control, -1 to 1, default 0\n\t\"Frequency 3\" Input, Control, 1 to 20000, default 15000.25\n\t\"Feedback 3\" Input, Control, -1 to 1, default 0\n\t\"Frequency 4\" Input, Control, 1 to 20000, default 20000\n\t\"Feedback 4\" Input, Control, -1 to 1, default 0\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/foverdrive" : "Name: Fast overdrive\nURI: http://plugin.org.uk/swh-plugins/foverdrive\nClass: Distortion\nAuthor: Steve Harris\nLatency: no\nPorts: \"Drive level\" Input, Control, 1 to 3, default 1\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/freqTracker" : "Name: Frequency tracker\nURI: http://plugin.org.uk/swh-plugins/freqTracker\nClass: Analyser\nAuthor: Steve Harris\nLatency: no\nPorts: \"Tracking speed\" Input, Control, 0 to 1, default 0.5\n\t\"Input\" Input, Audio\n\t\"Frequency (Hz)\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/gate" : "Name: Gate\nURI: http://plugin.org.uk/swh-plugins/gate\nClass: Gate\nAuthor: Steve Harris\nLatency: no\nPorts: \"LF key filter (Hz)\" Input, Control, 25 to 4000, default 500\n\t\"HF key filter (Hz)\" Input, Control, 250 to 20000, default 2000\n\t\"Threshold (dB)\" Input, Control, -70 to 20, default -40\n\t\"Attack (ms)\" Input, Control, 0.01 to 1000, default 0.1\n\t\"Hold (ms)\" Input, Control, 2 to 2000, default 50\n\t\"Decay (ms)\" Input, Control, 2 to 4000, default 50\n\t\"Range (dB)\" Input, Control, -90 to 0, default -20\n\t\"Output select (-1 = key listen, 0 = gate, 1 = bypass)\" Input, Control, -1 to 1, default 0\n\t\"Key level (dB)\" Output, Control, -90 to 0\n\t\"Gate state\" Output, Control, 0 to 1\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/giantFlange" : "Name: Giant flange\nURI: http://plugin.org.uk/swh-plugins/giantFlange\nClass: Flanger\nAuthor: Steve Harris\nLatency: no\nPorts: \"Double delay\" Input, Control\n\t\"LFO frequency 1 (Hz)\" Input, Control, 0 to 30, default 1\n\t\"Delay 1 range (s)\" Input, Control, 0 to 10.5, default 2.625\n\t\"LFO frequency 2 (Hz)\" Input, Control, 0 to 30, default 1\n\t\"Delay 2 range (s)\" Input, Control, 0 to 10.5, default 0\n\t\"Feedback\" Input, Control, -100 to 100, default 0\n\t\"Dry/Wet level\" Input, Control, 0 to 1, default 0\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/gong" : "Name: Gong model\nURI: http://plugin.org.uk/swh-plugins/gong\nClass: Generator\nAuthor: Steve Harris\nLatency: no\nPorts: \"Inner damping\" Input, Control, 0 to 1, default 0.5\n\t\"Outer damping\" Input, Control, 0 to 1, default 0.5\n\t\"Mic position\" Input, Control, 0 to 1, default 0.25\n\t\"Inner size 1\" Input, Control, 0 to 1, default 0.5\n\t\"Inner stiffness 1 +\" Input, Control, 0 to 1, default 0.5\n\t\"Inner stiffness 1 -\" Input, Control, 0 to 1, default 0.5\n\t\"Inner size 2\" Input, Control, 0 to 1, default 0.5\n\t\"Inner stiffness 2 +\" Input, Control, 0 to 1, default 0.5\n\t\"Inner stiffness 2 -\" Input, Control, 0 to 1, default 0.5\n\t\"Inner size 3\" Input, Control, 0 to 1, default 0.5\n\t\"Inner stiffness 3 +\" Input, Control, 0 to 1, default 0.5\n\t\"Inner stiffness 3 -\" Input, Control, 0 to 1, default 0.5\n\t\"Inner size 4\" Input, Control, 0 to 1, default 0.5\n\t\"Inner stiffness 4 +\" Input, Control, 0 to 1, default 0.5\n\t\"Inner stiffness 4 -\" Input, Control, 0 to 1, default 0.5\n\t\"Outer size 1\" Input, Control, 0 to 1, default 0.5\n\t\"Outer stiffness 1 +\" Input, Control, 0 to 1, default 0.5\n\t\"Outer stiffness 1 -\" Input, Control, 0 to 1, default 0.5\n\t\"Outer size 2\" Input, Control, 0 to 1, default 0.5\n\t\"Outer stiffness 2 +\" Input, Control, 0 to 1, default 0.5\n\t\"Outer stiffness 2 -\" Input, Control, 0 to 1, default 0.5\n\t\"Outer size 3\" Input, Control, 0 to 1, default 0.5\n\t\"Outer stiffness 3 +\" Input, Control, 0 to 1, default 0.5\n\t\"Outer stiffness 3 -\" Input, Control, 0 to 1, default 0.5\n\t\"Outer size 4\" Input, Control, 0 to 1, default 0.5\n\t\"Outer stiffness 4 +\" Input, Control, 0 to 1, default 0.5\n\t\"Outer stiffness 4 -\" Input, Control, 0 to 1, default 0.5\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/gongBeater" : "Name: Gong beater\nURI: http://plugin.org.uk/swh-plugins/gongBeater\nClass: Generator\nAuthor: Steve Harris\nLatency: no\nPorts: \"Impulse gain (dB)\" Input, Control, -70 to 0, default -70\n\t\"Strike gain (dB)\" Input, Control, -70 to 0, default 0\n\t\"Strike duration (s)\" Input, Control, 0.001 to 0.2, default 0.1005\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/gverb" : "Name: GVerb\nURI: http://plugin.org.uk/swh-plugins/gverb\nClass: Reverb\nAuthor: Steve Harris\nLatency: no\nPorts: \"Roomsize (m)\" Input, Control, 1 to 300, default 75.75\n\t\"Reverb time (s)\" Input, Control, 0.1 to 30, default 7.575\n\t\"Damping\" Input, Control, 0 to 1, default 0.5\n\t\"Input bandwidth\" Input, Control, 0 to 1, default 0.75\n\t\"Dry signal level (dB)\" Input, Control, -70 to 0, default -70\n\t\"Early reflection level (dB)\" Input, Control, -70 to 0, default 0\n\t\"Tail level (dB)\" Input, Control, -70 to 0, default -17.5\n\t\"Input\" Input, Audio\n\t\"Left output\" Output, Audio\n\t\"Right output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/hardLimiter" : "Name: Hard Limiter\nURI: http://plugin.org.uk/swh-plugins/hardLimiter\nClass: Distortion\nAuthor: Steve Harris\nLatency: no\nPorts: \"dB limit\" Input, Control, -50 to 0, default 0\n\t\"Wet level\" Input, Control, 0 to 1, default 1\n\t\"Residue level\" Input, Control, 0 to 1, default 0\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/harmonicGen" : "Name: Harmonic generator\nURI: http://plugin.org.uk/swh-plugins/harmonicGen\nClass: Generator\nAuthor: Steve Harris\nLatency: no\nPorts: \"Fundamental magnitude\" Input, Control, -1 to 1, default 1\n\t\"2nd harmonic magnitude\" Input, Control, -1 to 1, default 0\n\t\"3rd harmonic magnitude\" Input, Control, -1 to 1, default 0\n\t\"4th harmonic magnitude\" Input, Control, -1 to 1, default 0\n\t\"5th harmonic magnitude\" Input, Control, -1 to 1, default 0\n\t\"6th harmonic magnitude\" Input, Control, -1 to 1, default 0\n\t\"7th harmonic magnitude\" Input, Control, -1 to 1, default 0\n\t\"8th harmonic magnitude\" Input, Control, -1 to 1, default 0\n\t\"9th harmonic magnitude\" Input, Control, -1 to 1, default 0\n\t\"10th harmonic magnitude\" Input, Control, -1 to 1, default 0\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/hermesFilter" : "Name: Hermes Filter\nURI: http://plugin.org.uk/swh-plugins/hermesFilter\nClass: Filter\nAuthor: Steve Harris\nLatency: no\nPorts: \"LFO1 freq (Hz)\" Input, Control, 0 to 1000, default 250\n\t\"LFO1 wave (0 = sin, 1 = tri, 2 = saw, 3 = squ, 4 = s&h)\" Input, Control, 0 to 4, default 0\n\t\"LFO2 freq (Hz)\" Input, Control, 0 to 1000, default 250\n\t\"LFO2 wave (0 = sin, 1 = tri, 2 = saw, 3 = squ, 4 = s&h)\" Input, Control, 0 to 4, default 0\n\t\"Osc1 freq (Hz)\" Input, Control, 0 to 4000, default 440\n\t\"Osc1 wave (0 = sin, 1 = tri, 2 = saw, 3 = squ, 4 = noise)\" Input, Control, 0 to 4, default 0\n\t\"Osc2 freq (Hz)\" Input, Control, 0 to 4000, default 440\n\t\"Osc2 wave (0 = sin, 1 = tri, 2 = saw, 3 = squ, 4 = noise)\" Input, Control, 0 to 4, default 0\n\t\"Ringmod 1 depth (0=none, 1=AM, 2=RM)\" Input, Control, 0 to 2, default 0\n\t\"Ringmod 2 depth (0=none, 1=AM, 2=RM)\" Input, Control, 0 to 2, default 0\n\t\"Ringmod 3 depth (0=none, 1=AM, 2=RM)\" Input, Control, 0 to 2, default 0\n\t\"Osc1 gain (dB)\" Input, Control, -70 to 20, default -70\n\t\"RM1 gain (dB)\" Input, Control, -70 to 20, default -70\n\t\"Osc2 gain (dB)\" Input, Control, -70 to 20, default -70\n\t\"RM2 gain (dB)\" Input, Control, -70 to 20, default -70\n\t\"Input gain (dB)\" Input, Control, -70 to 20, default 0\n\t\"RM3 gain (dB)\" Input, Control, -70 to 20, default -70\n\t\"Xover lower freq\" Input, Control, 50 to 6000, default 1537.5\n\t\"Xover upper freq\" Input, Control, 1000 to 10000, default 7750\n\t\"Dist1 drive\" Input, Control, 0 to 3, default 0\n\t\"Dist2 drive\" Input, Control, 0 to 3, default 0\n\t\"Dist3 drive\" Input, Control, 0 to 3, default 0\n\t\"Filt1 type (0=none, 1=LP, 2=HP, 3=BP, 4=BR, 5=AP)\" Input, Control, 0 to 5, default 0\n\t\"Filt1 freq\" Input, Control, 0 to 8000, default 440\n\t\"Filt1 q\" Input, Control, 0 to 1, default 0\n\t\"Filt1 resonance\" Input, Control, 0 to 1, default 0\n\t\"Filt1 LFO1 level\" Input, Control, -500 to 500, default 0\n\t\"Filt1 LFO2 level\" Input, Control, -500 to 500, default 0\n\t\"Filt2 type (0=none, 1=LP, 2=HP, 3=BP, 4=BR, 5=AP)\" Input, Control, 0 to 5, default 0\n\t\"Filt2 freq\" Input, Control, 0 to 8000, default 440\n\t\"Filt2 q\" Input, Control, 0 to 1, default 0\n\t\"Filt2 resonance\" Input, Control, 0 to 1, default 0\n\t\"Filt2 LFO1 level\" Input, Control, -500 to 500, default 0\n\t\"Filt2 LFO2 level\" Input, Control, -500 to 500, default 0\n\t\"Filt3 type (0=none, 1=LP, 2=HP, 3=BP, 4=BR, 5=AP)\" Input, Control, 0 to 5, default 0\n\t\"Filt3 freq\" Input, Control, 0 to 8000, default 440\n\t\"Filt3 q\" Input, Control, 0 to 1, default 0\n\t\"Filt3 resonance\" Input, Control, 0 to 1, default 0\n\t\"Filt3 LFO1 level\" Input, Control, -500 to 500, default 0\n\t\"Filt3 LFO2 level\" Input, Control, -500 to 500, default 0\n\t\"Delay1 length (s)\" Input, Control, 0 to 2, default 0\n\t\"Delay1 feedback\" Input, Control, 0 to 1, default 0\n\t\"Delay1 wetness\" Input, Control, 0 to 1, default 0\n\t\"Delay2 length (s)\" Input, Control, 0 to 2, default 0\n\t\"Delay2 feedback\" Input, Control, 0 to 1, default 0\n\t\"Delay2 wetness\" Input, Control, 0 to 1, default 0\n\t\"Delay3 length (s)\" Input, Control, 0 to 2, default 0\n\t\"Delay3 feedback\" Input, Control, 0 to 1, default 0\n\t\"Delay3 wetness\" Input, Control, 0 to 1, default 0\n\t\"Band 1 gain (dB)\" Input, Control, -70 to 20, default 0\n\t\"Band 2 gain (dB)\" Input, Control, -70 to 20, default 0\n\t\"Band 3 gain (dB)\" Input, Control, -70 to 20, default 0\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/highpass_iir" : "Name: Glame Highpass Filter\nURI: http://plugin.org.uk/swh-plugins/highpass_iir\nClass: Highpass\nAuthor: Steve Harris\nLatency: no\nPorts: \"Cutoff Frequency\" Input, Control, 0.0001 to 0.45, default 0.112575\n\t\"Stages(2 poles per stage)\" Input, Control, 1 to 10, default 1\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/hilbert" : "Name: Hilbert transformer\nURI: http://plugin.org.uk/swh-plugins/hilbert\nClass: Analyser\nAuthor: Steve Harris\nLatency: yes, reported by port 3\nPorts: \"Input\" Input, Audio\n\t\"0deg output\" Output, Audio\n\t\"90deg output\" Output, Audio\n\t\"latency\" Output, Control\n\n", "elv2:http://plugin.org.uk/swh-plugins/impulse_fc" : "Name: Non-bandlimited single-sample impulses\nURI: http://plugin.org.uk/swh-plugins/impulse_fc\nClass: Generator\nAuthor: Steve Harris\nLatency: no\nPorts: \"Frequency (Hz)\" Input, Control, 0 to 5000, default 1\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/inv" : "Name: Inverter\nURI: http://plugin.org.uk/swh-plugins/inv\nClass: Utility\nAuthor: Steve Harris\nLatency: no\nPorts: \"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/karaoke" : "Name: Karaoke\nURI: http://plugin.org.uk/swh-plugins/karaoke\nClass: Filter\nAuthor: Steve Harris\nLatency: no\nPorts: \"Vocal volume (dB)\" Input, Control, -70 to 0, default 0\n\t\"Left in\" Input, Audio\n\t\"Right in\" Input, Audio\n\t\"Left out\" Output, Audio\n\t\"Right out\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/lcrDelay" : "Name: L/C/R Delay\nURI: http://plugin.org.uk/swh-plugins/lcrDelay\nClass: Delay\nAuthor: Steve Harris\nLatency: no\nPorts: \"L delay (ms)\" Input, Control, 0 to 2700, default 675\n\t\"L level\" Input, Control, 0 to 50, default 25\n\t\"C delay (ms)\" Input, Control, 0 to 2700, default 675\n\t\"C level\" Input, Control, 0 to 50, default 25\n\t\"R delay (ms)\" Input, Control, 0 to 2700, default 675\n\t\"R level\" Input, Control, 0 to 50, default 25\n\t\"Feedback\" Input, Control, -100 to 100, default 0\n\t\"High damp (%)\" Input, Control, 0 to 100, default 50\n\t\"Low damp (%)\" Input, Control, 0 to 100, default 50\n\t\"Spread\" Input, Control, 0 to 50, default 25\n\t\"Dry/Wet level\" Input, Control, 0 to 1, default 0\n\t\"L input\" Input, Audio\n\t\"R input\" Input, Audio\n\t\"L output\" Output, Audio\n\t\"R output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/lfoPhaser" : "Name: LFO Phaser\nURI: http://plugin.org.uk/swh-plugins/lfoPhaser\nClass: Phaser\nAuthor: Steve Harris\nLatency: no\nPorts: \"LFO rate (Hz)\" Input, Control, 0 to 100, default 25\n\t\"LFO depth\" Input, Control, 0 to 1, default 0.25\n\t\"Feedback\" Input, Control, -1 to 1, default 0\n\t\"Spread (octaves)\" Input, Control, 0 to 2, default 1\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/lookaheadLimiter" : "Name: Lookahead limiter\nURI: http://plugin.org.uk/swh-plugins/lookaheadLimiter\nClass: Limiter\nAuthor: Steve Harris\nLatency: yes, reported by port 7\nPorts: \"Limit (dB)\" Input, Control, -20 to 0, default 0\n\t\"Lookahead delay\" Input, Control, 0.001 to 2, default 1.0005\n\t\"Attenuation (dB)\" Output, Control, 0 to 12\n\t\"Input 1\" Input, Audio\n\t\"Input 2\" Input, Audio\n\t\"Output 1\" Output, Audio\n\t\"Output 2\" Output, Audio\n\t\"latency\" Output, Control\n\n", "elv2:http://plugin.org.uk/swh-plugins/lookaheadLimiterConst" : "Name: Lookahead limiter (fixed latency)\nURI: http://plugin.org.uk/swh-plugins/lookaheadLimiterConst\nClass: Limiter\nAuthor: Steve Harris\nLatency: yes, reported by port 7\nPorts: \"Limit (dB)\" Input, Control, -20 to 0, default 0\n\t\"Lookahead time (s)\" Input, Control, 0.001 to 0.15, default 0.0755\n\t\"Attenuation (dB)\" Output, Control, 0 to 12\n\t\"Input 1\" Input, Audio\n\t\"Input 2\" Input, Audio\n\t\"Output 1\" Output, Audio\n\t\"Output 2\" Output, Audio\n\t\"latency\" Output, Control\n\n", "elv2:http://plugin.org.uk/swh-plugins/lowpass_iir" : "Name: Glame Lowpass Filter\nURI: http://plugin.org.uk/swh-plugins/lowpass_iir\nClass: Lowpass\nAuthor: Steve Harris\nLatency: no\nPorts: \"Cutoff Frequency\" Input, Control, 0.0001 to 0.45, default 0.337525\n\t\"Stages(2 poles per stage)\" Input, Control, 1 to 10, default 1\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/lsFilter" : "Name: LS Filter\nURI: http://plugin.org.uk/swh-plugins/lsFilter\nClass: Filter\nAuthor: Steve Harris\nLatency: no\nPorts: \"Filter type (0=LP, 1=BP, 2=HP)\" Input, Control, 0 to 2, default 0\n\t\"Cutoff frequency (Hz)\" Input, Control, 0.002 to 0.5, default 0.251\n\t\"Resonance\" Input, Control, 0 to 1, default 0\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/matrixMSSt" : "Name: Matrix: MS to Stereo\nURI: http://plugin.org.uk/swh-plugins/matrixMSSt\nClass: Converter\nAuthor: Steve Harris\nLatency: no\nPorts: \"Width\" Input, Control, 0 to 2, default 1\n\t\"Mid\" Input, Audio\n\t\"Side\" Input, Audio\n\t\"Left\" Output, Audio\n\t\"Right\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/matrixSpatialiser" : "Name: Matrix Spatialiser\nURI: http://plugin.org.uk/swh-plugins/matrixSpatialiser\nClass: Utility\nAuthor: Steve Harris\nLatency: no\nPorts: \"Input L\" Input, Audio\n\t\"Input R\" Input, Audio\n\t\"Width\" Input, Control, -512 to 512, default 0\n\t\"Output L\" Output, Audio\n\t\"Output R\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/matrixStMS" : "Name: Matrix: Stereo to MS\nURI: http://plugin.org.uk/swh-plugins/matrixStMS\nClass: Converter\nAuthor: Steve Harris\nLatency: no\nPorts: \"Left\" Input, Audio\n\t\"Right\" Input, Audio\n\t\"Mid\" Output, Audio\n\t\"Side\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/mbeq" : "Name: Multiband EQ\nURI: http://plugin.org.uk/swh-plugins/mbeq\nClass: Multiband\nAuthor: Steve Harris\nLatency: yes, reported by port 17\nPorts: \"50Hz gain (low shelving)\" Input, Control, -70 to 30, default 0\n\t\"100Hz gain\" Input, Control, -70 to 30, default 0\n\t\"156Hz gain\" Input, Control, -70 to 30, default 0\n\t\"220Hz gain\" Input, Control, -70 to 30, default 0\n\t\"311Hz gain\" Input, Control, -70 to 30, default 0\n\t\"440Hz gain\" Input, Control, -70 to 30, default 0\n\t\"622Hz gain\" Input, Control, -70 to 30, default 0\n\t\"880Hz gain\" Input, Control, -70 to 30, default 0\n\t\"1250Hz gain\" Input, Control, -70 to 30, default 0\n\t\"1750Hz gain\" Input, Control, -70 to 30, default 0\n\t\"2500Hz gain\" Input, Control, -70 to 30, default 0\n\t\"3500Hz gain\" Input, Control, -70 to 30, default 0\n\t\"5000Hz gain\" Input, Control, -70 to 30, default 0\n\t\"10000Hz gain\" Input, Control, -70 to 30, default 0\n\t\"20000Hz gain\" Input, Control, -70 to 30, default 0\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\t\"latency\" Output, Control\n\n", "elv2:http://plugin.org.uk/swh-plugins/modDelay" : "Name: Modulatable delay\nURI: http://plugin.org.uk/swh-plugins/modDelay\nClass: Delay\nAuthor: Steve Harris\nLatency: no\nPorts: \"Base delay (s)\" Input, Control, 0 to 1, default 1\n\t\"Delay (s)\" Input, Audio\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/multivoiceChorus" : "Name: Multivoice Chorus\nURI: http://plugin.org.uk/swh-plugins/multivoiceChorus\nClass: Chorus\nAuthor: Steve Harris\nLatency: no\nPorts: \"Number of voices\" Input, Control, 1 to 8, default 1\n\t\"Delay base (ms)\" Input, Control, 10 to 40, default 10\n\t\"Voice separation (ms)\" Input, Control, 0 to 2, default 0.5\n\t\"Detune (%)\" Input, Control, 0 to 5, default 1\n\t\"LFO frequency (Hz)\" Input, Control, 2 to 30, default 9\n\t\"Output attenuation (dB)\" Input, Control, -20 to 0, default 0\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/pitchScaleHQ" : "Name: Higher Quality Pitch Scaler\nURI: http://plugin.org.uk/swh-plugins/pitchScaleHQ\nClass: Pitch Shifter\nAuthor: Steve Harris\nLatency: yes, reported by port 3\nPorts: \"Pitch co-efficient\" Input, Control, 0.5 to 2, default 1\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\t\"latency\" Output, Control\n\n", "elv2:http://plugin.org.uk/swh-plugins/plate" : "Name: Plate reverb\nURI: http://plugin.org.uk/swh-plugins/plate\nClass: Reverb\nAuthor: Steve Harris\nLatency: no\nPorts: \"Reverb time\" Input, Control, 0.01 to 8.5, default 4.255\n\t\"Damping\" Input, Control, 0 to 1, default 0.25\n\t\"Dry/wet mix\" Input, Control, 0 to 1, default 0.25\n\t\"Input\" Input, Audio\n\t\"Left output\" Output, Audio\n\t\"Right output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/pointerCastDistortion" : "Name: Pointer cast distortion\nURI: http://plugin.org.uk/swh-plugins/pointerCastDistortion\nClass: Distortion\nAuthor: Steve Harris\nLatency: no\nPorts: \"Effect cutoff freq (Hz)\" Input, Control, 0.0001 to 0.3, default 0.075075\n\t\"Dry/wet mix\" Input, Control, 0 to 1, default 0\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/rateShifter" : "Name: Rate shifter\nURI: http://plugin.org.uk/swh-plugins/rateShifter\nClass: Pitch Shifter\nAuthor: Steve Harris\nLatency: no\nPorts: \"Rate\" Input, Control, -4 to 4, default 1\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/retroFlange" : "Name: Retro Flanger\nURI: http://plugin.org.uk/swh-plugins/retroFlange\nClass: Flanger\nAuthor: Steve Harris\nLatency: no\nPorts: \"Average stall (ms)\" Input, Control, 0 to 10, default 2.5\n\t\"Flange frequency (Hz)\" Input, Control, 0.5 to 8, default 1\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/revdelay" : "Name: Reverse Delay (5s max)\nURI: http://plugin.org.uk/swh-plugins/revdelay\nClass: Delay\nAuthor: Steve Harris\nLatency: no\nPorts: \"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\t\"Delay Time (s)\" Input, Control, 0 to 5, default 0\n\t\"Dry Level (dB)\" Input, Control, -70 to 0, default 0\n\t\"Wet Level (dB)\" Input, Control, -70 to 0, default 0\n\t\"Feedback\" Input, Control, 0 to 1, default 0\n\t\"Crossfade samples\" Input, Control, 0 to 5000, default 1250\n\n", "elv2:http://plugin.org.uk/swh-plugins/ringmod_1i1o1l" : "Name: Ringmod with LFO\nURI: http://plugin.org.uk/swh-plugins/ringmod_1i1o1l\nClass: Modulator\nAuthor: Steve Harris\nLatency: no\nPorts: \"Modulation depth (0=none, 1=AM, 2=RM)\" Input, Control, 0 to 2, default 0\n\t\"Frequency (Hz)\" Input, Control, 1 to 1000, default 440\n\t\"Sine level\" Input, Control, -1 to 1, default 1\n\t\"Triangle level\" Input, Control, -1 to 1, default 0\n\t\"Sawtooth level\" Input, Control, -1 to 1, default 0\n\t\"Square level\" Input, Control, -1 to 1, default 0\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/ringmod_2i1o" : "Name: Ringmod with two inputs\nURI: http://plugin.org.uk/swh-plugins/ringmod_2i1o\nClass: Modulator\nAuthor: Steve Harris\nLatency: no\nPorts: \"Modulation depth (0=none, 1=AM, 2=RM)\" Input, Control, 0 to 2, default 0\n\t\"Input\" Input, Audio\n\t\"Modulator\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/satanMaximiser" : "Name: Barry's Satan Maximiser\nURI: http://plugin.org.uk/swh-plugins/satanMaximiser\nClass: Distortion\nAuthor: Steve Harris\nLatency: no\nPorts: \"Decay time (samples)\" Input, Control, 2 to 30, default 30\n\t\"Knee point (dB)\" Input, Control, -90 to 0, default 0\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/sc1" : "Name: SC1\nURI: http://plugin.org.uk/swh-plugins/sc1\nClass: Compressor\nAuthor: Steve Harris\nLatency: no\nPorts: \"Attack time (ms)\" Input, Control, 2 to 400, default 101.5\n\t\"Release time (ms)\" Input, Control, 2 to 800, default 401\n\t\"Threshold level (dB)\" Input, Control, -30 to 0, default 0\n\t\"Ratio (1:n)\" Input, Control, 1 to 10, default 1\n\t\"Knee radius (dB)\" Input, Control, 1 to 10, default 3.25\n\t\"Makeup gain (dB)\" Input, Control, 0 to 24, default 0\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/sc2" : "Name: SC2\nURI: http://plugin.org.uk/swh-plugins/sc2\nClass: Compressor\nAuthor: Steve Harris\nLatency: no\nPorts: \"Attack time (ms)\" Input, Control, 2 to 400, default 101.5\n\t\"Release time (ms)\" Input, Control, 2 to 800, default 401\n\t\"Threshold level (dB)\" Input, Control, -30 to 0, default 0\n\t\"Ratio (1:n)\" Input, Control, 1 to 10, default 1\n\t\"Knee radius (dB)\" Input, Control, 1 to 10, default 3.25\n\t\"Makeup gain (dB)\" Input, Control, 0 to 24, default 0\n\t\"Sidechain\" Input, Audio\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/sc3" : "Name: SC3\nURI: http://plugin.org.uk/swh-plugins/sc3\nClass: Compressor\nAuthor: Steve Harris\nLatency: no\nPorts: \"Attack time (ms)\" Input, Control, 2 to 400, default 101.5\n\t\"Release time (ms)\" Input, Control, 2 to 800, default 401\n\t\"Threshold level (dB)\" Input, Control, -30 to 0, default 0\n\t\"Ratio (1:n)\" Input, Control, 1 to 10, default 1\n\t\"Knee radius (dB)\" Input, Control, 1 to 10, default 3.25\n\t\"Makeup gain (dB)\" Input, Control, 0 to 24, default 0\n\t\"Chain balance\" Input, Control, 0 to 1, default 0\n\t\"Sidechain\" Input, Audio\n\t\"Left input\" Input, Audio\n\t\"Right input\" Input, Audio\n\t\"Left output\" Output, Audio\n\t\"Right output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/sc4" : "Name: SC4\nURI: http://plugin.org.uk/swh-plugins/sc4\nClass: Compressor\nAuthor: Steve Harris\nLatency: no\nPorts: \"RMS/peak\" Input, Control, 0 to 1, default 0\n\t\"Attack time (ms)\" Input, Control, 1.5 to 400, default 101.125\n\t\"Release time (ms)\" Input, Control, 2 to 800, default 401\n\t\"Threshold level (dB)\" Input, Control, -30 to 0, default 0\n\t\"Ratio (1:n)\" Input, Control, 1 to 20, default 1\n\t\"Knee radius (dB)\" Input, Control, 1 to 10, default 3.25\n\t\"Makeup gain (dB)\" Input, Control, 0 to 24, default 0\n\t\"Amplitude (dB)\" Output, Control, -40 to 12\n\t\"Gain reduction (dB)\" Output, Control, -24 to 0\n\t\"Left input\" Input, Audio\n\t\"Right input\" Input, Audio\n\t\"Left output\" Output, Audio\n\t\"Right output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/se4" : "Name: SE4\nURI: http://plugin.org.uk/swh-plugins/se4\nClass: Compressor\nAuthor: Steve Harris\nLatency: no\nPorts: \"RMS/peak\" Input, Control, 0 to 1, default 0\n\t\"Attack time (ms)\" Input, Control, 1.5 to 400, default 101.125\n\t\"Release time (ms)\" Input, Control, 2 to 800, default 401\n\t\"Threshold level (dB)\" Input, Control, -30 to 0, default 0\n\t\"Ratio (1:n)\" Input, Control, 1 to 20, default 1\n\t\"Knee radius (dB)\" Input, Control, 1 to 10, default 3.25\n\t\"Attenuation (dB)\" Input, Control, -24 to 0, default 0\n\t\"Amplitude (dB)\" Output, Control, -40 to 12\n\t\"Gain expansion (dB)\" Output, Control, 0 to 24\n\t\"Left input\" Input, Audio\n\t\"Right input\" Input, Audio\n\t\"Left output\" Output, Audio\n\t\"Right output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/shaper" : "Name: Wave shaper\nURI: http://plugin.org.uk/swh-plugins/shaper\nClass: Waveshaper\nAuthor: Steve Harris\nLatency: no\nPorts: \"Waveshape\" Input, Control, -10 to 10, default 0\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/sifter" : "Name: Signal sifter\nURI: http://plugin.org.uk/swh-plugins/sifter\nClass: Distortion\nAuthor: Steve Harris\nLatency: no\nPorts: \"Sift size\" Input, Control, 1 to 1000, default 1\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/sinCos" : "Name: Sine + cosine oscillator\nURI: http://plugin.org.uk/swh-plugins/sinCos\nClass: Oscillator\nAuthor: Steve Harris\nLatency: no\nPorts: \"Base frequency (Hz)\" Input, Control, 0.000001 to 0.5, default 440\n\t\"Pitch offset\" Input, Control, 0 to 8, default 0\n\t\"Sine output\" Output, Audio\n\t\"Cosine output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/singlePara" : "Name: Single band parametric\nURI: http://plugin.org.uk/swh-plugins/singlePara\nClass: Parametric\nAuthor: Steve Harris\nLatency: no\nPorts: \"Gain (dB)\" Input, Control, -70 to 30, default 0\n\t\"Frequency (Hz)\" Input, Control, 0 to 0.4, default 440\n\t\"Bandwidth (octaves)\" Input, Control, 0 to 4, default 1\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/sinusWavewrapper" : "Name: Sinus wavewrapper\nURI: http://plugin.org.uk/swh-plugins/sinusWavewrapper\nClass: Waveshaper\nAuthor: Steve Harris\nLatency: no\nPorts: \"Wrap degree\" Input, Control, 0 to 10, default 0\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/smoothDecimate" : "Name: Smooth Decimator\nURI: http://plugin.org.uk/swh-plugins/smoothDecimate\nClass: Distortion\nAuthor: Steve Harris\nLatency: no\nPorts: \"Resample rate\" Input, Control, 0 to 1, default 1\n\t\"Smoothing\" Input, Control, 0 to 1, default 1\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/split" : "Name: Mono to Stereo splitter\nURI: http://plugin.org.uk/swh-plugins/split\nClass: Converter\nAuthor: Steve Harris\nLatency: no\nPorts: \"Input\" Input, Audio\n\t\"Output 1\" Output, Audio\n\t\"Output 2\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/surroundEncoder" : "Name: Surround matrix encoder\nURI: http://plugin.org.uk/swh-plugins/surroundEncoder\nClass: Converter\nAuthor: Steve Harris\nLatency: no\nPorts: \"L\" Input, Audio\n\t\"R\" Input, Audio\n\t\"C\" Input, Audio\n\t\"S\" Input, Audio\n\t\"Lt\" Output, Audio\n\t\"Rt\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/svf" : "Name: State Variable Filter\nURI: http://plugin.org.uk/swh-plugins/svf\nClass: Filter\nAuthor: Steve Harris\nLatency: no\nPorts: \"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\t\"Filter type (0=none, 1=LP, 2=HP, 3=BP, 4=BR, 5=AP)\" Input, Control, 0 to 5, default 0\n\t\"Filter freq\" Input, Control, 0 to 6000, default 440\n\t\"Filter Q\" Input, Control, 0 to 1, default 0.25\n\t\"Filter resonance\" Input, Control, 0 to 1, default 0\n\n", "elv2:http://plugin.org.uk/swh-plugins/tapeDelay" : "Name: Tape Delay Simulation\nURI: http://plugin.org.uk/swh-plugins/tapeDelay\nClass: Delay\nAuthor: Steve Harris\nLatency: no\nPorts: \"Tape speed (inches/sec, 1=normal)\" Input, Control, 0 to 10, default 1\n\t\"Dry level (dB)\" Input, Control, -90 to 0, default -90\n\t\"Tap 1 distance (inches)\" Input, Control, 0 to 4, default 0\n\t\"Tap 1 level (dB)\" Input, Control, -90 to 0, default 0\n\t\"Tap 2 distance (inches)\" Input, Control, 0 to 4, default 1\n\t\"Tap 2 level (dB)\" Input, Control, -90 to 0, default -90\n\t\"Tap 3 distance (inches)\" Input, Control, 0 to 4, default 2\n\t\"Tap 3 level (dB)\" Input, Control, -90 to 0, default -90\n\t\"Tap 4 distance (inches)\" Input, Control, 0 to 4, default 3\n\t\"Tap 4 level (dB)\" Input, Control, -90 to 0, default -90\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/transient" : "Name: Transient mangler\nURI: http://plugin.org.uk/swh-plugins/transient\nClass: Dynamics\nAuthor: Steve Harris\nLatency: no\nPorts: \"Attack speed\" Input, Control, -1 to 1, default 0\n\t\"Sustain time\" Input, Control, -1 to 1, default 0\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/triplePara" : "Name: Triple band parametric with shelves\nURI: http://plugin.org.uk/swh-plugins/triplePara\nClass: Parametric\nAuthor: Steve Harris\nLatency: no\nPorts: \"Low-shelving gain (dB)\" Input, Control, -70 to 30, default 0\n\t\"Low-shelving frequency (Hz)\" Input, Control, 0.0001 to 0.49, default 0.0001\n\t\"Low-shelving slope\" Input, Control, 0 to 1, default 0.5\n\t\"Band 1 gain (dB)\" Input, Control, -70 to 30, default 0\n\t\"Band 1 frequency (Hz)\" Input, Control, 0.0001 to 0.49, default 0.122575\n\t\"Band 1 bandwidth (octaves)\" Input, Control, 0 to 4, default 1\n\t\"Band 2 gain (dB)\" Input, Control, -70 to 30, default 0\n\t\"Band 2 frequency (Hz)\" Input, Control, 0.0001 to 0.49, default 0.24505\n\t\"Band 2 bandwidth (octaves)\" Input, Control, 0 to 4, default 1\n\t\"Band 3 gain (dB)\" Input, Control, -70 to 30, default 0\n\t\"Band 3 frequency (Hz)\" Input, Control, 0.0001 to 0.49, default 0.367525\n\t\"Band 3 bandwidth (octaves)\" Input, Control, 0 to 4, default 1\n\t\"High-shelving gain (dB)\" Input, Control, -70 to 30, default 0\n\t\"High-shelving frequency (Hz)\" Input, Control, 0.0001 to 0.49, default 0.49\n\t\"High-shelving slope\" Input, Control, 0 to 1, default 0.5\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/ulaw" : "Name: μ-Law Compressor\nURI: http://plugin.org.uk/swh-plugins/ulaw\nClass: Dynamics\nAuthor: Steve Harris\nLatency: no\nPorts: \"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/valve" : "Name: Valve saturation\nURI: http://plugin.org.uk/swh-plugins/valve\nClass: Distortion\nAuthor: Steve Harris\nLatency: no\nPorts: \"Distortion level\" Input, Control, 0 to 1, default 0\n\t\"Distortion character\" Input, Control, 0 to 1, default 0\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/valveRect" : "Name: Valve rectifier\nURI: http://plugin.org.uk/swh-plugins/valveRect\nClass: Distortion\nAuthor: Steve Harris\nLatency: no\nPorts: \"Sag level\" Input, Control, 0 to 1, default 0\n\t\"Distortion\" Input, Control, 0 to 1, default 0\n\t\"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/vynil" : "Name: VyNil (Vinyl Effect)\nURI: http://plugin.org.uk/swh-plugins/vynil\nClass: Distortion\nAuthor: Steve Harris\nLatency: no\nPorts: \"Year\" Input, Control, 1900 to 1990, default 1990\n\t\"RPM\" Input, Control, 33 to 78, default 33\n\t\"Surface warping\" Input, Control, 0 to 1, default 0\n\t\"Crackle\" Input, Control, 0 to 1, default 0\n\t\"Wear\" Input, Control, 0 to 1, default 0\n\t\"Input L\" Input, Audio\n\t\"Input R\" Input, Audio\n\t\"Output L\" Output, Audio\n\t\"Output R\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/waveTerrain" : "Name: Wave Terrain Oscillator\nURI: http://plugin.org.uk/swh-plugins/waveTerrain\nClass: Oscillator\nAuthor: Steve Harris\nLatency: no\nPorts: \"x\" Input, Audio\n\t\"y\" Input, Audio\n\t\"z\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/xfade" : "Name: Crossfade\nURI: http://plugin.org.uk/swh-plugins/xfade\nClass: Mixer\nAuthor: Steve Harris\nLatency: no\nPorts: \"Crossfade\" Input, Control, -1 to 1, default 0\n\t\"Input A left\" Input, Audio\n\t\"Input A right\" Input, Audio\n\t\"Input B left\" Input, Audio\n\t\"Input B right\" Input, Audio\n\t\"Output left\" Output, Audio\n\t\"Output right\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/xfade4" : "Name: Crossfade (4 outs)\nURI: http://plugin.org.uk/swh-plugins/xfade4\nClass: Mixer\nAuthor: Steve Harris\nLatency: no\nPorts: \"Crossfade\" Input, Control, -1 to 1, default 0\n\t\"Input A left\" Input, Audio\n\t\"Input A right\" Input, Audio\n\t\"Input B left\" Input, Audio\n\t\"Input B right\" Input, Audio\n\t\"Output A left\" Output, Audio\n\t\"Output A right\" Output, Audio\n\t\"Output B left\" Output, Audio\n\t\"Output B right\" Output, Audio\n\n", "elv2:http://plugin.org.uk/swh-plugins/zm1" : "Name: z-1\nURI: http://plugin.org.uk/swh-plugins/zm1\nClass: Utility\nAuthor: Steve Harris\nLatency: no\nPorts: \"Input\" Input, Audio\n\t\"Output\" Output, Audio\n\n", "elv2:urn:50m30n3:plugins:SO-404" : "Name: SO-404 Bass Synthesizer\nURI: urn:50m30n3:plugins:SO-404\nClass: Instrument\nLatency: no\nPorts: \"Output\" Output, Audio\n\t\"MIDI Input\" Input, Midi\n\t\"Control Mode\" Input, Control, 2-way Selector\n\t\"Volume\" Input, Control, 0 to 127, default 100\n\t\"Filter Cutoff\" Input, Control, 0 to 127, default 50\n\t\"Filter Resonance\" Input, Control, 0 to 127, default 100\n\t\"Filter Envelope\" Input, Control, 0 to 127, default 80\n\t\"Portamento Time\" Input, Control, 0 to 127, default 64\n\t\"Release Time\" Input, Control, 0 to 127, default 100\n\t\"Midi Channel\" Input, Control, 0 to 16, default 0, 1-way Selector\n\n", "elv2:urn:50m30n3:plugins:SO-666" : "Name: SO-666 Feedback Synthesizer\nURI: urn:50m30n3:plugins:SO-666\nClass: Instrument\nLatency: no\nPorts: \"Output\" Output, Audio\n\t\"MIDI Input\" Input, Midi\n\t\"Control Mode\" Input, Control, 2-way Selector\n\t\"Feedback\" Input, Control, 0 to 1, default 0.25\n\t\"Filter Resonance\" Input, Control, 0 to 1, default 0.5\n\t\"Filter Cutoff\" Input, Control, 0.25 to 0.885, default 0.57\n\t\"Volume\" Input, Control, 0 to 127, default 100\n\t\"Midi Channel\" Input, Control, 0 to 16, default 0, 1-way Selector\n\n", "elv2:urn:50m30n3:plugins:SO-kl5" : "Name: SO-kl5 Piano Synthesizer\nURI: urn:50m30n3:plugins:SO-kl5\nClass: Instrument\nLatency: no\nPorts: \"Output\" Output, Audio\n\t\"MIDI Input\" Input, Midi\n\t\"Control Mode\" Input, Control, 2-way Selector\n\t\"Sustain\" Input, Control, 0 to 1, default 0\n\t\"Filter Resonance\" Input, Control, 0 to 0.907, default 0.625\n\t\"Filter Cutoff\" Input, Control, 0.0125 to 0.3333, default 0.1725\n\t\"Attack\" Input, Control, 0.00625 to 0.165, default 0.01125\n\t\"Volume\" Input, Control, 0 to 127, default 100\n\t\"Midi Channel\" Input, Control, 0 to 16, default 0, 1-way Selector\n\n" }, "partial_label_to_full" : { "chcopy" : "chcopy", "chmix" : "chmix", "chmove" : "chmove", "chmute" : "chmute", "chorder" : "chorder", "eS" : "eS", "ea" : "ea", "eac" : "eac", "eadb" : "eadb", "eal" : "eal", "eaw" : "eaw", "ec" : "ec", "eca" : "eca", "eemb" : "eemb", "eemp" : "eemp", "eemt" : "eemt", "ef1" : "ef1", "ef3" : "ef3", "ef4" : "ef4", "efa" : "efa", "efb" : "efb", "efc" : "efc", "efh" : "efh", "efi" : "efi", "efl" : "efl", "efr" : "efr", "efs" : "efs", "ei" : "ei", "el:Ambisonics-11-cube-decoder" : "el:Ambisonics-11-cube-decoder", "el:Ambisonics-11-hexagon-decoder" : "el:Ambisonics-11-hexagon-decoder", "el:Ambisonics-11-mono-panner" : "el:Ambisonics-11-mono-panner", "el:Ambisonics-11-rotator" : "el:Ambisonics-11-rotator", "el:Ambisonics-11-square-decoder" : "el:Ambisonics-11-square-decoder", "el:Ambisonics-11-stereo-panner" : "el:Ambisonics-11-stereo-panner", "el:Ambisonics-21-panner" : "el:Ambisonics-21-panner", "el:Ambisonics-21-rotator" : "el:Ambisonics-21-rotator", "el:Ambisonics-22-panner" : "el:Ambisonics-22-panner", "el:Ambisonics-22-rotator" : "el:Ambisonics-22-rotator", "el:Ambisonics-31-panner" : "el:Ambisonics-31-panner", "el:Ambisonics-31-rotator" : "el:Ambisonics-31-rotator", "el:Ambisonics-33-panner" : "el:Ambisonics-33-panner", "el:Ambisonics-33-rotator" : "el:Ambisonics-33-rotator", "el:AmpVTS" : "el:AmpVTS", "el:AutoFilter" : "el:AutoFilter", "el:CEO" : "el:CEO", "el:CabinetIV" : "el:CabinetIV", "el:ChorusI" : "el:ChorusI", "el:Click" : "el:Click", "el:Compress" : "el:Compress", "el:CompressX2" : "el:CompressX2", "el:Eq10" : "el:Eq10", "el:Eq10X2" : "el:Eq10X2", "el:Eq4p" : "el:Eq4p", "el:Fractal" : "el:Fractal", "el:G2reverb" : "el:G2reverb", "el:Narrower" : "el:Narrower", "el:NoiseGate" : "el:NoiseGate", "el:Parametric1" : "el:Parametric1", "el:PhaserII" : "el:PhaserII", "el:Plate" : "el:Plate", "el:PlateX2" : "el:PlateX2", "el:Saturate" : "el:Saturate", "el:Scape" : "el:Scape", "el:Sin" : "el:Sin", "el:Spice" : "el:Spice", "el:SpiceX2" : "el:SpiceX2", "el:ToneStack" : "el:ToneStack", "el:Tricardioid-to-AMB" : "el:Tricardioid-to-AMB", "el:UHJ-decoder" : "el:UHJ-decoder", "el:UHJ-encoder" : "el:UHJ-encoder", "el:Virtualmic" : "el:Virtualmic", "el:White" : "el:White", "el:Wider" : "el:Wider", "el:alias" : "el:alias", "el:allpass_c" : "el:allpass_c", "el:allpass_l" : "el:allpass_l", "el:allpass_n" : "el:allpass_n", "el:amPitchshift" : "el:amPitchshift", "el:amp" : "el:amp", "el:amp_mono" : "el:amp_mono", "el:amp_stereo" : "el:amp_stereo", "el:analogueOsc" : "el:analogueOsc", "el:artificialLatency" : "el:artificialLatency", "el:autoPhaser" : "el:autoPhaser", "el:bandpass_a_iir" : "el:bandpass_a_iir", "el:bandpass_iir" : "el:bandpass_iir", "el:bodeShifter" : "el:bodeShifter", "el:bodeShifterCV" : "el:bodeShifterCV", "el:butthigh_iir" : "el:butthigh_iir", "el:buttlow_iir" : "el:buttlow_iir", "el:bwxover_iir" : "el:bwxover_iir", "el:chebstortion" : "el:chebstortion", "el:comb" : "el:comb", "el:combSplitter" : "el:combSplitter", "el:comb_c" : "el:comb_c", "el:comb_l" : "el:comb_l", "el:comb_n" : "el:comb_n", "el:const" : "el:const", "el:crossoverDist" : "el:crossoverDist", "el:dcRemove" : "el:dcRemove", "el:decay" : "el:decay", "el:decimator" : "el:decimator", "el:declip" : "el:declip", "el:delay_5s" : "el:delay_5s", "el:delay_c" : "el:delay_c", "el:delay_l" : "el:delay_l", "el:delay_n" : "el:delay_n", "el:delayorama" : "el:delayorama", "el:diode" : "el:diode", "el:divider" : "el:divider", "el:djFlanger" : "el:djFlanger", "el:dj_eq" : "el:dj_eq", "el:dj_eq_mono" : "el:dj_eq_mono", "el:dysonCompress" : "el:dysonCompress", "el:fadDelay" : "el:fadDelay", "el:fastLookaheadLimiter" : "el:fastLookaheadLimiter", "el:flanger" : "el:flanger", "el:fmOsc" : "el:fmOsc", "el:foldover" : "el:foldover", "el:fourByFourPole" : "el:fourByFourPole", "el:foverdrive" : "el:foverdrive", "el:freqTracker" : "el:freqTracker", "el:gate" : "el:gate", "el:giantFlange" : "el:giantFlange", "el:gong" : "el:gong", "el:gongBeater" : "el:gongBeater", "el:gsm" : "el:gsm", "el:gverb" : "el:gverb", "el:hardLimiter" : "el:hardLimiter", "el:harmonicGen" : "el:harmonicGen", "el:hermesFilter" : "el:hermesFilter", "el:highpass_iir" : "el:highpass_iir", "el:hilbert" : "el:hilbert", "el:hpf" : "el:hpf", "el:imp" : "el:imp", "el:impulse_fc" : "el:impulse_fc", "el:inv" : "el:inv", "el:invada_hp_mono_filter_module_0_1" : "el:invada_hp_mono_filter_module_0_1", "el:invada_hp_stereo_filter_module_0_1" : "el:invada_hp_stereo_filter_module_0_1", "el:invada_lp_mono_filter_module_0_1" : "el:invada_lp_mono_filter_module_0_1", "el:invada_lp_stereo_filter_module_0_1" : "el:invada_lp_stereo_filter_module_0_1", "el:invada_mono_compressor_module_0_1" : "el:invada_mono_compressor_module_0_1", "el:invada_mono_reverbER_module_0_1" : "el:invada_mono_reverbER_module_0_1", "el:invada_mono_tube_module_0_1" : "el:invada_mono_tube_module_0_1", "el:invada_stereo_compressor_module_0_1" : "el:invada_stereo_compressor_module_0_1", "el:invada_stereo_input_module_0_1" : "el:invada_stereo_input_module_0_1", "el:invada_stereo_tube_module_0_1" : "el:invada_stereo_tube_module_0_1", "el:invada_sum_reverbER_module_0_1" : "el:invada_sum_reverbER_module_0_1", "el:karaoke" : "el:karaoke", "el:lcrDelay" : "el:lcrDelay", "el:lfoPhaser" : "el:lfoPhaser", "el:lowpass_iir" : "el:lowpass_iir", "el:lpf" : "el:lpf", "el:lsFilter" : "el:lsFilter", "el:matrixMSSt" : "el:matrixMSSt", "el:matrixSpatialiser" : "el:matrixSpatialiser", "el:matrixStMS" : "el:matrixStMS", "el:mbeq" : "el:mbeq", "el:modDelay" : "el:modDelay", "el:multivoiceChorus" : "el:multivoiceChorus", "el:noise_white" : "el:noise_white", "el:notch_iir" : "el:notch_iir", "el:pitchScale" : "el:pitchScale", "el:pitchScaleHQ" : "el:pitchScaleHQ", "el:plate" : "el:plate", "el:pointerCastDistortion" : "el:pointerCastDistortion", "el:rateShifter" : "el:rateShifter", "el:retroFlange" : "el:retroFlange", "el:revdelay" : "el:revdelay", "el:ringmod_1i1o1l" : "el:ringmod_1i1o1l", "el:ringmod_2i1o" : "el:ringmod_2i1o", "el:satanMaximiser" : "el:satanMaximiser", "el:sc1" : "el:sc1", "el:sc2" : "el:sc2", "el:sc3" : "el:sc3", "el:sc4" : "el:sc4", "el:sc4m" : "el:sc4m", "el:se4" : "el:se4", "el:shaper" : "el:shaper", "el:sifter" : "el:sifter", "el:sinCos" : "el:sinCos", "el:sine_faaa" : "el:sine_faaa", "el:sine_faac" : "el:sine_faac", "el:sine_fcaa" : "el:sine_fcaa", "el:sine_fcac" : "el:sine_fcac", "el:singlePara" : "el:singlePara", "el:sinusWavewrapper" : "el:sinusWavewrapper", "el:smoothDecimate" : "el:smoothDecimate", "el:split" : "el:split", "el:stepMuxer" : "el:stepMuxer", "el:surroundEncoder" : "el:surroundEncoder", "el:svf" : "el:svf", "el:tap_autopan" : "el:tap_autopan", "el:tap_chorusflanger" : "el:tap_chorusflanger", "el:tap_deesser" : "el:tap_deesser", "el:tap_doubler" : "el:tap_doubler", "el:tap_dynamics_m" : "el:tap_dynamics_m", "el:tap_dynamics_st" : "el:tap_dynamics_st", "el:tap_equalizer" : "el:tap_equalizer", "el:tap_equalizer_bw" : "el:tap_equalizer_bw", "el:tap_limiter" : "el:tap_limiter", "el:tap_pinknoise" : "el:tap_pinknoise", "el:tap_pitch" : "el:tap_pitch", "el:tap_reflector" : "el:tap_reflector", "el:tap_reverb" : "el:tap_reverb", "el:tap_rotspeak" : "el:tap_rotspeak", "el:tap_sigmoid" : "el:tap_sigmoid", "el:tap_stereo_echo" : "el:tap_stereo_echo", "el:tap_tremolo" : "el:tap_tremolo", "el:tap_tubewarmth" : "el:tap_tubewarmth", "el:tap_vibrato" : "el:tap_vibrato", "el:tapeDelay" : "el:tapeDelay", "el:transient" : "el:transient", "el:triplePara" : "el:triplePara", "el:valve" : "el:valve", "el:valveRect" : "el:valveRect", "el:vynil" : "el:vynil", "el:waveTerrain" : "el:waveTerrain", "el:xfade" : "el:xfade", "el:xfade4" : "el:xfade4", "el:zita-reverb" : "el:zita-reverb", "el:zita-reverb-amb" : "el:zita-reverb-amb", "el:zm1" : "el:zm1", "elv2:http://gareus.org/oss/lv2/b_overdrive" : "elv2:http://gareus.org/oss/lv2/b_overdrive", "elv2:http://gareus.org/oss/lv2/b_reverb" : "elv2:http://gareus.org/oss/lv2/b_reverb", "elv2:http://gareus.org/oss/lv2/b_whirl#extended" : "elv2:http://gareus.org/oss/lv2/b_whirl#extended", "elv2:http://gareus.org/oss/lv2/b_whirl#simple" : "elv2:http://gareus.org/oss/lv2/b_whirl#simple", "elv2:http://hyperglitch.com/dev/VocProc" : "elv2:http://hyperglitch.com/dev/VocProc", "elv2:http://plugin.org.uk/swh-plugins/alaw" : "elv2:http://plugin.org.uk/swh-plugins/alaw", "elv2:http://plugin.org.uk/swh-plugins/alias" : "elv2:http://plugin.org.uk/swh-plugins/alias", "elv2:http://plugin.org.uk/swh-plugins/allpass_c" : "elv2:http://plugin.org.uk/swh-plugins/allpass_c", "elv2:http://plugin.org.uk/swh-plugins/allpass_l" : "elv2:http://plugin.org.uk/swh-plugins/allpass_l", "elv2:http://plugin.org.uk/swh-plugins/allpass_n" : "elv2:http://plugin.org.uk/swh-plugins/allpass_n", "elv2:http://plugin.org.uk/swh-plugins/amPitchshift" : "elv2:http://plugin.org.uk/swh-plugins/amPitchshift", "elv2:http://plugin.org.uk/swh-plugins/amp" : "elv2:http://plugin.org.uk/swh-plugins/amp", "elv2:http://plugin.org.uk/swh-plugins/analogueOsc" : "elv2:http://plugin.org.uk/swh-plugins/analogueOsc", "elv2:http://plugin.org.uk/swh-plugins/artificialLatency" : "elv2:http://plugin.org.uk/swh-plugins/artificialLatency", "elv2:http://plugin.org.uk/swh-plugins/autoPhaser" : "elv2:http://plugin.org.uk/swh-plugins/autoPhaser", "elv2:http://plugin.org.uk/swh-plugins/bandpass_a_iir" : "elv2:http://plugin.org.uk/swh-plugins/bandpass_a_iir", "elv2:http://plugin.org.uk/swh-plugins/bandpass_iir" : "elv2:http://plugin.org.uk/swh-plugins/bandpass_iir", "elv2:http://plugin.org.uk/swh-plugins/bodeShifter" : "elv2:http://plugin.org.uk/swh-plugins/bodeShifter", "elv2:http://plugin.org.uk/swh-plugins/bodeShifterCV" : "elv2:http://plugin.org.uk/swh-plugins/bodeShifterCV", "elv2:http://plugin.org.uk/swh-plugins/butthigh_iir" : "elv2:http://plugin.org.uk/swh-plugins/butthigh_iir", "elv2:http://plugin.org.uk/swh-plugins/buttlow_iir" : "elv2:http://plugin.org.uk/swh-plugins/buttlow_iir", "elv2:http://plugin.org.uk/swh-plugins/bwxover_iir" : "elv2:http://plugin.org.uk/swh-plugins/bwxover_iir", "elv2:http://plugin.org.uk/swh-plugins/chebstortion" : "elv2:http://plugin.org.uk/swh-plugins/chebstortion", "elv2:http://plugin.org.uk/swh-plugins/comb" : "elv2:http://plugin.org.uk/swh-plugins/comb", "elv2:http://plugin.org.uk/swh-plugins/combSplitter" : "elv2:http://plugin.org.uk/swh-plugins/combSplitter", "elv2:http://plugin.org.uk/swh-plugins/comb_c" : "elv2:http://plugin.org.uk/swh-plugins/comb_c", "elv2:http://plugin.org.uk/swh-plugins/comb_l" : "elv2:http://plugin.org.uk/swh-plugins/comb_l", "elv2:http://plugin.org.uk/swh-plugins/comb_n" : "elv2:http://plugin.org.uk/swh-plugins/comb_n", "elv2:http://plugin.org.uk/swh-plugins/const" : "elv2:http://plugin.org.uk/swh-plugins/const", "elv2:http://plugin.org.uk/swh-plugins/crossoverDist" : "elv2:http://plugin.org.uk/swh-plugins/crossoverDist", "elv2:http://plugin.org.uk/swh-plugins/dcRemove" : "elv2:http://plugin.org.uk/swh-plugins/dcRemove", "elv2:http://plugin.org.uk/swh-plugins/decay" : "elv2:http://plugin.org.uk/swh-plugins/decay", "elv2:http://plugin.org.uk/swh-plugins/decimator" : "elv2:http://plugin.org.uk/swh-plugins/decimator", "elv2:http://plugin.org.uk/swh-plugins/declip" : "elv2:http://plugin.org.uk/swh-plugins/declip", "elv2:http://plugin.org.uk/swh-plugins/delay_c" : "elv2:http://plugin.org.uk/swh-plugins/delay_c", "elv2:http://plugin.org.uk/swh-plugins/delay_l" : "elv2:http://plugin.org.uk/swh-plugins/delay_l", "elv2:http://plugin.org.uk/swh-plugins/delay_n" : "elv2:http://plugin.org.uk/swh-plugins/delay_n", "elv2:http://plugin.org.uk/swh-plugins/delayorama" : "elv2:http://plugin.org.uk/swh-plugins/delayorama", "elv2:http://plugin.org.uk/swh-plugins/diode" : "elv2:http://plugin.org.uk/swh-plugins/diode", "elv2:http://plugin.org.uk/swh-plugins/divider" : "elv2:http://plugin.org.uk/swh-plugins/divider", "elv2:http://plugin.org.uk/swh-plugins/djFlanger" : "elv2:http://plugin.org.uk/swh-plugins/djFlanger", "elv2:http://plugin.org.uk/swh-plugins/dj_eq" : "elv2:http://plugin.org.uk/swh-plugins/dj_eq", "elv2:http://plugin.org.uk/swh-plugins/dj_eq_mono" : "elv2:http://plugin.org.uk/swh-plugins/dj_eq_mono", "elv2:http://plugin.org.uk/swh-plugins/dysonCompress" : "elv2:http://plugin.org.uk/swh-plugins/dysonCompress", "elv2:http://plugin.org.uk/swh-plugins/fadDelay" : "elv2:http://plugin.org.uk/swh-plugins/fadDelay", "elv2:http://plugin.org.uk/swh-plugins/fastLookaheadLimiter" : "elv2:http://plugin.org.uk/swh-plugins/fastLookaheadLimiter", "elv2:http://plugin.org.uk/swh-plugins/flanger" : "elv2:http://plugin.org.uk/swh-plugins/flanger", "elv2:http://plugin.org.uk/swh-plugins/fmOsc" : "elv2:http://plugin.org.uk/swh-plugins/fmOsc", "elv2:http://plugin.org.uk/swh-plugins/foldover" : "elv2:http://plugin.org.uk/swh-plugins/foldover", "elv2:http://plugin.org.uk/swh-plugins/fourByFourPole" : "elv2:http://plugin.org.uk/swh-plugins/fourByFourPole", "elv2:http://plugin.org.uk/swh-plugins/foverdrive" : "elv2:http://plugin.org.uk/swh-plugins/foverdrive", "elv2:http://plugin.org.uk/swh-plugins/freqTracker" : "elv2:http://plugin.org.uk/swh-plugins/freqTracker", "elv2:http://plugin.org.uk/swh-plugins/gate" : "elv2:http://plugin.org.uk/swh-plugins/gate", "elv2:http://plugin.org.uk/swh-plugins/giantFlange" : "elv2:http://plugin.org.uk/swh-plugins/giantFlange", "elv2:http://plugin.org.uk/swh-plugins/gong" : "elv2:http://plugin.org.uk/swh-plugins/gong", "elv2:http://plugin.org.uk/swh-plugins/gongBeater" : "elv2:http://plugin.org.uk/swh-plugins/gongBeater", "elv2:http://plugin.org.uk/swh-plugins/gverb" : "elv2:http://plugin.org.uk/swh-plugins/gverb", "elv2:http://plugin.org.uk/swh-plugins/hardLimiter" : "elv2:http://plugin.org.uk/swh-plugins/hardLimiter", "elv2:http://plugin.org.uk/swh-plugins/harmonicGen" : "elv2:http://plugin.org.uk/swh-plugins/harmonicGen", "elv2:http://plugin.org.uk/swh-plugins/hermesFilter" : "elv2:http://plugin.org.uk/swh-plugins/hermesFilter", "elv2:http://plugin.org.uk/swh-plugins/highpass_iir" : "elv2:http://plugin.org.uk/swh-plugins/highpass_iir", "elv2:http://plugin.org.uk/swh-plugins/hilbert" : "elv2:http://plugin.org.uk/swh-plugins/hilbert", "elv2:http://plugin.org.uk/swh-plugins/impulse_fc" : "elv2:http://plugin.org.uk/swh-plugins/impulse_fc", "elv2:http://plugin.org.uk/swh-plugins/inv" : "elv2:http://plugin.org.uk/swh-plugins/inv", "elv2:http://plugin.org.uk/swh-plugins/karaoke" : "elv2:http://plugin.org.uk/swh-plugins/karaoke", "elv2:http://plugin.org.uk/swh-plugins/lcrDelay" : "elv2:http://plugin.org.uk/swh-plugins/lcrDelay", "elv2:http://plugin.org.uk/swh-plugins/lfoPhaser" : "elv2:http://plugin.org.uk/swh-plugins/lfoPhaser", "elv2:http://plugin.org.uk/swh-plugins/lookaheadLimiter" : "elv2:http://plugin.org.uk/swh-plugins/lookaheadLimiter", "elv2:http://plugin.org.uk/swh-plugins/lookaheadLimiterConst" : "elv2:http://plugin.org.uk/swh-plugins/lookaheadLimiterConst", "elv2:http://plugin.org.uk/swh-plugins/lowpass_iir" : "elv2:http://plugin.org.uk/swh-plugins/lowpass_iir", "elv2:http://plugin.org.uk/swh-plugins/lsFilter" : "elv2:http://plugin.org.uk/swh-plugins/lsFilter", "elv2:http://plugin.org.uk/swh-plugins/matrixMSSt" : "elv2:http://plugin.org.uk/swh-plugins/matrixMSSt", "elv2:http://plugin.org.uk/swh-plugins/matrixSpatialiser" : "elv2:http://plugin.org.uk/swh-plugins/matrixSpatialiser", "elv2:http://plugin.org.uk/swh-plugins/matrixStMS" : "elv2:http://plugin.org.uk/swh-plugins/matrixStMS", "elv2:http://plugin.org.uk/swh-plugins/mbeq" : "elv2:http://plugin.org.uk/swh-plugins/mbeq", "elv2:http://plugin.org.uk/swh-plugins/modDelay" : "elv2:http://plugin.org.uk/swh-plugins/modDelay", "elv2:http://plugin.org.uk/swh-plugins/multivoiceChorus" : "elv2:http://plugin.org.uk/swh-plugins/multivoiceChorus", "elv2:http://plugin.org.uk/swh-plugins/pitchScaleHQ" : "elv2:http://plugin.org.uk/swh-plugins/pitchScaleHQ", "elv2:http://plugin.org.uk/swh-plugins/plate" : "elv2:http://plugin.org.uk/swh-plugins/plate", "elv2:http://plugin.org.uk/swh-plugins/pointerCastDistortion" : "elv2:http://plugin.org.uk/swh-plugins/pointerCastDistortion", "elv2:http://plugin.org.uk/swh-plugins/rateShifter" : "elv2:http://plugin.org.uk/swh-plugins/rateShifter", "elv2:http://plugin.org.uk/swh-plugins/retroFlange" : "elv2:http://plugin.org.uk/swh-plugins/retroFlange", "elv2:http://plugin.org.uk/swh-plugins/revdelay" : "elv2:http://plugin.org.uk/swh-plugins/revdelay", "elv2:http://plugin.org.uk/swh-plugins/ringmod_1i1o1l" : "elv2:http://plugin.org.uk/swh-plugins/ringmod_1i1o1l", "elv2:http://plugin.org.uk/swh-plugins/ringmod_2i1o" : "elv2:http://plugin.org.uk/swh-plugins/ringmod_2i1o", "elv2:http://plugin.org.uk/swh-plugins/satanMaximiser" : "elv2:http://plugin.org.uk/swh-plugins/satanMaximiser", "elv2:http://plugin.org.uk/swh-plugins/sc1" : "elv2:http://plugin.org.uk/swh-plugins/sc1", "elv2:http://plugin.org.uk/swh-plugins/sc2" : "elv2:http://plugin.org.uk/swh-plugins/sc2", "elv2:http://plugin.org.uk/swh-plugins/sc3" : "elv2:http://plugin.org.uk/swh-plugins/sc3", "elv2:http://plugin.org.uk/swh-plugins/sc4" : "elv2:http://plugin.org.uk/swh-plugins/sc4", "elv2:http://plugin.org.uk/swh-plugins/se4" : "elv2:http://plugin.org.uk/swh-plugins/se4", "elv2:http://plugin.org.uk/swh-plugins/shaper" : "elv2:http://plugin.org.uk/swh-plugins/shaper", "elv2:http://plugin.org.uk/swh-plugins/sifter" : "elv2:http://plugin.org.uk/swh-plugins/sifter", "elv2:http://plugin.org.uk/swh-plugins/sinCos" : "elv2:http://plugin.org.uk/swh-plugins/sinCos", "elv2:http://plugin.org.uk/swh-plugins/singlePara" : "elv2:http://plugin.org.uk/swh-plugins/singlePara", "elv2:http://plugin.org.uk/swh-plugins/sinusWavewrapper" : "elv2:http://plugin.org.uk/swh-plugins/sinusWavewrapper", "elv2:http://plugin.org.uk/swh-plugins/smoothDecimate" : "elv2:http://plugin.org.uk/swh-plugins/smoothDecimate", "elv2:http://plugin.org.uk/swh-plugins/split" : "elv2:http://plugin.org.uk/swh-plugins/split", "elv2:http://plugin.org.uk/swh-plugins/surroundEncoder" : "elv2:http://plugin.org.uk/swh-plugins/surroundEncoder", "elv2:http://plugin.org.uk/swh-plugins/svf" : "elv2:http://plugin.org.uk/swh-plugins/svf", "elv2:http://plugin.org.uk/swh-plugins/tapeDelay" : "elv2:http://plugin.org.uk/swh-plugins/tapeDelay", "elv2:http://plugin.org.uk/swh-plugins/transient" : "elv2:http://plugin.org.uk/swh-plugins/transient", "elv2:http://plugin.org.uk/swh-plugins/triplePara" : "elv2:http://plugin.org.uk/swh-plugins/triplePara", "elv2:http://plugin.org.uk/swh-plugins/ulaw" : "elv2:http://plugin.org.uk/swh-plugins/ulaw", "elv2:http://plugin.org.uk/swh-plugins/valve" : "elv2:http://plugin.org.uk/swh-plugins/valve", "elv2:http://plugin.org.uk/swh-plugins/valveRect" : "elv2:http://plugin.org.uk/swh-plugins/valveRect", "elv2:http://plugin.org.uk/swh-plugins/vynil" : "elv2:http://plugin.org.uk/swh-plugins/vynil", "elv2:http://plugin.org.uk/swh-plugins/waveTerrain" : "elv2:http://plugin.org.uk/swh-plugins/waveTerrain", "elv2:http://plugin.org.uk/swh-plugins/xfade" : "elv2:http://plugin.org.uk/swh-plugins/xfade", "elv2:http://plugin.org.uk/swh-plugins/xfade4" : "elv2:http://plugin.org.uk/swh-plugins/xfade4", "elv2:http://plugin.org.uk/swh-plugins/zm1" : "elv2:http://plugin.org.uk/swh-plugins/zm1", "enm" : "enm", "epp" : "epp", "erc" : "erc", "erm" : "erm", "etc" : "etc", "etd" : "etd", "ete" : "ete", "etf" : "etf", "etl" : "etl", "etm" : "etm", "etp" : "etp", "etr" : "etr", "ev" : "ev", "evp" : "evp", "ezf" : "ezf", "ezx" : "ezx", "gc" : "gc", "ge" : "ge", "gm" : "gm", "kf" : "kf", "kl" : "kl", "kl2" : "kl2", "klg" : "klg", "km" : "km", "kog" : "kog", "kos" : "kos", "ksv" : "ksv", "lv2-VocProc" : "elv2:http://hyperglitch.com/dev/VocProc", "lv2-alaw" : "elv2:http://plugin.org.uk/swh-plugins/alaw", "lv2-alias" : "elv2:http://plugin.org.uk/swh-plugins/alias", "lv2-allpass_c" : "elv2:http://plugin.org.uk/swh-plugins/allpass_c", "lv2-allpass_l" : "elv2:http://plugin.org.uk/swh-plugins/allpass_l", "lv2-allpass_n" : "elv2:http://plugin.org.uk/swh-plugins/allpass_n", "lv2-amPitchshift" : "elv2:http://plugin.org.uk/swh-plugins/amPitchshift", "lv2-amp" : "elv2:http://plugin.org.uk/swh-plugins/amp", "lv2-analogueOsc" : "elv2:http://plugin.org.uk/swh-plugins/analogueOsc", "lv2-artificialLatency" : "elv2:http://plugin.org.uk/swh-plugins/artificialLatency", "lv2-autoPhaser" : "elv2:http://plugin.org.uk/swh-plugins/autoPhaser", "lv2-b_overdrive" : "elv2:http://gareus.org/oss/lv2/b_overdrive", "lv2-b_reverb" : "elv2:http://gareus.org/oss/lv2/b_reverb", "lv2-b_whirl#extended" : "elv2:http://gareus.org/oss/lv2/b_whirl#extended", "lv2-b_whirl#simple" : "elv2:http://gareus.org/oss/lv2/b_whirl#simple", "lv2-bandpass_a_iir" : "elv2:http://plugin.org.uk/swh-plugins/bandpass_a_iir", "lv2-bandpass_iir" : "elv2:http://plugin.org.uk/swh-plugins/bandpass_iir", "lv2-bodeShifter" : "elv2:http://plugin.org.uk/swh-plugins/bodeShifter", "lv2-bodeShifterCV" : "elv2:http://plugin.org.uk/swh-plugins/bodeShifterCV", "lv2-butthigh_iir" : "elv2:http://plugin.org.uk/swh-plugins/butthigh_iir", "lv2-buttlow_iir" : "elv2:http://plugin.org.uk/swh-plugins/buttlow_iir", "lv2-bwxover_iir" : "elv2:http://plugin.org.uk/swh-plugins/bwxover_iir", "lv2-chebstortion" : "elv2:http://plugin.org.uk/swh-plugins/chebstortion", "lv2-comb" : "elv2:http://plugin.org.uk/swh-plugins/comb", "lv2-combSplitter" : "elv2:http://plugin.org.uk/swh-plugins/combSplitter", "lv2-comb_c" : "elv2:http://plugin.org.uk/swh-plugins/comb_c", "lv2-comb_l" : "elv2:http://plugin.org.uk/swh-plugins/comb_l", "lv2-comb_n" : "elv2:http://plugin.org.uk/swh-plugins/comb_n", "lv2-const" : "elv2:http://plugin.org.uk/swh-plugins/const", "lv2-crossoverDist" : "elv2:http://plugin.org.uk/swh-plugins/crossoverDist", "lv2-dcRemove" : "elv2:http://plugin.org.uk/swh-plugins/dcRemove", "lv2-decay" : "elv2:http://plugin.org.uk/swh-plugins/decay", "lv2-decimator" : "elv2:http://plugin.org.uk/swh-plugins/decimator", "lv2-declip" : "elv2:http://plugin.org.uk/swh-plugins/declip", "lv2-delay_c" : "elv2:http://plugin.org.uk/swh-plugins/delay_c", "lv2-delay_l" : "elv2:http://plugin.org.uk/swh-plugins/delay_l", "lv2-delay_n" : "elv2:http://plugin.org.uk/swh-plugins/delay_n", "lv2-delayorama" : "elv2:http://plugin.org.uk/swh-plugins/delayorama", "lv2-diode" : "elv2:http://plugin.org.uk/swh-plugins/diode", "lv2-divider" : "elv2:http://plugin.org.uk/swh-plugins/divider", "lv2-djFlanger" : "elv2:http://plugin.org.uk/swh-plugins/djFlanger", "lv2-dj_eq" : "elv2:http://plugin.org.uk/swh-plugins/dj_eq", "lv2-dj_eq_mono" : "elv2:http://plugin.org.uk/swh-plugins/dj_eq_mono", "lv2-dysonCompress" : "elv2:http://plugin.org.uk/swh-plugins/dysonCompress", "lv2-fadDelay" : "elv2:http://plugin.org.uk/swh-plugins/fadDelay", "lv2-fastLookaheadLimiter" : "elv2:http://plugin.org.uk/swh-plugins/fastLookaheadLimiter", "lv2-flanger" : "elv2:http://plugin.org.uk/swh-plugins/flanger", "lv2-fmOsc" : "elv2:http://plugin.org.uk/swh-plugins/fmOsc", "lv2-foldover" : "elv2:http://plugin.org.uk/swh-plugins/foldover", "lv2-fourByFourPole" : "elv2:http://plugin.org.uk/swh-plugins/fourByFourPole", "lv2-foverdrive" : "elv2:http://plugin.org.uk/swh-plugins/foverdrive", "lv2-freqTracker" : "elv2:http://plugin.org.uk/swh-plugins/freqTracker", "lv2-gate" : "elv2:http://plugin.org.uk/swh-plugins/gate", "lv2-giantFlange" : "elv2:http://plugin.org.uk/swh-plugins/giantFlange", "lv2-gong" : "elv2:http://plugin.org.uk/swh-plugins/gong", "lv2-gongBeater" : "elv2:http://plugin.org.uk/swh-plugins/gongBeater", "lv2-gverb" : "elv2:http://plugin.org.uk/swh-plugins/gverb", "lv2-hardLimiter" : "elv2:http://plugin.org.uk/swh-plugins/hardLimiter", "lv2-harmonicGen" : "elv2:http://plugin.org.uk/swh-plugins/harmonicGen", "lv2-hermesFilter" : "elv2:http://plugin.org.uk/swh-plugins/hermesFilter", "lv2-highpass_iir" : "elv2:http://plugin.org.uk/swh-plugins/highpass_iir", "lv2-hilbert" : "elv2:http://plugin.org.uk/swh-plugins/hilbert", "lv2-impulse_fc" : "elv2:http://plugin.org.uk/swh-plugins/impulse_fc", "lv2-inv" : "elv2:http://plugin.org.uk/swh-plugins/inv", "lv2-karaoke" : "elv2:http://plugin.org.uk/swh-plugins/karaoke", "lv2-lcrDelay" : "elv2:http://plugin.org.uk/swh-plugins/lcrDelay", "lv2-lfoPhaser" : "elv2:http://plugin.org.uk/swh-plugins/lfoPhaser", "lv2-lookaheadLimiter" : "elv2:http://plugin.org.uk/swh-plugins/lookaheadLimiter", "lv2-lookaheadLimiterConst" : "elv2:http://plugin.org.uk/swh-plugins/lookaheadLimiterConst", "lv2-lowpass_iir" : "elv2:http://plugin.org.uk/swh-plugins/lowpass_iir", "lv2-lsFilter" : "elv2:http://plugin.org.uk/swh-plugins/lsFilter", "lv2-matrixMSSt" : "elv2:http://plugin.org.uk/swh-plugins/matrixMSSt", "lv2-matrixSpatialiser" : "elv2:http://plugin.org.uk/swh-plugins/matrixSpatialiser", "lv2-matrixStMS" : "elv2:http://plugin.org.uk/swh-plugins/matrixStMS", "lv2-mbeq" : "elv2:http://plugin.org.uk/swh-plugins/mbeq", "lv2-modDelay" : "elv2:http://plugin.org.uk/swh-plugins/modDelay", "lv2-multivoiceChorus" : "elv2:http://plugin.org.uk/swh-plugins/multivoiceChorus", "lv2-pitchScaleHQ" : "elv2:http://plugin.org.uk/swh-plugins/pitchScaleHQ", "lv2-plate" : "elv2:http://plugin.org.uk/swh-plugins/plate", "lv2-pointerCastDistortion" : "elv2:http://plugin.org.uk/swh-plugins/pointerCastDistortion", "lv2-rateShifter" : "elv2:http://plugin.org.uk/swh-plugins/rateShifter", "lv2-retroFlange" : "elv2:http://plugin.org.uk/swh-plugins/retroFlange", "lv2-revdelay" : "elv2:http://plugin.org.uk/swh-plugins/revdelay", "lv2-ringmod_1i1o1l" : "elv2:http://plugin.org.uk/swh-plugins/ringmod_1i1o1l", "lv2-ringmod_2i1o" : "elv2:http://plugin.org.uk/swh-plugins/ringmod_2i1o", "lv2-satanMaximiser" : "elv2:http://plugin.org.uk/swh-plugins/satanMaximiser", "lv2-sc1" : "elv2:http://plugin.org.uk/swh-plugins/sc1", "lv2-sc2" : "elv2:http://plugin.org.uk/swh-plugins/sc2", "lv2-sc3" : "elv2:http://plugin.org.uk/swh-plugins/sc3", "lv2-sc4" : "elv2:http://plugin.org.uk/swh-plugins/sc4", "lv2-se4" : "elv2:http://plugin.org.uk/swh-plugins/se4", "lv2-shaper" : "elv2:http://plugin.org.uk/swh-plugins/shaper", "lv2-sifter" : "elv2:http://plugin.org.uk/swh-plugins/sifter", "lv2-sinCos" : "elv2:http://plugin.org.uk/swh-plugins/sinCos", "lv2-singlePara" : "elv2:http://plugin.org.uk/swh-plugins/singlePara", "lv2-sinusWavewrapper" : "elv2:http://plugin.org.uk/swh-plugins/sinusWavewrapper", "lv2-smoothDecimate" : "elv2:http://plugin.org.uk/swh-plugins/smoothDecimate", "lv2-split" : "elv2:http://plugin.org.uk/swh-plugins/split", "lv2-surroundEncoder" : "elv2:http://plugin.org.uk/swh-plugins/surroundEncoder", "lv2-svf" : "elv2:http://plugin.org.uk/swh-plugins/svf", "lv2-tapeDelay" : "elv2:http://plugin.org.uk/swh-plugins/tapeDelay", "lv2-transient" : "elv2:http://plugin.org.uk/swh-plugins/transient", "lv2-triplePara" : "elv2:http://plugin.org.uk/swh-plugins/triplePara", "lv2-ulaw" : "elv2:http://plugin.org.uk/swh-plugins/ulaw", "lv2-valve" : "elv2:http://plugin.org.uk/swh-plugins/valve", "lv2-valveRect" : "elv2:http://plugin.org.uk/swh-plugins/valveRect", "lv2-vynil" : "elv2:http://plugin.org.uk/swh-plugins/vynil", "lv2-waveTerrain" : "elv2:http://plugin.org.uk/swh-plugins/waveTerrain", "lv2-xfade" : "elv2:http://plugin.org.uk/swh-plugins/xfade", "lv2-xfade4" : "elv2:http://plugin.org.uk/swh-plugins/xfade4", "lv2-zm1" : "elv2:http://plugin.org.uk/swh-plugins/zm1", "pn:dyn_compress_brutal" : "pn:dyn_compress_brutal", "pn:dyn_compress_hard" : "pn:dyn_compress_hard", "pn:dyn_compress_infinite" : "pn:dyn_compress_infinite", "pn:dyn_compress_medium" : "pn:dyn_compress_medium", "pn:dyn_compress_soft" : "pn:dyn_compress_soft", "pn:dyn_compress_supersoft" : "pn:dyn_compress_supersoft", "pn:eq_template" : "pn:eq_template", "pn:eq_template2" : "pn:eq_template2", "pn:f_bandpass" : "pn:f_bandpass", "pn:f_filtertest" : "pn:f_filtertest", "pn:f_high_and_low" : "pn:f_high_and_low", "pn:f_highpass" : "pn:f_highpass", "pn:f_inverse_comb" : "pn:f_inverse_comb", "pn:f_lowp_sine" : "pn:f_lowp_sine", "pn:f_lowp_sine2" : "pn:f_lowp_sine2", "pn:f_lowpass" : "pn:f_lowpass", "pn:f_rejectband" : "pn:f_rejectband", "pn:f_res_bandpass" : "pn:f_res_bandpass", "pn:f_res_lowpass" : "pn:f_res_lowpass", "pn:f_resonator" : "pn:f_resonator", "pn:f_two_filters" : "pn:f_two_filters", "pn:f_two_filters_pareq" : "pn:f_two_filters_pareq", "pn:gate_crop" : "pn:gate_crop", "pn:gate_noisegate_1" : "pn:gate_noisegate_1", "pn:gate_noisegate_delanalog" : "pn:gate_noisegate_delanalog", "pn:gate_threshold" : "pn:gate_threshold", "pn:lad_hermes" : "pn:lad_hermes", "pn:lad_metronome" : "pn:lad_metronome", "pn:lad_oscillator_stack" : "pn:lad_oscillator_stack", "pn:lad_oscillator_test" : "pn:lad_oscillator_test", "pn:lad_sc4" : "pn:lad_sc4", "pn:lad_sc4_rg" : "pn:lad_sc4_rg", "pn:metronome" : "pn:metronome", "pn:time_chorus1" : "pn:time_chorus1", "pn:time_delay1" : "pn:time_delay1", "pn:time_delay2" : "pn:time_delay2", "pn:time_flanger1" : "pn:time_flanger1", "pn:time_phaser1" : "pn:time_phaser1", "pn:time_reverb1" : "pn:time_reverb1", "pn:time_reverb2" : "pn:time_reverb2", "pn:time_reverb3" : "pn:time_reverb3", "pn:time_reverb4" : "pn:time_reverb4", "pn:time_wicked_dub" : "pn:time_wicked_dub", "pn:var_aw" : "pn:var_aw", "pn:var_aw_custom" : "pn:var_aw_custom", "pn:var_aw_ksv" : "pn:var_aw_ksv", "pn:var_aw_tri" : "pn:var_aw_tri", "pn:var_aw_tri_custom" : "pn:var_aw_tri_custom", "pn:var_chipmunk" : "pn:var_chipmunk", "pn:var_dali" : "pn:var_dali", "pn:var_molten_tape" : "pn:var_molten_tape", "pn:var_paralmadness" : "pn:var_paralmadness", "pn:var_parchip" : "pn:var_parchip", "pn:var_stretched_tape" : "pn:var_stretched_tape", "pn:var_sweeping_pan" : "pn:var_sweeping_pan", "pn:var_switching_pan" : "pn:var_switching_pan" }, "registry" : [ {}, { "code" : "eS", "count" : 1, "display" : "field", "name" : "Audio stamp", "number" : "1", "params" : [ { "name" : "stamp-id" } ] }, { "code" : "ea", "count" : "1", "display" : "scale", "name" : "Volume", "params" : [ { "begin" : "0", "default" : "100", "end" : "600", "name" : "Level %", "resolution" : "0" } ] }, { "code" : "eadb", "count" : "1", "display" : "scale", "name" : "Volume", "params" : [ { "begin" : "-40", "default" : "0", "end" : "60", "name" : "Level db", "resolution" : "0.5" } ] }, { "code" : "eac", "count" : 2, "display" : "field", "name" : "Channel amplify", "number" : "4", "params" : [ { "name" : "amp-%" }, { "name" : "channel" } ] }, { "code" : "eal", "count" : "1", "display" : "scale", "name" : "Limiter", "params" : [ { "begin" : "0", "default" : "100", "end" : "100", "name" : "Limit %", "resolution" : "0" } ] }, { "code" : "eaw", "count" : 2, "display" : "field", "name" : "Amplify with clipping control", "number" : "6", "params" : [ { "name" : "amp-%" }, { "name" : "max-clipped-samples" } ] }, { "code" : "ec", "count" : "2", "display" : "scale", "name" : "Compressor", "params" : [ { "begin" : "0", "default" : "1", "end" : "1", "name" : "Compression Rate (Db)", "resolution" : "0" }, { "begin" : "0", "default" : "50", "end" : "100", "name" : "Threshold %", "resolution" : "0" } ] }, { "code" : "eca", "count" : "4", "display" : "scale", "name" : "Advanced Compressor", "params" : [ { "begin" : "0", "default" : "69", "end" : "100", "name" : "Peak Level %", "resolution" : "0" }, { "begin" : "0", "default" : "2", "end" : "5", "name" : "Release Time (Seconds)", "resolution" : "0" }, { "begin" : "0", "default" : "0.5", "end" : "1", "name" : "Fast Compressor Rate", "resolution" : "0" }, { "begin" : "0", "default" : "1", "end" : "1", "name" : "Compressor Rate (Db)", "resolution" : "0" } ] }, { "code" : "eemb", "count" : 2, "display" : "field", "name" : "Pulse gate BPM", "number" : "9", "params" : [ { "name" : "bpm" }, { "name" : "on-time-msec" } ] }, { "code" : "eemp", "count" : 2, "display" : "field", "name" : "Pulse Gate", "number" : "10", "params" : [ { "name" : "freq-Hz" }, { "name" : "on-time-%" } ] }, { "code" : "eemt", "count" : 2, "display" : "field", "name" : "Tremolo", "number" : "11", "params" : [ { "name" : "bpm" }, { "name" : "depth-%" } ] }, { "code" : "ef1", "count" : "2", "display" : "scale", "name" : "Resonant Bandpass Filter", "params" : [ { "begin" : "0", "default" : "0", "end" : "20000", "name" : "Center Frequency (Hz)", "resolution" : "0" }, { "begin" : "0", "default" : "0", "end" : "2000", "name" : "Width (Hz)", "resolution" : "0" } ] }, { "code" : "ef3", "count" : "3", "display" : "scale", "name" : "Resonant Lowpass Filter", "params" : [ { "begin" : "0", "default" : "0", "end" : "5000", "name" : "Cutoff Frequency (Hz)", "resolution" : "0" }, { "begin" : "0", "default" : "0", "end" : "2", "name" : "Resonance", "resolution" : "0" }, { "begin" : "0", "default" : "0", "end" : "1", "name" : "Gain", "resolution" : "0" } ] }, { "code" : "ef4", "count" : 2, "display" : "field", "name" : "RC-lowpass filter", "number" : "14", "params" : [ { "name" : "cutoff-freq" }, { "name" : "resonance" } ] }, { "code" : "efa", "count" : "2", "display" : "scale", "name" : "Allpass Filter", "params" : [ { "begin" : "0", "default" : "0", "end" : "10000", "name" : "Delay Samples", "resolution" : "0" }, { "begin" : "0", "default" : "50", "end" : "100", "name" : "Feedback %", "resolution" : "0" } ] }, { "code" : "efb", "count" : "2", "display" : "scale", "name" : "Bandpass Filter", "params" : [ { "begin" : "0", "default" : "11000", "end" : "11000", "name" : "Center Frequency (Hz)", "resolution" : "0" }, { "begin" : "0", "default" : "22000", "end" : "22000", "name" : "Width (Hz)", "resolution" : "0" } ] }, { "code" : "efc", "count" : 2, "display" : "field", "name" : "Comb filter", "number" : "17", "params" : [ { "name" : "delay-samples" }, { "name" : "radius" } ] }, { "code" : "efh", "count" : "1", "display" : "scale", "name" : "Highpass Filter", "params" : [ { "begin" : "10000", "default" : "10000", "end" : "22000", "name" : "Cutoff Frequency (Hz)", "resolution" : "0" } ] }, { "code" : "efi", "count" : 2, "display" : "field", "name" : "Inverse comb filter", "number" : "19", "params" : [ { "name" : "delay-samples" }, { "name" : "radius" } ] }, { "code" : "efl", "count" : "1", "display" : "scale", "name" : "Lowpass Filter", "params" : [ { "begin" : "0", "default" : "0", "end" : "10000", "name" : "Cutoff Frequency (Hz)", "resolution" : "0" } ] }, { "code" : "efr", "count" : "2", "display" : "scale", "name" : "Bandreject Filter", "params" : [ { "begin" : "0", "default" : "11000", "end" : "11000", "name" : "Center Frequency (Hz)", "resolution" : "0" }, { "begin" : "0", "default" : "22000", "end" : "22000", "name" : "Width (Hz)", "resolution" : "0" } ] }, { "code" : "efs", "count" : "2", "display" : "scale", "name" : "Resonator Filter", "params" : [ { "begin" : "0", "default" : "11000", "end" : "11000", "name" : "Center Frequency (Hz)", "resolution" : "0" }, { "begin" : "0", "default" : "22000", "end" : "22000", "name" : "Width (Hz)", "resolution" : "0" } ] }, { "code" : "ei", "count" : 1, "display" : "field", "name" : "Pitch shifter", "number" : "23", "params" : [ { "name" : "change-%" } ] }, { "code" : "enm", "count" : "5", "display" : "scale", "name" : "Noise Gate", "params" : [ { "begin" : "0", "default" : "100", "end" : "100", "name" : "Threshold Level %", "resolution" : "0" }, { "begin" : "0", "default" : "200", "end" : "2000", "name" : "Pre Hold Time (ms)", "resolution" : "0" }, { "begin" : "0", "default" : "200", "end" : "2000", "name" : "Attack Time (ms)", "resolution" : "0" }, { "begin" : "0", "default" : "200", "end" : "2000", "name" : "Post Hold Time (ms)", "resolution" : "0" }, { "begin" : "0", "default" : "200", "end" : "2000", "name" : "Release Time (ms)", "resolution" : "0" } ] }, { "code" : "epp", "count" : "1", "display" : "scale", "name" : "Pan", "params" : [ { "begin" : "0", "default" : "50", "end" : "100", "name" : "Level %", "resolution" : "0" } ] }, { "code" : "chorder", "count" : 0, "display" : "field", "name" : "Channel select", "number" : "26", "params" : [] }, { "code" : "chcopy", "count" : 2, "display" : "field", "name" : "Channel copy", "number" : "27", "params" : [ { "name" : "from-channel" }, { "name" : "to-channel" } ] }, { "code" : "erc", "count" : 2, "display" : "field", "name" : "Channel copy", "number" : "28", "params" : [ { "name" : "from-channel" }, { "name" : "to-channel" } ] }, { "code" : "chmove", "count" : 2, "display" : "field", "name" : "Channel move", "number" : "29", "params" : [ { "name" : "from-channel" }, { "name" : "to-channel" } ] }, { "code" : "chmute", "count" : 1, "display" : "field", "name" : "Channel mute", "number" : "30", "params" : [ { "name" : "channel" } ] }, { "code" : "erm", "count" : 1, "display" : "field", "name" : "Mix to channel", "number" : "31", "params" : [ { "name" : "to-channel" } ] }, { "code" : "chmix", "count" : 1, "display" : "field", "name" : "Mix to channel", "number" : "32", "params" : [ { "name" : "to-channel" } ] }, { "code" : "etc", "count" : "4", "display" : "scale", "name" : "Chorus", "params" : [ { "begin" : "0", "default" : "200", "end" : "2000", "name" : "Delay Time (ms)", "resolution" : "0" }, { "begin" : "0", "default" : "500", "end" : "10000", "name" : "Variance Time Samples", "resolution" : "0" }, { "begin" : "0", "default" : "50", "end" : "100", "name" : "Feedback %", "resolution" : "0" }, { "begin" : "0", "default" : "50", "end" : "100", "name" : "LFO Frequency (Hz)", "resolution" : "0" } ] }, { "code" : "etd", "count" : "5", "display" : "scale", "name" : "Delay", "params" : [ { "begin" : "0", "default" : "200", "end" : "2000", "name" : "Delay Time (ms)", "resolution" : "0" }, { "begin" : "0", "default" : "0", "end" : "2", "name" : "Surround Mode (Normal, Surround St., Spread)", "resolution" : "1" }, { "begin" : "0", "default" : "50", "end" : "100", "name" : "Number of Delays", "resolution" : "0" }, { "begin" : "0", "default" : "50", "end" : "100", "name" : "Mix %", "resolution" : "0" }, { "begin" : "0", "default" : "0", "end" : "100", "name" : "Feedback %", "resolution" : "0" } ] }, { "code" : "ete", "count" : "3", "display" : "scale", "name" : "Advanced Reverb", "params" : [ { "begin" : "0", "default" : "10", "end" : "100", "name" : "Room Size (Meters)", "resolution" : "0" }, { "begin" : "0", "default" : "50", "end" : "100", "name" : "Feedback %", "resolution" : "0" }, { "begin" : "0", "default" : "50", "end" : "100", "name" : "Wet %", "resolution" : "0" } ] }, { "code" : "etf", "count" : "1", "display" : "scale", "name" : "Fake Stereo", "params" : [ { "begin" : "0", "default" : "40", "end" : "500", "name" : "Delay Time (ms)", "resolution" : "0" } ] }, { "code" : "etl", "count" : "4", "display" : "scale", "name" : "Flanger", "params" : [ { "begin" : "0", "default" : "200", "end" : "1000", "name" : "Delay Time (ms)", "resolution" : "0" }, { "begin" : "0", "default" : "200", "end" : "10000", "name" : "Variance Time Samples", "resolution" : "0" }, { "begin" : "0", "default" : "50", "end" : "100", "name" : "Feedback %", "resolution" : "0" }, { "begin" : "0", "default" : "50", "end" : "100", "name" : "LFO Frequency (Hz)", "resolution" : "0" } ] }, { "code" : "etm", "count" : "3", "display" : "scale", "name" : "Multitap Delay", "params" : [ { "begin" : "0", "default" : "200", "end" : "2000", "name" : "Delay Time (ms)", "resolution" : "0" }, { "begin" : "0", "default" : "20", "end" : "100", "name" : "Number of Delays", "resolution" : "0" }, { "begin" : "0", "default" : "50", "end" : "100", "name" : "Mix %", "resolution" : "0" } ] }, { "code" : "etp", "count" : "4", "display" : "scale", "name" : "Phaser", "params" : [ { "begin" : "0", "default" : "200", "end" : "2000", "name" : "Delay Time (ms)", "resolution" : "0" }, { "begin" : "0", "default" : "100", "end" : "10000", "name" : "Variance Time Samples", "resolution" : "0" }, { "begin" : "0", "default" : "50", "end" : "100", "name" : "Feedback %", "resolution" : "0" }, { "begin" : "0", "default" : "50", "end" : "100", "name" : "LFO Frequency (Hz)", "resolution" : "0" } ] }, { "code" : "etr", "count" : "3", "display" : "scale", "name" : "Reverb", "params" : [ { "begin" : "0", "default" : "200", "end" : "2000", "name" : "Delay Time (ms)", "resolution" : "0" }, { "begin" : "0", "default" : "0", "end" : "1", "name" : "Surround Mode (0=Normal, 1=Surround)", "resolution" : "1" }, { "begin" : "0", "default" : "50", "end" : "100", "name" : "Feedback %", "resolution" : "0" } ] }, { "code" : "ev", "count" : 2, "display" : "field", "name" : "Volume analysis", "number" : "41", "params" : [ { "name" : "cumulative-mode" }, { "name" : "result-max-multiplier" } ] }, { "code" : "evp", "count" : 0, "display" : "field", "name" : "Peak amplitude watcher", "number" : "42", "params" : [] }, { "code" : "ezf", "count" : 0, "display" : "field", "name" : "DC-Find", "number" : "43", "params" : [] }, { "code" : "ezx", "count" : 1, "display" : "field", "name" : "DC-Fix", "number" : "44", "params" : [ { "name" : "channel-count" } ] }, { "code" : "gc", "count" : 2, "display" : "field", "name" : "Time crop gate", "number" : "45", "params" : [ { "name" : "open-at-sec" }, { "name" : "duration-sec" } ] }, { "code" : "ge", "count" : 4, "display" : "field", "name" : "Threshold gate", "number" : "46", "params" : [ { "name" : "threshold-openlevel-%" }, { "name" : "threshold-closelevel-%" }, { "name" : "rms-enabled" }, { "name" : "reopen-count" } ] }, { "code" : "gm", "count" : 1, "display" : "field", "name" : "Manual gate", "number" : "47", "params" : [ { "name" : "state" } ] }, { "code" : "el:bandpass_a_iir", "count" : 2, "display" : "field", "name" : "Glame Bandpass Analog Filter", "number" : "1", "params" : [ { "name" : "Center Frequency (Hz)" }, { "name" : "Bandwidth (Hz)" } ] }, { "code" : "el:hilbert", "count" : 1, "display" : "field", "name" : "Hilbert transformer", "number" : "2", "params" : [ { "name" : "latency" } ] }, { "code" : "el:invada_lp_mono_filter_module_0_1", "count" : 3, "display" : "field", "name" : ":: Invada :: Filter - Low Pass Mono", "number" : "3", "params" : [ { "name" : "Frequency (Hz)" }, { "name" : "Gain (dB)" }, { "name" : "Soft Clip" } ] }, { "code" : "el:invada_hp_mono_filter_module_0_1", "count" : 3, "display" : "field", "name" : ":: Invada :: Filter - High Pass Mono", "number" : "4", "params" : [ { "name" : "Frequency (Hz)" }, { "name" : "Gain (dB)" }, { "name" : "Soft Clip" } ] }, { "code" : "el:invada_lp_stereo_filter_module_0_1", "count" : 3, "display" : "field", "name" : ":: Invada :: Filter - Low Pass Stereo", "number" : "5", "params" : [ { "name" : "Frequency (Hz)" }, { "name" : "Gain (dB)" }, { "name" : "Soft Clip" } ] }, { "code" : "el:invada_hp_stereo_filter_module_0_1", "count" : 3, "display" : "field", "name" : ":: Invada :: Filter - High Pass Stereo", "number" : "6", "params" : [ { "name" : "Frequency (Hz)" }, { "name" : "Gain (dB)" }, { "name" : "Soft Clip" } ] }, { "code" : "el:analogueOsc", "count" : 4, "display" : "field", "name" : "Analogue Oscillator", "number" : "7", "params" : [ { "name" : "Waveform (1=sin, 2=tri, 3=squ, 4=saw)" }, { "name" : "Frequency (Hz)" }, { "name" : "Warmth" }, { "name" : "Instability" } ] }, { "code" : "el:inv", "count" : 0, "display" : "field", "name" : "Inverter", "number" : "8", "params" : [] }, { "code" : "el:tap_tremolo", "count" : 3, "display" : "field", "name" : "TAP Tremolo", "number" : "9", "params" : [ { "name" : "Frequency [Hz]" }, { "name" : "Depth [%]" }, { "name" : "Gain [dB]" } ] }, { "code" : "el:valve", "count" : 2, "display" : "field", "name" : "Valve saturation", "number" : "10", "params" : [ { "name" : "Distortion level" }, { "name" : "Distortion character" } ] }, { "code" : "el:sc4", "count" : 9, "display" : "field", "name" : "SC4", "number" : "11", "params" : [ { "name" : "RMS/peak" }, { "name" : "Attack time (ms)" }, { "name" : "Release time (ms)" }, { "name" : "Threshold level (dB)" }, { "name" : "Ratio (1:n)" }, { "name" : "Knee radius (dB)" }, { "name" : "Makeup gain (dB)" }, { "name" : "Amplitude (dB)" }, { "name" : "Gain reduction (dB)" } ] }, { "code" : "el:amp_mono", "count" : 1, "display" : "field", "name" : "Mono Amplifier", "number" : "12", "params" : [ { "name" : "Gain" } ] }, { "code" : "el:amp_stereo", "count" : 1, "display" : "field", "name" : "Stereo Amplifier", "number" : "13", "params" : [ { "name" : "Gain" } ] }, { "code" : "el:lcrDelay", "count" : 11, "display" : "field", "name" : "L/C/R Delay", "number" : "14", "params" : [ { "name" : "L delay (ms)" }, { "name" : "L level" }, { "name" : "C delay (ms)" }, { "name" : "C level" }, { "name" : "R delay (ms)" }, { "name" : "R level" }, { "name" : "Feedback" }, { "name" : "High damp (%)" }, { "name" : "Low damp (%)" }, { "name" : "Spread" }, { "name" : "Dry/Wet level" } ] }, { "code" : "el:tap_sigmoid", "count" : 2, "display" : "field", "name" : "TAP Sigmoid Booster", "number" : "15", "params" : [ { "name" : "Pre Gain [dB]" }, { "name" : "Post Gain [dB]" } ] }, { "code" : "el:decay", "count" : 1, "display" : "field", "name" : "Exponential signal decay", "number" : "16", "params" : [ { "name" : "Decay Time (s)" } ] }, { "code" : "el:bwxover_iir", "count" : 2, "display" : "field", "name" : "Glame Butterworth X-over Filter", "number" : "17", "params" : [ { "name" : "Cutoff Frequency (Hz)" }, { "name" : "Resonance" } ] }, { "code" : "el:buttlow_iir", "count" : 2, "display" : "field", "name" : "GLAME Butterworth Lowpass", "number" : "18", "params" : [ { "name" : "Cutoff Frequency (Hz)" }, { "name" : "Resonance" } ] }, { "code" : "el:butthigh_iir", "count" : 2, "display" : "field", "name" : "GLAME Butterworth Highpass", "number" : "19", "params" : [ { "name" : "Cutoff Frequency (Hz)" }, { "name" : "Resonance" } ] }, { "code" : "el:foverdrive", "count" : 1, "display" : "field", "name" : "Fast overdrive", "number" : "20", "params" : [ { "name" : "Drive level" } ] }, { "code" : "el:delayorama", "count" : 11, "display" : "field", "name" : "Delayorama", "number" : "21", "params" : [ { "name" : "Random seed" }, { "name" : "Input gain (dB)" }, { "name" : "Feedback (%)" }, { "name" : "Number of taps" }, { "name" : "First delay (s)" }, { "name" : "Delay range (s)" }, { "name" : "Delay change" }, { "name" : "Delay random (%)" }, { "name" : "Amplitude change" }, { "name" : "Amplitude random (%)" }, { "name" : "Dry/wet mix" } ] }, { "code" : "el:comb_n", "count" : 3, "display" : "field", "name" : "Comb delay line, noninterpolating", "number" : "22", "params" : [ { "name" : "Max Delay (s)" }, { "name" : "Delay Time (s)" }, { "name" : "Decay Time (s)" } ] }, { "code" : "el:comb_l", "count" : 3, "display" : "field", "name" : "Comb delay line, linear interpolation", "number" : "23", "params" : [ { "name" : "Max Delay (s)" }, { "name" : "Delay Time (s)" }, { "name" : "Decay Time (s)" } ] }, { "code" : "el:comb_c", "count" : 3, "display" : "field", "name" : "Comb delay line, cubic spline interpolation", "number" : "24", "params" : [ { "name" : "Max Delay (s)" }, { "name" : "Delay Time (s)" }, { "name" : "Decay Time (s)" } ] }, { "code" : "el:sc3", "count" : 7, "display" : "field", "name" : "SC3", "number" : "25", "params" : [ { "name" : "Attack time (ms)" }, { "name" : "Release time (ms)" }, { "name" : "Threshold level (dB)" }, { "name" : "Ratio (1:n)" }, { "name" : "Knee radius (dB)" }, { "name" : "Makeup gain (dB)" }, { "name" : "Chain balance" } ] }, { "code" : "el:hermesFilter", "count" : 52, "display" : "field", "name" : "Hermes Filter", "number" : "26", "params" : [ { "name" : "LFO1 freq (Hz)" }, { "name" : "LFO1 wave (0 = sin, 1 = tri, 2 = saw, 3 = squ, 4 = s&h)" }, { "name" : "LFO2 freq (Hz)" }, { "name" : "LFO2 wave (0 = sin, 1 = tri, 2 = saw, 3 = squ, 4 = s&h)" }, { "name" : "Osc1 freq (Hz)" }, { "name" : "Osc1 wave (0 = sin, 1 = tri, 2 = saw, 3 = squ, 4 = noise)" }, { "name" : "Osc2 freq (Hz)" }, { "name" : "Osc2 wave (0 = sin, 1 = tri, 2 = saw, 3 = squ, 4 = noise)" }, { "name" : "Ringmod 1 depth (0=none, 1=AM, 2=RM)" }, { "name" : "Ringmod 2 depth (0=none, 1=AM, 2=RM)" }, { "name" : "Ringmod 3 depth (0=none, 1=AM, 2=RM)" }, { "name" : "Osc1 gain (dB)" }, { "name" : "RM1 gain (dB)" }, { "name" : "Osc2 gain (dB)" }, { "name" : "RM2 gain (dB)" }, { "name" : "Input gain (dB)" }, { "name" : "RM3 gain (dB)" }, { "name" : "Xover lower freq" }, { "name" : "Xover upper freq" }, { "name" : "Dist1 drive" }, { "name" : "Dist2 drive" }, { "name" : "Dist3 drive" }, { "name" : "Filt1 type (0=none, 1=LP, 2=HP, 3=BP, 4=BR, 5=AP)" }, { "name" : "Filt1 freq" }, { "name" : "Filt1 q" }, { "name" : "Filt1 resonance" }, { "name" : "Filt1 LFO1 level" }, { "name" : "Filt1 LFO2 level" }, { "name" : "Filt2 type (0=none, 1=LP, 2=HP, 3=BP, 4=BR, 5=AP)" }, { "name" : "Filt2 freq" }, { "name" : "Filt2 q" }, { "name" : "Filt2 resonance" }, { "name" : "Filt2 LFO1 level" }, { "name" : "Filt2 LFO2 level" }, { "name" : "Filt3 type (0=none, 1=LP, 2=HP, 3=BP, 4=BR, 5=AP)" }, { "name" : "Filt3 freq" }, { "name" : "Filt3 q" }, { "name" : "Filt3 resonance" }, { "name" : "Filt3 LFO1 level" }, { "name" : "Filt3 LFO2 level" }, { "name" : "Delay1 length (s)" }, { "name" : "Delay1 feedback" }, { "name" : "Delay1 wetness" }, { "name" : "Delay2 length (s)" }, { "name" : "Delay2 feedback" }, { "name" : "Delay2 wetness" }, { "name" : "Delay3 length (s)" }, { "name" : "Delay3 feedback" }, { "name" : "Delay3 wetness" }, { "name" : "Band 1 gain (dB)" }, { "name" : "Band 2 gain (dB)" }, { "name" : "Band 3 gain (dB)" } ] }, { "code" : "el:bodeShifter", "count" : 2, "display" : "field", "name" : "Bode frequency shifter", "number" : "27", "params" : [ { "name" : "Frequency shift" }, { "name" : "latency" } ] }, { "code" : "el:sine_faaa", "count" : 0, "display" : "field", "name" : "Sine Oscillator (Freq:audio, Amp:audio)", "number" : "28", "params" : [] }, { "code" : "el:sine_faac", "count" : 1, "display" : "field", "name" : "Sine Oscillator (Freq:audio, Amp:control)", "number" : "29", "params" : [ { "name" : "Amplitude" } ] }, { "code" : "el:sine_fcaa", "count" : 1, "display" : "field", "name" : "Sine Oscillator (Freq:control, Amp:audio)", "number" : "30", "params" : [ { "name" : "Frequency (Hz)" } ] }, { "code" : "el:sine_fcac", "count" : 2, "display" : "field", "name" : "Sine Oscillator (Freq:control, Amp:control)", "number" : "31", "params" : [ { "name" : "Frequency (Hz)" }, { "name" : "Amplitude" } ] }, { "code" : "el:tap_limiter", "count" : 3, "display" : "field", "name" : "TAP Scaling Limiter", "number" : "32", "params" : [ { "name" : "Limit Level [dB]" }, { "name" : "Output Volume [dB]" }, { "name" : "latency" } ] }, { "code" : "el:tap_rotspeak", "count" : 5, "display" : "field", "name" : "TAP Rotary Speaker", "number" : "33", "params" : [ { "name" : "Rotor Frequency [Hz]" }, { "name" : "Horn Frequency [Hz]" }, { "name" : "Mic Distance [%]" }, { "name" : "Rotor/Horn Mix" }, { "name" : "latency" } ] }, { "code" : "el:zm1", "count" : 0, "display" : "field", "name" : "z-1", "number" : "34", "params" : [] }, { "code" : "el:multivoiceChorus", "count" : 6, "display" : "field", "name" : "Multivoice Chorus", "number" : "35", "params" : [ { "name" : "Number of voices" }, { "name" : "Delay base (ms)" }, { "name" : "Voice separation (ms)" }, { "name" : "Detune (%)" }, { "name" : "LFO frequency (Hz)" }, { "name" : "Output attenuation (dB)" } ] }, { "code" : "el:fmOsc", "count" : 1, "display" : "field", "name" : "FM Oscillator", "number" : "36", "params" : [ { "name" : "Waveform (1=sin, 2=tri, 3=squ, 4=saw)" } ] }, { "code" : "el:sc4m", "count" : 9, "display" : "field", "name" : "SC4 mono", "number" : "37", "params" : [ { "name" : "RMS/peak" }, { "name" : "Attack time (ms)" }, { "name" : "Release time (ms)" }, { "name" : "Threshold level (dB)" }, { "name" : "Ratio (1:n)" }, { "name" : "Knee radius (dB)" }, { "name" : "Makeup gain (dB)" }, { "name" : "Amplitude (dB)" }, { "name" : "Gain reduction (dB)" } ] }, { "code" : "el:gongBeater", "count" : 3, "display" : "field", "name" : "Gong beater", "number" : "38", "params" : [ { "name" : "Impulse gain (dB)" }, { "name" : "Strike gain (dB)" }, { "name" : "Strike duration (s)" } ] }, { "code" : "el:delay_n", "count" : 2, "display" : "field", "name" : "Simple delay line, noninterpolating", "number" : "39", "params" : [ { "name" : "Max Delay (s)" }, { "name" : "Delay Time (s)" } ] }, { "code" : "el:delay_l", "count" : 2, "display" : "field", "name" : "Simple delay line, linear interpolation", "number" : "40", "params" : [ { "name" : "Max Delay (s)" }, { "name" : "Delay Time (s)" } ] }, { "code" : "el:delay_c", "count" : 2, "display" : "field", "name" : "Simple delay line, cubic spline interpolation", "number" : "41", "params" : [ { "name" : "Max Delay (s)" }, { "name" : "Delay Time (s)" } ] }, { "code" : "el:tap_vibrato", "count" : 5, "display" : "field", "name" : "TAP Vibrato", "number" : "42", "params" : [ { "name" : "Frequency [Hz]" }, { "name" : "Depth [%]" }, { "name" : "Dry Level [dB]" }, { "name" : "Wet Level [dB]" }, { "name" : "latency" } ] }, { "code" : "el:fastLookaheadLimiter", "count" : 5, "display" : "field", "name" : "Fast Lookahead limiter", "number" : "43", "params" : [ { "name" : "Input gain (dB)" }, { "name" : "Limit (dB)" }, { "name" : "Release time (s)" }, { "name" : "Attenuation (dB)" }, { "name" : "latency" } ] }, { "code" : "el:impulse_fc", "count" : 1, "display" : "field", "name" : "Nonbandlimited single-sample impulses (Frequency: Control)", "number" : "44", "params" : [ { "name" : "Frequency (Hz)" } ] }, { "code" : "el:lowpass_iir", "count" : 2, "display" : "field", "name" : "Glame Lowpass Filter", "number" : "45", "params" : [ { "name" : "Cutoff Frequency" }, { "name" : "Stages(2 poles per stage)" } ] }, { "code" : "el:tap_pitch", "count" : 5, "display" : "field", "name" : "TAP Pitch Shifter", "number" : "46", "params" : [ { "name" : "Semitone Shift" }, { "name" : "Rate Shift [%]" }, { "name" : "Dry Level [dB]" }, { "name" : "Wet Level [dB]" }, { "name" : "latency" } ] }, { "code" : "el:NoiseGate", "count" : 4, "display" : "field", "name" : "C* NoiseGate - Attenuate hum and noise", "number" : "47", "params" : [ { "name" : "open (dB)" }, { "name" : "attack (ms)" }, { "name" : "close (dB)" }, { "name" : "mains (Hz)" } ] }, { "code" : "el:Compress", "count" : 7, "display" : "field", "name" : "C* Compress - Compressor and saturating limiter", "number" : "48", "params" : [ { "name" : "measure" }, { "name" : "mode" }, { "name" : "threshold" }, { "name" : "strength" }, { "name" : "attack" }, { "name" : "release" }, { "name" : "gain (dB)" } ] }, { "code" : "el:CompressX2", "count" : 7, "display" : "field", "name" : "C* CompressX2 - Stereo compressor and saturating limiter", "number" : "49", "params" : [ { "name" : "measure" }, { "name" : "mode" }, { "name" : "threshold" }, { "name" : "strength" }, { "name" : "attack" }, { "name" : "release" }, { "name" : "gain (dB)" } ] }, { "code" : "el:ToneStack", "count" : 4, "display" : "field", "name" : "C* ToneStack - Classic amplifier tone stack emulation", "number" : "50", "params" : [ { "name" : "model" }, { "name" : "bass" }, { "name" : "mid" }, { "name" : "treble" } ] }, { "code" : "el:AmpVTS", "count" : 11, "display" : "field", "name" : "C* AmpVTS - Idealised guitar amplification", "number" : "51", "params" : [ { "name" : "over" }, { "name" : "gain" }, { "name" : "bright" }, { "name" : "power" }, { "name" : "tonestack" }, { "name" : "bass" }, { "name" : "mid" }, { "name" : "treble" }, { "name" : "attack" }, { "name" : "squash" }, { "name" : "lowcut" } ] }, { "code" : "el:CabinetIV", "count" : 2, "display" : "field", "name" : "C* CabinetIV - Idealised loudspeaker cabinet", "number" : "52", "params" : [ { "name" : "model" }, { "name" : "gain (dB)" } ] }, { "code" : "el:Plate", "count" : 4, "display" : "field", "name" : "C* Plate - Versatile plate reverb", "number" : "53", "params" : [ { "name" : "bandwidth" }, { "name" : "tail" }, { "name" : "damping" }, { "name" : "blend" } ] }, { "code" : "el:PlateX2", "count" : 4, "display" : "field", "name" : "C* PlateX2 - Versatile plate reverb, stereo inputs", "number" : "54", "params" : [ { "name" : "bandwidth" }, { "name" : "tail" }, { "name" : "damping" }, { "name" : "blend" } ] }, { "code" : "el:Saturate", "count" : 3, "display" : "field", "name" : "C* Saturate - Various overdrive models, 8x oversampled", "number" : "55", "params" : [ { "name" : "mode" }, { "name" : "gain (dB)" }, { "name" : "bias" } ] }, { "code" : "el:Spice", "count" : 5, "display" : "field", "name" : "C* Spice - Not an exciter", "number" : "56", "params" : [ { "name" : "lo.f (Hz)" }, { "name" : "lo.compress" }, { "name" : "lo.gain" }, { "name" : "hi.f (Hz)" }, { "name" : "hi.gain" } ] }, { "code" : "el:SpiceX2", "count" : 5, "display" : "field", "name" : "C* SpiceX2 - Not an exciter either", "number" : "57", "params" : [ { "name" : "lo.f (Hz)" }, { "name" : "lo.compress" }, { "name" : "lo.gain" }, { "name" : "hi.f (Hz)" }, { "name" : "hi.gain" } ] }, { "code" : "el:ChorusI", "count" : 6, "display" : "field", "name" : "C* ChorusI - Mono chorus/flanger", "number" : "58", "params" : [ { "name" : "t (ms)" }, { "name" : "width (ms)" }, { "name" : "rate (Hz)" }, { "name" : "blend" }, { "name" : "feedforward" }, { "name" : "feedback" } ] }, { "code" : "el:PhaserII", "count" : 5, "display" : "field", "name" : "C* PhaserII - Mono phaser", "number" : "59", "params" : [ { "name" : "rate" }, { "name" : "lfo" }, { "name" : "depth" }, { "name" : "spread" }, { "name" : "resonance" } ] }, { "code" : "el:AutoFilter", "count" : 9, "display" : "field", "name" : "C* AutoFilter - Modulated filter cascade", "number" : "60", "params" : [ { "name" : "over" }, { "name" : "mode" }, { "name" : "filter" }, { "name" : "gain (dB)" }, { "name" : "f (Hz)" }, { "name" : "Q" }, { "name" : "range" }, { "name" : "lfo/env" }, { "name" : "rate" } ] }, { "code" : "el:Scape", "count" : 6, "display" : "field", "name" : "C* Scape - Stereo delay with chromatic resonances", "number" : "61", "params" : [ { "name" : "bpm" }, { "name" : "divider" }, { "name" : "feedback" }, { "name" : "dry" }, { "name" : "blend" }, { "name" : "tune (Hz)" } ] }, { "code" : "el:Eq10", "count" : 10, "display" : "field", "name" : "C* Eq10 - 10-band equaliser", "number" : "62", "params" : [ { "name" : "31 Hz" }, { "name" : "63 Hz" }, { "name" : "125 Hz" }, { "name" : "250 Hz" }, { "name" : "500 Hz" }, { "name" : "1 kHz" }, { "name" : "2 kHz" }, { "name" : "4 kHz" }, { "name" : "8 kHz" }, { "name" : "16 kHz" } ] }, { "code" : "el:Eq10X2", "count" : 10, "display" : "field", "name" : "C* Eq10X2 - Stereo 10-band equaliser", "number" : "63", "params" : [ { "name" : "31 Hz" }, { "name" : "63 Hz" }, { "name" : "125 Hz" }, { "name" : "250 Hz" }, { "name" : "500 Hz" }, { "name" : "1 kHz" }, { "name" : "2 kHz" }, { "name" : "4 kHz" }, { "name" : "8 kHz" }, { "name" : "16 kHz" } ] }, { "code" : "el:Eq4p", "count" : 16, "display" : "field", "name" : "C* Eq4p - 4-band parametric equaliser", "number" : "64", "params" : [ { "name" : "a.mode" }, { "name" : "a.f (Hz)" }, { "name" : "a.Q" }, { "name" : "a.gain (dB)" }, { "name" : "b.mode" }, { "name" : "b.f (Hz)" }, { "name" : "b.Q" }, { "name" : "b.gain (dB)" }, { "name" : "c.mode" }, { "name" : "c.f (Hz)" }, { "name" : "c.Q" }, { "name" : "c.gain (dB)" }, { "name" : "d.mode" }, { "name" : "d.f (Hz)" }, { "name" : "d.Q" }, { "name" : "d.gain (dB)" } ] }, { "code" : "el:Wider", "count" : 2, "display" : "field", "name" : "C* Wider - Stereo image synthesis", "number" : "65", "params" : [ { "name" : "pan" }, { "name" : "width" } ] }, { "code" : "el:Narrower", "count" : 2, "display" : "field", "name" : "C* Narrower - Stereo image width reduction", "number" : "66", "params" : [ { "name" : "mode" }, { "name" : "strength" } ] }, { "code" : "el:Sin", "count" : 2, "display" : "field", "name" : "C* Sin - Sine wave generator", "number" : "67", "params" : [ { "name" : "f (Hz)" }, { "name" : "volume" } ] }, { "code" : "el:White", "count" : 1, "display" : "field", "name" : "C* White - Noise generator", "number" : "68", "params" : [ { "name" : "volume" } ] }, { "code" : "el:Fractal", "count" : 7, "display" : "field", "name" : "C* Fractal - Audio stream from deterministic chaos", "number" : "69", "params" : [ { "name" : "rate" }, { "name" : "mode" }, { "name" : "x" }, { "name" : "y" }, { "name" : "z" }, { "name" : "hp" }, { "name" : "volume" } ] }, { "code" : "el:Click", "count" : 4, "display" : "field", "name" : "C* Click - Metronome", "number" : "70", "params" : [ { "name" : "model" }, { "name" : "bpm" }, { "name" : "volume" }, { "name" : "damping" } ] }, { "code" : "el:CEO", "count" : 3, "display" : "field", "name" : "C* CEO - Chief Executive Oscillator", "number" : "71", "params" : [ { "name" : "ppm" }, { "name" : "volume" }, { "name" : "damping" } ] }, { "code" : "el:transient", "count" : 2, "display" : "field", "name" : "Transient mangler", "number" : "72", "params" : [ { "name" : "Attack speed" }, { "name" : "Sustain time" } ] }, { "code" : "el:waveTerrain", "count" : 0, "display" : "field", "name" : "Wave Terrain Oscillator", "number" : "73", "params" : [] }, { "code" : "el:fadDelay", "count" : 2, "display" : "field", "name" : "Fractionally Addressed Delay Line", "number" : "74", "params" : [ { "name" : "Delay (seconds)" }, { "name" : "Feedback (dB)" } ] }, { "code" : "el:tap_stereo_echo", "count" : 10, "display" : "field", "name" : "TAP Stereo Echo", "number" : "75", "params" : [ { "name" : "L Delay [ms]" }, { "name" : "L Feedback [%]" }, { "name" : "R/Haas Delay [ms]" }, { "name" : "R/Haas Feedback [%]" }, { "name" : "L Echo Level [dB]" }, { "name" : "R Echo Level [dB]" }, { "name" : "Dry Level [dB]" }, { "name" : "Cross Mode" }, { "name" : "Haas Effect" }, { "name" : "Swap Outputs" } ] }, { "code" : "el:smoothDecimate", "count" : 2, "display" : "field", "name" : "Smooth Decimator", "number" : "76", "params" : [ { "name" : "Resample rate" }, { "name" : "Smoothing" } ] }, { "code" : "el:tap_chorusflanger", "count" : 7, "display" : "field", "name" : "TAP Chorus/Flanger", "number" : "77", "params" : [ { "name" : "Frequency [Hz]" }, { "name" : "L/R Phase Shift [deg]" }, { "name" : "Depth [%]" }, { "name" : "Delay [ms]" }, { "name" : "Contour [Hz]" }, { "name" : "Dry Level [dB]" }, { "name" : "Wet Level [dB]" } ] }, { "code" : "el:vynil", "count" : 5, "display" : "field", "name" : "VyNil (Vinyl Effect)", "number" : "78", "params" : [ { "name" : "Year" }, { "name" : "RPM" }, { "name" : "Surface warping" }, { "name" : "Crackle" }, { "name" : "Wear" } ] }, { "code" : "el:retroFlange", "count" : 2, "display" : "field", "name" : "Retro Flanger", "number" : "79", "params" : [ { "name" : "Average stall (ms)" }, { "name" : "Flange frequency (Hz)" } ] }, { "code" : "el:harmonicGen", "count" : 10, "display" : "field", "name" : "Harmonic generator", "number" : "80", "params" : [ { "name" : "Fundamental magnitude" }, { "name" : "2nd harmonic magnitude" }, { "name" : "3rd harmonic magnitude" }, { "name" : "4th harmonic magnitude" }, { "name" : "5th harmonic magnitude" }, { "name" : "6th harmonic magnitude" }, { "name" : "7th harmonic magnitude" }, { "name" : "8th harmonic magnitude" }, { "name" : "9th harmonic magnitude" }, { "name" : "10th harmonic magnitude" } ] }, { "code" : "el:Parametric1", "count" : 18, "display" : "field", "name" : "4-band parametric filter", "number" : "81", "params" : [ { "name" : "Filter" }, { "name" : "Gain" }, { "name" : "Section 1" }, { "name" : "Frequency 1" }, { "name" : "Bandwidth 1" }, { "name" : "Gain 1" }, { "name" : "Section 2" }, { "name" : "Frequency 2" }, { "name" : "Bandwidth 2" }, { "name" : "Gain 2" }, { "name" : "Section 3" }, { "name" : "Frequency 3" }, { "name" : "Bandwidth 3" }, { "name" : "Gain 3" }, { "name" : "Section 4" }, { "name" : "Frequency 4" }, { "name" : "Bandwidth 4" }, { "name" : "Gain 4" } ] }, { "code" : "el:tap_equalizer", "count" : 16, "display" : "field", "name" : "TAP Equalizer", "number" : "82", "params" : [ { "name" : "Band 1 Gain [dB]" }, { "name" : "Band 2 Gain [dB]" }, { "name" : "Band 3 Gain [dB]" }, { "name" : "Band 4 Gain [dB]" }, { "name" : "Band 5 Gain [dB]" }, { "name" : "Band 6 Gain [dB]" }, { "name" : "Band 7 Gain [dB]" }, { "name" : "Band 8 Gain [dB]" }, { "name" : "Band 1 Freq [Hz]" }, { "name" : "Band 2 Freq [Hz]" }, { "name" : "Band 3 Freq [Hz]" }, { "name" : "Band 4 Freq [Hz]" }, { "name" : "Band 5 Freq [Hz]" }, { "name" : "Band 6 Freq [Hz]" }, { "name" : "Band 7 Freq [Hz]" }, { "name" : "Band 8 Freq [Hz]" } ] }, { "code" : "el:matrixMSSt", "count" : 1, "display" : "field", "name" : "Matrix: MS to Stereo", "number" : "83", "params" : [ { "name" : "Width" } ] }, { "code" : "el:invada_mono_tube_module_0_1", "count" : 4, "display" : "field", "name" : ":: Invada :: Tube - Mono", "number" : "84", "params" : [ { "name" : "Drive (dB)" }, { "name" : "DC Offset" }, { "name" : "Phase" }, { "name" : "Wet/Dry Mix (%)" } ] }, { "code" : "el:invada_stereo_tube_module_0_1", "count" : 4, "display" : "field", "name" : ":: Invada :: Tube - Stereo", "number" : "85", "params" : [ { "name" : "Drive (dB)" }, { "name" : "DC Offset" }, { "name" : "Phase" }, { "name" : "Wet/Dry Mix (%)" } ] }, { "code" : "el:declip", "count" : 0, "display" : "field", "name" : "Declipper", "number" : "86", "params" : [] }, { "code" : "el:bandpass_iir", "count" : 3, "display" : "field", "name" : "Glame Bandpass Filter", "number" : "87", "params" : [ { "name" : "Center Frequency (Hz)" }, { "name" : "Bandwidth (Hz)" }, { "name" : "Stages(2 poles per stage)" } ] }, { "code" : "el:artificialLatency", "count" : 2, "display" : "field", "name" : "Artificial latency", "number" : "88", "params" : [ { "name" : "Delay (ms)" }, { "name" : "latency" } ] }, { "code" : "el:satanMaximiser", "count" : 2, "display" : "field", "name" : "Barry's Satan Maximiser", "number" : "89", "params" : [ { "name" : "Decay time (samples)" }, { "name" : "Knee point (dB)" } ] }, { "code" : "el:singlePara", "count" : 3, "display" : "field", "name" : "Single band parametric", "number" : "90", "params" : [ { "name" : "Gain (dB)" }, { "name" : "Frequency (Hz)" }, { "name" : "Bandwidth (octaves)" } ] }, { "code" : "el:decimator", "count" : 2, "display" : "field", "name" : "Decimator", "number" : "91", "params" : [ { "name" : "Bit depth" }, { "name" : "Sample rate (Hz)" } ] }, { "code" : "el:mbeq", "count" : 16, "display" : "field", "name" : "Multiband EQ", "number" : "92", "params" : [ { "name" : "50Hz gain (low shelving)" }, { "name" : "100Hz gain" }, { "name" : "156Hz gain" }, { "name" : "220Hz gain" }, { "name" : "311Hz gain" }, { "name" : "440Hz gain" }, { "name" : "622Hz gain" }, { "name" : "880Hz gain" }, { "name" : "1250Hz gain" }, { "name" : "1750Hz gain" }, { "name" : "2500Hz gain" }, { "name" : "3500Hz gain" }, { "name" : "5000Hz gain" }, { "name" : "10000Hz gain" }, { "name" : "20000Hz gain" }, { "name" : "latency" } ] }, { "code" : "el:svf", "count" : 4, "display" : "field", "name" : "State Variable Filter", "number" : "93", "params" : [ { "name" : "Filter type (0=none, 1=LP, 2=HP, 3=BP, 4=BR, 5=AP)" }, { "name" : "Filter freq" }, { "name" : "Filter Q" }, { "name" : "Filter resonance" } ] }, { "code" : "el:giantFlange", "count" : 7, "display" : "field", "name" : "Giant flange", "number" : "94", "params" : [ { "name" : "Double delay" }, { "name" : "LFO frequency 1 (Hz)" }, { "name" : "Delay 1 range (s)" }, { "name" : "LFO frequency 2 (Hz)" }, { "name" : "Delay 2 range (s)" }, { "name" : "Feedback" }, { "name" : "Dry/Wet level" } ] }, { "code" : "el:Ambisonics-21-panner", "count" : 2, "display" : "field", "name" : "AMB order 2,1 panner", "number" : "95", "params" : [ { "name" : "Elevation" }, { "name" : "Azimuth" } ] }, { "code" : "el:Ambisonics-21-rotator", "count" : 1, "display" : "field", "name" : "AMB order 2,1 rotator", "number" : "96", "params" : [ { "name" : "Angle" } ] }, { "code" : "el:Ambisonics-22-panner", "count" : 2, "display" : "field", "name" : "AMB order 2,2 panner", "number" : "97", "params" : [ { "name" : "Elevation" }, { "name" : "Azimuth" } ] }, { "code" : "el:Ambisonics-22-rotator", "count" : 1, "display" : "field", "name" : "AMB order 2,2 rotator", "number" : "98", "params" : [ { "name" : "Angle" } ] }, { "code" : "el:delay_5s", "count" : 2, "display" : "field", "name" : "Simple Delay Line", "number" : "99", "params" : [ { "name" : "Delay (Seconds)" }, { "name" : "Dry/Wet Balance" } ] }, { "code" : "el:rateShifter", "count" : 1, "display" : "field", "name" : "Rate shifter", "number" : "100", "params" : [ { "name" : "Rate" } ] }, { "code" : "el:invada_stereo_input_module_0_1", "count" : 6, "display" : "field", "name" : ":: Invada :: Input Module", "number" : "101", "params" : [ { "name" : "Phase Reverse (Left)" }, { "name" : "Phase Reverse (Right)" }, { "name" : "Gain (dB)" }, { "name" : "Pan (L-R)" }, { "name" : "Width (M-S)" }, { "name" : "Soft Clip" } ] }, { "code" : "el:amp", "count" : 1, "display" : "field", "name" : "Simple amplifier", "number" : "102", "params" : [ { "name" : "Amps gain (dB)" } ] }, { "code" : "el:const", "count" : 1, "display" : "field", "name" : "Constant Signal Generator", "number" : "103", "params" : [ { "name" : "Signal amplitude" } ] }, { "code" : "el:matrixStMS", "count" : 0, "display" : "field", "name" : "Matrix: Stereo to MS", "number" : "104", "params" : [] }, { "code" : "el:lfoPhaser", "count" : 4, "display" : "field", "name" : "LFO Phaser", "number" : "105", "params" : [ { "name" : "LFO rate (Hz)" }, { "name" : "LFO depth" }, { "name" : "Feedback" }, { "name" : "Spread (octaves)" } ] }, { "code" : "el:fourByFourPole", "count" : 8, "display" : "field", "name" : "4 x 4 pole allpass", "number" : "106", "params" : [ { "name" : "Frequency 1" }, { "name" : "Feedback 1" }, { "name" : "Frequency 2" }, { "name" : "Feedback 2" }, { "name" : "Frequency 3" }, { "name" : "Feedback 3" }, { "name" : "Frequency 4" }, { "name" : "Feedback 4" } ] }, { "code" : "el:autoPhaser", "count" : 5, "display" : "field", "name" : "Auto phaser", "number" : "107", "params" : [ { "name" : "Attack time (s)" }, { "name" : "Decay time (s)" }, { "name" : "Modulation depth" }, { "name" : "Feedback" }, { "name" : "Spread (octaves)" } ] }, { "code" : "el:se4", "count" : 9, "display" : "field", "name" : "SE4", "number" : "108", "params" : [ { "name" : "RMS/peak" }, { "name" : "Attack time (ms)" }, { "name" : "Release time (ms)" }, { "name" : "Threshold level (dB)" }, { "name" : "Ratio (1:n)" }, { "name" : "Knee radius (dB)" }, { "name" : "Attenuation (dB)" }, { "name" : "Amplitude (dB)" }, { "name" : "Gain expansion (dB)" } ] }, { "code" : "el:amPitchshift", "count" : 3, "display" : "field", "name" : "AM pitchshifter", "number" : "109", "params" : [ { "name" : "Pitch shift" }, { "name" : "Buffer size" }, { "name" : "latency" } ] }, { "code" : "el:valveRect", "count" : 2, "display" : "field", "name" : "Valve rectifier", "number" : "110", "params" : [ { "name" : "Sag level" }, { "name" : "Distortion" } ] }, { "code" : "el:tap_doubler", "count" : 8, "display" : "field", "name" : "TAP Fractal Doubler", "number" : "111", "params" : [ { "name" : "Time Tracking" }, { "name" : "Pitch Tracking" }, { "name" : "Dry Level [dB]" }, { "name" : "Dry Left Position" }, { "name" : "Dry Right Position" }, { "name" : "Wet Level [dB]" }, { "name" : "Wet Left Position" }, { "name" : "Wet Right Position" } ] }, { "code" : "el:gong", "count" : 27, "display" : "field", "name" : "Gong model", "number" : "112", "params" : [ { "name" : "Inner damping" }, { "name" : "Outer damping" }, { "name" : "Mic position" }, { "name" : "Inner size 1" }, { "name" : "Inner stiffness 1 +" }, { "name" : "Inner stiffness 1 -" }, { "name" : "Inner size 2" }, { "name" : "Inner stiffness 2 +" }, { "name" : "Inner stiffness 2 -" }, { "name" : "Inner size 3" }, { "name" : "Inner stiffness 3 +" }, { "name" : "Inner stiffness 3 -" }, { "name" : "Inner size 4" }, { "name" : "Inner stiffness 4 +" }, { "name" : "Inner stiffness 4 -" }, { "name" : "Outer size 1" }, { "name" : "Outer stiffness 1 +" }, { "name" : "Outer stiffness 1 -" }, { "name" : "Outer size 2" }, { "name" : "Outer stiffness 2 +" }, { "name" : "Outer stiffness 2 -" }, { "name" : "Outer size 3" }, { "name" : "Outer stiffness 3 +" }, { "name" : "Outer stiffness 3 -" }, { "name" : "Outer size 4" }, { "name" : "Outer stiffness 4 +" }, { "name" : "Outer stiffness 4 -" } ] }, { "code" : "el:invada_mono_compressor_module_0_1", "count" : 8, "display" : "field", "name" : ":: Invada :: Compressor - Mono", "number" : "113", "params" : [ { "name" : "Tight / Sloppy" }, { "name" : "Attack (ms)" }, { "name" : "Release (ms)" }, { "name" : "Threshold (dB)" }, { "name" : "Ratio" }, { "name" : "Gain (dB)" }, { "name" : "Soft Clip" }, { "name" : "Gain Reduction" } ] }, { "code" : "el:invada_stereo_compressor_module_0_1", "count" : 8, "display" : "field", "name" : ":: Invada :: Compressor - Stereo", "number" : "114", "params" : [ { "name" : "Tight / Sloppy" }, { "name" : "Attack (ms)" }, { "name" : "Release (ms)" }, { "name" : "Threshold (dB)" }, { "name" : "Ratio" }, { "name" : "Gain (dB)" }, { "name" : "Soft Clip" }, { "name" : "Gain Reduction" } ] }, { "code" : "el:gate", "count" : 8, "display" : "field", "name" : "Gate", "number" : "115", "params" : [ { "name" : "LF key filter (Hz)" }, { "name" : "HF key filter (Hz)" }, { "name" : "Threshold (dB)" }, { "name" : "Attack (ms)" }, { "name" : "Hold (ms)" }, { "name" : "Decay (ms)" }, { "name" : "Range (dB)" }, { "name" : "Output select (-1 = key listen, 0 = gate, 1 = bypass)" } ] }, { "code" : "el:stepMuxer", "count" : 1, "display" : "field", "name" : "Step Demuxer", "number" : "116", "params" : [ { "name" : "Crossfade time (in ms)" } ] }, { "code" : "el:dj_eq_mono", "count" : 4, "display" : "field", "name" : "DJ EQ (mono)", "number" : "117", "params" : [ { "name" : "Lo gain (dB)" }, { "name" : "Mid gain (dB)" }, { "name" : "Hi gain (dB)" }, { "name" : "latency" } ] }, { "code" : "el:dj_eq", "count" : 4, "display" : "field", "name" : "DJ EQ", "number" : "118", "params" : [ { "name" : "Lo gain (dB)" }, { "name" : "Mid gain (dB)" }, { "name" : "Hi gain (dB)" }, { "name" : "latency" } ] }, { "code" : "el:djFlanger", "count" : 4, "display" : "field", "name" : "DJ flanger", "number" : "119", "params" : [ { "name" : "LFO sync" }, { "name" : "LFO period (s)" }, { "name" : "LFO depth (ms)" }, { "name" : "Feedback (%)" } ] }, { "code" : "el:dysonCompress", "count" : 4, "display" : "field", "name" : "Dyson compressor", "number" : "120", "params" : [ { "name" : "Peak limit (dB)" }, { "name" : "Release time (s)" }, { "name" : "Fast compression ratio" }, { "name" : "Compression ratio" } ] }, { "code" : "el:sinusWavewrapper", "count" : 1, "display" : "field", "name" : "Sinus wavewrapper", "number" : "121", "params" : [ { "name" : "Wrap degree" } ] }, { "code" : "el:alias", "count" : 1, "display" : "field", "name" : "Aliasing", "number" : "122", "params" : [ { "name" : "Aliasing level" } ] }, { "code" : "el:gverb", "count" : 7, "display" : "field", "name" : "GVerb", "number" : "123", "params" : [ { "name" : "Roomsize (m)" }, { "name" : "Reverb time (s)" }, { "name" : "Damping" }, { "name" : "Input bandwidth" }, { "name" : "Dry signal level (dB)" }, { "name" : "Early reflection level (dB)" }, { "name" : "Tail level (dB)" } ] }, { "code" : "el:freqTracker", "count" : 1, "display" : "field", "name" : "Frequency tracker", "number" : "124", "params" : [ { "name" : "Tracking speed" } ] }, { "code" : "el:pointerCastDistortion", "count" : 2, "display" : "field", "name" : "Pointer cast distortion", "number" : "125", "params" : [ { "name" : "Effect cutoff freq (Hz)" }, { "name" : "Dry/wet mix" } ] }, { "code" : "el:tap_equalizer_bw", "count" : 24, "display" : "field", "name" : "TAP Equalizer/BW", "number" : "126", "params" : [ { "name" : "Band 1 Gain [dB]" }, { "name" : "Band 2 Gain [dB]" }, { "name" : "Band 3 Gain [dB]" }, { "name" : "Band 4 Gain [dB]" }, { "name" : "Band 5 Gain [dB]" }, { "name" : "Band 6 Gain [dB]" }, { "name" : "Band 7 Gain [dB]" }, { "name" : "Band 8 Gain [dB]" }, { "name" : "Band 1 Freq [Hz]" }, { "name" : "Band 2 Freq [Hz]" }, { "name" : "Band 3 Freq [Hz]" }, { "name" : "Band 4 Freq [Hz]" }, { "name" : "Band 5 Freq [Hz]" }, { "name" : "Band 6 Freq [Hz]" }, { "name" : "Band 7 Freq [Hz]" }, { "name" : "Band 8 Freq [Hz]" }, { "name" : "Band 1 Bandwidth [octaves]" }, { "name" : "Band 2 Bandwidth [octaves]" }, { "name" : "Band 3 Bandwidth [octaves]" }, { "name" : "Band 4 Bandwidth [octaves]" }, { "name" : "Band 5 Bandwidth [octaves]" }, { "name" : "Band 6 Bandwidth [octaves]" }, { "name" : "Band 7 Bandwidth [octaves]" }, { "name" : "Band 8 Bandwidth [octaves]" } ] }, { "code" : "el:gsm", "count" : 4, "display" : "field", "name" : "GSM simulator", "number" : "127", "params" : [ { "name" : "Dry/wet mix" }, { "name" : "Number of passes" }, { "name" : "Error rate (bits/block)" }, { "name" : "latency" } ] }, { "code" : "el:revdelay", "count" : 5, "display" : "field", "name" : "Reverse Delay (5s max)", "number" : "128", "params" : [ { "name" : "Delay Time (s)" }, { "name" : "Dry Level (dB)" }, { "name" : "Wet Level (dB)" }, { "name" : "Feedback" }, { "name" : "Crossfade samples" } ] }, { "code" : "el:karaoke", "count" : 1, "display" : "field", "name" : "Karaoke", "number" : "129", "params" : [ { "name" : "Vocal volume (dB)" } ] }, { "code" : "el:dcRemove", "count" : 0, "display" : "field", "name" : "DC Offset Remover", "number" : "130", "params" : [] }, { "code" : "el:Ambisonics-31-panner", "count" : 2, "display" : "field", "name" : "AMB order 3,1 panner", "number" : "131", "params" : [ { "name" : "Elevation" }, { "name" : "Azimuth" } ] }, { "code" : "el:Ambisonics-31-rotator", "count" : 1, "display" : "field", "name" : "AMB order 3,1 rotator", "number" : "132", "params" : [ { "name" : "Angle" } ] }, { "code" : "el:Ambisonics-33-panner", "count" : 2, "display" : "field", "name" : "AMB order 3,3 panner", "number" : "133", "params" : [ { "name" : "Elevation" }, { "name" : "Azimuth" } ] }, { "code" : "el:Ambisonics-33-rotator", "count" : 1, "display" : "field", "name" : "AMB order 3,3 rotator", "number" : "134", "params" : [ { "name" : "Angle" } ] }, { "code" : "el:sc1", "count" : 6, "display" : "field", "name" : "SC1", "number" : "135", "params" : [ { "name" : "Attack time (ms)" }, { "name" : "Release time (ms)" }, { "name" : "Threshold level (dB)" }, { "name" : "Ratio (1:n)" }, { "name" : "Knee radius (dB)" }, { "name" : "Makeup gain (dB)" } ] }, { "code" : "el:foldover", "count" : 2, "display" : "field", "name" : "Foldover distortion", "number" : "136", "params" : [ { "name" : "Drive" }, { "name" : "Skew" } ] }, { "code" : "el:hardLimiter", "count" : 3, "display" : "field", "name" : "Hard Limiter", "number" : "137", "params" : [ { "name" : "dB limit" }, { "name" : "Wet level" }, { "name" : "Residue level" } ] }, { "code" : "el:lsFilter", "count" : 3, "display" : "field", "name" : "LS Filter", "number" : "138", "params" : [ { "name" : "Filter type (0=LP, 1=BP, 2=HP)" }, { "name" : "Cutoff frequency (Hz)" }, { "name" : "Resonance" } ] }, { "code" : "el:matrixSpatialiser", "count" : 1, "display" : "field", "name" : "Matrix Spatialiser", "number" : "139", "params" : [ { "name" : "Width" } ] }, { "code" : "el:invada_mono_reverbER_module_0_1", "count" : 10, "display" : "field", "name" : ":: Invada :: ER Reverb - Mono In", "number" : "140", "params" : [ { "name" : "Room Length" }, { "name" : "Room Width" }, { "name" : "Room Height" }, { "name" : "Source (L/R)" }, { "name" : "Source (F/B)" }, { "name" : "Listener (L/R)" }, { "name" : "Listener (F/B)" }, { "name" : "HPF (Hz)" }, { "name" : "Warmth" }, { "name" : "Diffusion" } ] }, { "code" : "el:invada_sum_reverbER_module_0_1", "count" : 10, "display" : "field", "name" : ":: Invada :: ER Reverb - Sum L+R In", "number" : "141", "params" : [ { "name" : "Room Length" }, { "name" : "Room Width" }, { "name" : "Room Height" }, { "name" : "Source (L/R)" }, { "name" : "Source (F/B)" }, { "name" : "Listener (L/R)" }, { "name" : "Listener (F/B)" }, { "name" : "HPF (Hz)" }, { "name" : "Warmth" }, { "name" : "Diffusion" } ] }, { "code" : "el:imp", "count" : 4, "display" : "field", "name" : "Impulse convolver", "number" : "142", "params" : [ { "name" : "Impulse ID" }, { "name" : "High latency mode" }, { "name" : "Gain (dB)" }, { "name" : "latency" } ] }, { "code" : "el:highpass_iir", "count" : 2, "display" : "field", "name" : "Glame Highpass Filter", "number" : "143", "params" : [ { "name" : "Cutoff Frequency" }, { "name" : "Stages(2 poles per stage)" } ] }, { "code" : "el:pitchScale", "count" : 2, "display" : "field", "name" : "Pitch Scaler", "number" : "144", "params" : [ { "name" : "Pitch co-efficient" }, { "name" : "latency" } ] }, { "code" : "el:G2reverb", "count" : 7, "display" : "field", "name" : "Stereo reverb", "number" : "145", "params" : [ { "name" : "Room size" }, { "name" : "Reverb time" }, { "name" : "Input BW" }, { "name" : "Damping" }, { "name" : "Dry sound" }, { "name" : "Reflections" }, { "name" : "Reverb tail" } ] }, { "code" : "el:zita-reverb", "count" : 10, "display" : "field", "name" : "zita-reverb", "number" : "146", "params" : [ { "name" : "Delay" }, { "name" : "Xover" }, { "name" : "RT-low" }, { "name" : "RT-mid" }, { "name" : "Damping" }, { "name" : "F1-freq" }, { "name" : "F1-gain" }, { "name" : "F2-freq" }, { "name" : "F2-gain" }, { "name" : "Output mix" } ] }, { "code" : "el:zita-reverb-amb", "count" : 10, "display" : "field", "name" : "zita-reverb-amb", "number" : "147", "params" : [ { "name" : "Delay" }, { "name" : "Xover" }, { "name" : "RT-low" }, { "name" : "RT-mid" }, { "name" : "Damping" }, { "name" : "F1-freq" }, { "name" : "F1-gain" }, { "name" : "F2-freq" }, { "name" : "F2-gain" }, { "name" : "XYZ gain" } ] }, { "code" : "el:tap_autopan", "count" : 3, "display" : "field", "name" : "TAP AutoPanner", "number" : "148", "params" : [ { "name" : "Frequency [Hz]" }, { "name" : "Depth [%]" }, { "name" : "Gain [dB]" } ] }, { "code" : "el:flanger", "count" : 4, "display" : "field", "name" : "Flanger", "number" : "149", "params" : [ { "name" : "Delay base (ms)" }, { "name" : "Max slowdown (ms)" }, { "name" : "LFO frequency (Hz)" }, { "name" : "Feedback" } ] }, { "code" : "el:tap_reverb", "count" : 8, "display" : "field", "name" : "TAP Reverberator", "number" : "150", "params" : [ { "name" : "Decay [ms]" }, { "name" : "Dry Level [dB]" }, { "name" : "Wet Level [dB]" }, { "name" : "Comb Filters" }, { "name" : "Allpass Filters" }, { "name" : "Bandpass Filter" }, { "name" : "Enhanced Stereo" }, { "name" : "Reverb Type" } ] }, { "code" : "el:split", "count" : 0, "display" : "field", "name" : "Mono to Stereo splitter", "number" : "151", "params" : [] }, { "code" : "el:plate", "count" : 3, "display" : "field", "name" : "Plate reverb", "number" : "152", "params" : [ { "name" : "Reverb time" }, { "name" : "Damping" }, { "name" : "Dry/wet mix" } ] }, { "code" : "el:triplePara", "count" : 15, "display" : "field", "name" : "Triple band parametric with shelves", "number" : "153", "params" : [ { "name" : "Low-shelving gain (dB)" }, { "name" : "Low-shelving frequency (Hz)" }, { "name" : "Low-shelving slope" }, { "name" : "Band 1 gain (dB)" }, { "name" : "Band 1 frequency (Hz)" }, { "name" : "Band 1 bandwidth (octaves)" }, { "name" : "Band 2 gain (dB)" }, { "name" : "Band 2 frequency (Hz)" }, { "name" : "Band 2 bandwidth (octaves)" }, { "name" : "Band 3 gain (dB)" }, { "name" : "Band 3 frequency (Hz)" }, { "name" : "Band 3 bandwidth (octaves)" }, { "name" : "High-shelving gain (dB)" }, { "name" : "High-shelving frequency (Hz)" }, { "name" : "High-shelving slope" } ] }, { "code" : "el:tap_dynamics_m", "count" : 7, "display" : "field", "name" : "TAP Dynamics (M)", "number" : "154", "params" : [ { "name" : "Attack [ms]" }, { "name" : "Release [ms]" }, { "name" : "Offset Gain [dB]" }, { "name" : "Makeup Gain [dB]" }, { "name" : "Envelope Volume [dB]" }, { "name" : "Gain Adjustment [dB]" }, { "name" : "Function" } ] }, { "code" : "el:xfade", "count" : 1, "display" : "field", "name" : "Crossfade", "number" : "155", "params" : [ { "name" : "Crossfade" } ] }, { "code" : "el:xfade4", "count" : 1, "display" : "field", "name" : "Crossfade (4 outs)", "number" : "156", "params" : [ { "name" : "Crossfade" } ] }, { "code" : "el:tap_dynamics_st", "count" : 10, "display" : "field", "name" : "TAP Dynamics (St)", "number" : "157", "params" : [ { "name" : "Attack [ms]" }, { "name" : "Release [ms]" }, { "name" : "Offset Gain [dB]" }, { "name" : "Makeup Gain [dB]" }, { "name" : "Envelope Volume (L) [dB]" }, { "name" : "Envelope Volume (R) [dB]" }, { "name" : "Gain Adjustment (L) [dB]" }, { "name" : "Gain Adjustment (R) [dB]" }, { "name" : "Stereo Mode" }, { "name" : "Function" } ] }, { "code" : "el:lpf", "count" : 1, "display" : "field", "name" : "Simple Low Pass Filter", "number" : "158", "params" : [ { "name" : "Cutoff Frequency (Hz)" } ] }, { "code" : "el:hpf", "count" : 1, "display" : "field", "name" : "Simple High Pass Filter", "number" : "159", "params" : [ { "name" : "Cutoff Frequency (Hz)" } ] }, { "code" : "el:ringmod_2i1o", "count" : 1, "display" : "field", "name" : "Ringmod with two inputs", "number" : "160", "params" : [ { "name" : "Modulation depth (0=none, 1=AM, 2=RM)" } ] }, { "code" : "el:ringmod_1i1o1l", "count" : 6, "display" : "field", "name" : "Ringmod with LFO", "number" : "161", "params" : [ { "name" : "Modulation depth (0=none, 1=AM, 2=RM)" }, { "name" : "Frequency (Hz)" }, { "name" : "Sine level" }, { "name" : "Triangle level" }, { "name" : "Sawtooth level" }, { "name" : "Square level" } ] }, { "code" : "el:comb", "count" : 2, "display" : "field", "name" : "Comb Filter", "number" : "162", "params" : [ { "name" : "Band separation (Hz)" }, { "name" : "Feedback" } ] }, { "code" : "el:tapeDelay", "count" : 10, "display" : "field", "name" : "Tape Delay Simulation", "number" : "163", "params" : [ { "name" : "Tape speed (inches/sec, 1=normal)" }, { "name" : "Dry level (dB)" }, { "name" : "Tap 1 distance (inches)" }, { "name" : "Tap 1 level (dB)" }, { "name" : "Tap 2 distance (inches)" }, { "name" : "Tap 2 level (dB)" }, { "name" : "Tap 3 distance (inches)" }, { "name" : "Tap 3 level (dB)" }, { "name" : "Tap 4 distance (inches)" }, { "name" : "Tap 4 level (dB)" } ] }, { "code" : "el:shaper", "count" : 1, "display" : "field", "name" : "Wave shaper", "number" : "164", "params" : [ { "name" : "Waveshape" } ] }, { "code" : "el:tap_deesser", "count" : 5, "display" : "field", "name" : "TAP DeEsser", "number" : "165", "params" : [ { "name" : "Threshold Level [dB]" }, { "name" : "Frequency [Hz]" }, { "name" : "Sidechain Filter" }, { "name" : "Monitor" }, { "name" : "Attenuation [dB]" } ] }, { "code" : "el:bodeShifterCV", "count" : 4, "display" : "field", "name" : "Bode frequency shifter (CV)", "number" : "166", "params" : [ { "name" : "Base shift" }, { "name" : "Mix (-1=down, +1=up)" }, { "name" : "CV Attenuation" }, { "name" : "latency" } ] }, { "code" : "el:chebstortion", "count" : 1, "display" : "field", "name" : "Chebyshev distortion", "number" : "167", "params" : [ { "name" : "Distortion" } ] }, { "code" : "el:surroundEncoder", "count" : 0, "display" : "field", "name" : "Surround matrix encoder", "number" : "168", "params" : [] }, { "code" : "el:divider", "count" : 1, "display" : "field", "name" : "Audio Divider (Suboctave Generator)", "number" : "169", "params" : [ { "name" : "Denominator" } ] }, { "code" : "el:modDelay", "count" : 1, "display" : "field", "name" : "Modulatable delay", "number" : "170", "params" : [ { "name" : "Base delay (s)" } ] }, { "code" : "el:combSplitter", "count" : 1, "display" : "field", "name" : "Comb Splitter", "number" : "171", "params" : [ { "name" : "Band separation (Hz)" } ] }, { "code" : "el:sc2", "count" : 6, "display" : "field", "name" : "SC2", "number" : "172", "params" : [ { "name" : "Attack time (ms)" }, { "name" : "Release time (ms)" }, { "name" : "Threshold level (dB)" }, { "name" : "Ratio (1:n)" }, { "name" : "Knee radius (dB)" }, { "name" : "Makeup gain (dB)" } ] }, { "code" : "el:allpass_n", "count" : 3, "display" : "field", "name" : "Allpass delay line, noninterpolating", "number" : "173", "params" : [ { "name" : "Max Delay (s)" }, { "name" : "Delay Time (s)" }, { "name" : "Decay Time (s)" } ] }, { "code" : "el:allpass_l", "count" : 3, "display" : "field", "name" : "Allpass delay line, linear interpolation", "number" : "174", "params" : [ { "name" : "Max Delay (s)" }, { "name" : "Delay Time (s)" }, { "name" : "Decay Time (s)" } ] }, { "code" : "el:allpass_c", "count" : 3, "display" : "field", "name" : "Allpass delay line, cubic spline interpolation", "number" : "175", "params" : [ { "name" : "Max Delay (s)" }, { "name" : "Delay Time (s)" }, { "name" : "Decay Time (s)" } ] }, { "code" : "el:tap_tubewarmth", "count" : 2, "display" : "field", "name" : "TAP TubeWarmth", "number" : "176", "params" : [ { "name" : "Drive" }, { "name" : "Tape--Tube Blend" } ] }, { "code" : "el:sinCos", "count" : 2, "display" : "field", "name" : "Sine + cosine oscillator", "number" : "177", "params" : [ { "name" : "Base frequency (Hz)" }, { "name" : "Pitch offset" } ] }, { "code" : "el:sifter", "count" : 1, "display" : "field", "name" : "Signal sifter", "number" : "178", "params" : [ { "name" : "Sift size" } ] }, { "code" : "el:notch_iir", "count" : 3, "display" : "field", "name" : "Mag's Notch Filter", "number" : "179", "params" : [ { "name" : "Center Frequency (Hz)" }, { "name" : "Bandwidth (Hz)" }, { "name" : "Stages(2 poles per stage)" } ] }, { "code" : "el:Tricardioid-to-AMB", "count" : 0, "display" : "field", "name" : "Three cardioids to AMB matrix", "number" : "180", "params" : [] }, { "code" : "el:Virtualmic", "count" : 4, "display" : "field", "name" : "Virtual stereo microphone", "number" : "181", "params" : [ { "name" : "Elevation" }, { "name" : "Azimuth" }, { "name" : "Angle" }, { "name" : "Polar" } ] }, { "code" : "el:UHJ-encoder", "count" : 0, "display" : "field", "name" : "UHJ Encoder", "number" : "182", "params" : [] }, { "code" : "el:UHJ-decoder", "count" : 0, "display" : "field", "name" : "UHJ Decoder", "number" : "183", "params" : [] }, { "code" : "el:pitchScaleHQ", "count" : 2, "display" : "field", "name" : "Higher Quality Pitch Scaler", "number" : "184", "params" : [ { "name" : "Pitch co-efficient" }, { "name" : "latency" } ] }, { "code" : "el:crossoverDist", "count" : 2, "display" : "field", "name" : "Crossover distortion", "number" : "185", "params" : [ { "name" : "Crossover amplitude" }, { "name" : "Smoothing" } ] }, { "code" : "el:Ambisonics-11-mono-panner", "count" : 2, "display" : "field", "name" : "AMB order 1,1 mono panner", "number" : "186", "params" : [ { "name" : "Elevation" }, { "name" : "Azimuth" } ] }, { "code" : "el:Ambisonics-11-stereo-panner", "count" : 3, "display" : "field", "name" : "AMB order 1,1 stereo panner", "number" : "187", "params" : [ { "name" : "Elevation" }, { "name" : "Width" }, { "name" : "Azimuth" } ] }, { "code" : "el:Ambisonics-11-rotator", "count" : 1, "display" : "field", "name" : "AMB order 1,1 rotator", "number" : "188", "params" : [ { "name" : "Angle" } ] }, { "code" : "el:Ambisonics-11-square-decoder", "count" : 6, "display" : "field", "name" : "AMB order 1,1 square decoder", "number" : "189", "params" : [ { "name" : "Front spkr" }, { "name" : "Shelf filt" }, { "name" : "HF XY gain" }, { "name" : "LF XY gain" }, { "name" : "Shelf freq" }, { "name" : "Distance" } ] }, { "code" : "el:Ambisonics-11-hexagon-decoder", "count" : 6, "display" : "field", "name" : "AMB order 1,1 hexagon decoder", "number" : "190", "params" : [ { "name" : "Front spkr" }, { "name" : "Shelf filt" }, { "name" : "HF XY gain" }, { "name" : "LF XY gain" }, { "name" : "Shelf freq" }, { "name" : "Distance" } ] }, { "code" : "el:Ambisonics-11-cube-decoder", "count" : 5, "display" : "field", "name" : "AMB order 1,1 cube decoder", "number" : "191", "params" : [ { "name" : "Shelf filt" }, { "name" : "HF XYZ gain" }, { "name" : "LF XYZ gain" }, { "name" : "Shelf freq" }, { "name" : "Distance" } ] }, { "code" : "el:tap_pinknoise", "count" : 3, "display" : "field", "name" : "TAP Pink/Fractal Noise", "number" : "192", "params" : [ { "name" : "Fractal Dimension" }, { "name" : "Signal Level [dB]" }, { "name" : "Noise Level [dB]" } ] }, { "code" : "el:tap_reflector", "count" : 3, "display" : "field", "name" : "TAP Reflector", "number" : "193", "params" : [ { "name" : "Fragment Length [ms]" }, { "name" : "Dry Level [dB]" }, { "name" : "Wet Level [dB]" } ] }, { "code" : "el:noise_white", "count" : 1, "display" : "field", "name" : "White Noise Source", "number" : "194", "params" : [ { "name" : "Amplitude" } ] }, { "code" : "el:diode", "count" : 1, "display" : "field", "name" : "Diode Processor", "number" : "195", "params" : [ { "name" : "Mode (0 for none, 1 for half wave, 2 for full wave)" } ] }, { "code" : "pn:dyn_compress_brutal", "count" : 1, "display" : "field", "name" : "dyn_compress_brutal", "number" : "1", "params" : [ { "name" : "gain-%" } ] }, { "code" : "pn:dyn_compress_hard", "count" : 1, "display" : "field", "name" : "dyn_compress_hard", "number" : "2", "params" : [ { "name" : "gain-%" } ] }, { "code" : "pn:dyn_compress_infinite", "count" : 1, "display" : "field", "name" : "dyn_compress_infinite", "number" : "3", "params" : [ { "name" : "gain-%" } ] }, { "code" : "pn:dyn_compress_medium", "count" : 1, "display" : "field", "name" : "dyn_compress_medium", "number" : "4", "params" : [ { "name" : "gain-%" } ] }, { "code" : "pn:dyn_compress_soft", "count" : 1, "display" : "field", "name" : "dyn_compress_soft", "number" : "5", "params" : [ { "name" : "gain-%" } ] }, { "code" : "pn:dyn_compress_supersoft", "count" : 1, "display" : "field", "name" : "dyn_compress_supersoft", "number" : "6", "params" : [ { "name" : "gain-%" } ] }, { "code" : "pn:eq_template", "count" : 10, "display" : "field", "name" : "eq_template", "number" : "7", "params" : [ { "name" : "10hz" }, { "name" : "40hz" }, { "name" : "100hz" }, { "name" : "220hz" }, { "name" : "460hz" }, { "name" : "940hz" }, { "name" : "1900hz" }, { "name" : "3800hz" }, { "name" : "7620hz" }, { "name" : "15300hz" } ] }, { "code" : "pn:eq_template2", "count" : 2, "display" : "field", "name" : "eq_template2", "number" : "8", "params" : [ { "name" : "1000hz" }, { "name" : "4000hz" } ] }, { "code" : "pn:f_bandpass", "count" : 2, "display" : "field", "name" : "f_bandpass", "number" : "9", "params" : [ { "name" : "freq" }, { "name" : "width" } ] }, { "code" : "pn:f_filtertest", "count" : 2, "display" : "field", "name" : "f_filtertest", "number" : "10", "params" : [ { "name" : "freq1" }, { "name" : "freq2" } ] }, { "code" : "pn:f_high_and_low", "count" : 0, "display" : "field", "name" : "f_high_and_low", "number" : "11", "params" : [] }, { "code" : "pn:f_highpass", "count" : 0, "display" : "field", "name" : "f_highpass", "number" : "12", "params" : [] }, { "code" : "pn:f_inverse_comb", "count" : 0, "display" : "field", "name" : "f_inverse_comb", "number" : "13", "params" : [] }, { "code" : "pn:f_lowp_sine", "count" : 0, "display" : "field", "name" : "f_lowp_sine", "number" : "14", "params" : [] }, { "code" : "pn:f_lowp_sine2", "count" : 0, "display" : "field", "name" : "f_lowp_sine2", "number" : "15", "params" : [] }, { "code" : "pn:f_lowpass", "count" : 1, "display" : "field", "name" : "f_lowpass", "number" : "16", "params" : [ { "name" : "freq_hz" } ] }, { "code" : "pn:f_rejectband", "count" : 0, "display" : "field", "name" : "f_rejectband", "number" : "17", "params" : [] }, { "code" : "pn:f_res_bandpass", "count" : 2, "display" : "field", "name" : "f_res_bandpass", "number" : "18", "params" : [ { "name" : "freq" }, { "name" : "width" } ] }, { "code" : "pn:f_res_lowpass", "count" : 1, "display" : "field", "name" : "f_res_lowpass", "number" : "19", "params" : [ { "name" : "arg-1" } ] }, { "code" : "pn:f_resonator", "count" : 0, "display" : "field", "name" : "f_resonator", "number" : "20", "params" : [] }, { "code" : "pn:f_two_filters", "count" : 2, "display" : "field", "name" : "f_two_filters", "number" : "21", "params" : [ { "name" : "lowgain" }, { "name" : "highgain" } ] }, { "code" : "pn:f_two_filters_pareq", "count" : 4, "display" : "field", "name" : "f_two_filters_pareq", "number" : "22", "params" : [ { "name" : "lowfreq" }, { "name" : "lowgain" }, { "name" : "highfreq" }, { "name" : "highgain" } ] }, { "code" : "pn:gate_crop", "count" : 0, "display" : "field", "name" : "gate_crop", "number" : "23", "params" : [] }, { "code" : "pn:gate_noisegate_1", "count" : 0, "display" : "field", "name" : "gate_noisegate_1", "number" : "24", "params" : [] }, { "code" : "pn:gate_noisegate_delanalog", "count" : 0, "display" : "field", "name" : "gate_noisegate_delanalog", "number" : "25", "params" : [] }, { "code" : "pn:gate_threshold", "count" : 0, "display" : "field", "name" : "gate_threshold", "number" : "26", "params" : [] }, { "code" : "pn:lad_hermes", "count" : 4, "display" : "field", "name" : "lad_hermes", "number" : "27", "params" : [ { "name" : "p1" }, { "name" : "p2" }, { "name" : "p3" }, { "name" : "p4" } ] }, { "code" : "pn:lad_metronome", "count" : 1, "display" : "field", "name" : "lad_metronome", "number" : "28", "params" : [ { "name" : "bpm" } ] }, { "code" : "pn:lad_oscillator_stack", "count" : 5, "display" : "field", "name" : "lad_oscillator_stack", "number" : "29", "params" : [ { "name" : "freq" }, { "name" : "osctype1" }, { "name" : "osctype2" }, { "name" : "gain1" }, { "name" : "gain2" } ] }, { "code" : "pn:lad_oscillator_test", "count" : 2, "display" : "field", "name" : "lad_oscillator_test", "number" : "30", "params" : [ { "name" : "freq" }, { "name" : "gain1" } ] }, { "code" : "pn:lad_sc4", "count" : 2, "display" : "field", "name" : "lad_sc4", "number" : "31", "params" : [ { "name" : "output-amplitude-dB" }, { "name" : "output-gain-reduction-dB" } ] }, { "code" : "pn:lad_sc4_rg", "count" : 4, "display" : "field", "name" : "lad_sc4_rg", "number" : "32", "params" : [ { "name" : "ratio" }, { "name" : "gain-dB" }, { "name" : "output-amplitude-dB" }, { "name" : "output-gain-reduction-dB" } ] }, { "code" : "pn:metronome", "count" : "1", "display" : "scale", "name" : "Metronome", "params" : [ { "begin" : "30", "default" : "120", "end" : "300", "name" : "BPM", "resolution" : "1" } ] }, { "code" : "pn:time_chorus1", "count" : 0, "display" : "field", "name" : "time_chorus1", "number" : "34", "params" : [] }, { "code" : "pn:time_delay1", "count" : 0, "display" : "field", "name" : "time_delay1", "number" : "35", "params" : [] }, { "code" : "pn:time_delay2", "count" : 0, "display" : "field", "name" : "time_delay2", "number" : "36", "params" : [] }, { "code" : "pn:time_flanger1", "count" : 0, "display" : "field", "name" : "time_flanger1", "number" : "37", "params" : [] }, { "code" : "pn:time_phaser1", "count" : 0, "display" : "field", "name" : "time_phaser1", "number" : "38", "params" : [] }, { "code" : "pn:time_reverb1", "count" : 0, "display" : "field", "name" : "time_reverb1", "number" : "39", "params" : [] }, { "code" : "pn:time_reverb2", "count" : 0, "display" : "field", "name" : "time_reverb2", "number" : "40", "params" : [] }, { "code" : "pn:time_reverb3", "count" : 0, "display" : "field", "name" : "time_reverb3", "number" : "41", "params" : [] }, { "code" : "pn:time_reverb4", "count" : 0, "display" : "field", "name" : "time_reverb4", "number" : "42", "params" : [] }, { "code" : "pn:time_wicked_dub", "count" : 0, "display" : "field", "name" : "time_wicked_dub", "number" : "43", "params" : [] }, { "code" : "pn:var_aw", "count" : 1, "display" : "field", "name" : "var_aw", "number" : "44", "params" : [ { "name" : "speed" } ] }, { "code" : "pn:var_aw_custom", "count" : 3, "display" : "field", "name" : "var_aw_custom", "number" : "45", "params" : [ { "name" : "low" }, { "name" : "high" }, { "name" : "speed" } ] }, { "code" : "pn:var_aw_ksv", "count" : 0, "display" : "field", "name" : "var_aw_ksv", "number" : "46", "params" : [] }, { "code" : "pn:var_aw_tri", "count" : 1, "display" : "field", "name" : "var_aw_tri", "number" : "47", "params" : [ { "name" : "speed" } ] }, { "code" : "pn:var_aw_tri_custom", "count" : 1, "display" : "field", "name" : "var_aw_tri_custom", "number" : "48", "params" : [ { "name" : "speed" } ] }, { "code" : "pn:var_chipmunk", "count" : 0, "display" : "field", "name" : "var_chipmunk", "number" : "49", "params" : [] }, { "code" : "pn:var_dali", "count" : 0, "display" : "field", "name" : "var_dali", "number" : "50", "params" : [] }, { "code" : "pn:var_molten_tape", "count" : 0, "display" : "field", "name" : "var_molten_tape", "number" : "51", "params" : [] }, { "code" : "pn:var_paralmadness", "count" : 3, "display" : "field", "name" : "var_paralmadness", "number" : "52", "params" : [ { "name" : "freq1" }, { "name" : "freq2" }, { "name" : "freq3" } ] }, { "code" : "pn:var_parchip", "count" : 2, "display" : "field", "name" : "var_parchip", "number" : "53", "params" : [ { "name" : "pitch" }, { "name" : "modfreq" } ] }, { "code" : "pn:var_stretched_tape", "count" : 0, "display" : "field", "name" : "var_stretched_tape", "number" : "54", "params" : [] }, { "code" : "pn:var_sweeping_pan", "count" : 1, "display" : "field", "name" : "var_sweeping_pan", "number" : "55", "params" : [ { "name" : "speed_hz" } ] }, { "code" : "pn:var_switching_pan", "count" : 1, "display" : "field", "name" : "var_switching_pan", "number" : "56", "params" : [ { "name" : "speed_hz" } ] }, { "code" : "kf", "count" : 6, "display" : "field", "name" : "Generic oscillator (preset)", "number" : "1", "params" : [ { "name" : "param-id" }, { "name" : "range-low" }, { "name" : "range-high" }, { "name" : "freq" }, { "name" : "mode" }, { "name" : "preset-number" } ] }, { "code" : "kog", "count" : 8, "display" : "field", "name" : "Generic oscillator", "number" : "2", "params" : [ { "name" : "param-id" }, { "name" : "range-low" }, { "name" : "range-high" }, { "name" : "freq" }, { "name" : "mode" }, { "name" : "pcount" }, { "name" : "start_val" }, { "name" : "end_val" } ] }, { "code" : "kl", "count" : 4, "display" : "field", "name" : "Linear envelope", "number" : "3", "params" : [ { "name" : "param-id" }, { "name" : "range-low" }, { "name" : "range-high" }, { "name" : "length-sec" } ] }, { "code" : "kl2", "count" : 5, "display" : "field", "name" : "Two-stage linear envelope", "number" : "4", "params" : [ { "name" : "param-id" }, { "name" : "range-low" }, { "name" : "range-high" }, { "name" : "1st-stage-sec" }, { "name" : "2nd-stage-sec" } ] }, { "code" : "klg", "count" : 4, "display" : "field", "name" : "Generic linear envelope", "number" : "5", "params" : [ { "name" : "param-id" }, { "name" : "range-low" }, { "name" : "range-high" }, { "name" : "point_count" } ] }, { "code" : "km", "count" : 5, "display" : "field", "name" : "MIDI-Controller", "number" : "6", "params" : [ { "name" : "param-id" }, { "name" : "range-low" }, { "name" : "range-high" }, { "name" : "controller" }, { "name" : "channel" } ] }, { "code" : "kos", "count" : 5, "display" : "field", "name" : "Sine oscillator", "number" : "7", "params" : [ { "name" : "param-id" }, { "name" : "range-low" }, { "name" : "range-high" }, { "name" : "freq" }, { "name" : "phase-offset" } ] }, { "code" : "ksv", "count" : 5, "display" : "field", "name" : "Volume analyze controller", "number" : "8", "params" : [ { "name" : "param-id" }, { "name" : "range-low" }, { "name" : "range-high" }, { "name" : "stamp-id" }, { "name" : "rms-toggle" } ] }, { "code" : "elv2:http://gareus.org/oss/lv2/b_overdrive", "count" : 7, "display" : "field", "name" : "B Organ Overdrive", "number" : "1", "params" : [ { "name" : "Bias" }, { "name" : "Feedback" }, { "name" : "SagToBias" }, { "name" : "Postdiff feedback" }, { "name" : "Global feedback" }, { "name" : "Input Gain" }, { "name" : "Output Gain" } ] }, { "code" : "elv2:http://gareus.org/oss/lv2/b_reverb", "count" : 2, "display" : "field", "name" : "B Organ Reverb", "number" : "2", "params" : [ { "name" : "Dry/Wet" }, { "name" : "Input Gain" } ] }, { "code" : "elv2:http://gareus.org/oss/lv2/b_whirl#extended", "count" : 26, "display" : "field", "name" : "B Organ Whirl Speaker Extended Version", "number" : "3", "params" : [ { "name" : "Motors (horn, drum speed: off/slow/fast)" }, { "name" : "Horn Level [dB]" }, { "name" : "Drum Level [dB]" }, { "name" : "Drum Stereo Width" }, { "name" : "Horn Speed Slow [rpm]" }, { "name" : "Horn Speed Fast [rpm]" }, { "name" : "Horn Acceleration [s]" }, { "name" : "Horn Deceleration [s]" }, { "name" : "Horn Brake" }, { "name" : "Horn Filter-1 Type:" }, { "name" : "Horn Filter-1 Frequency [Hz]" }, { "name" : "Horn Filter-1 Quality" }, { "name" : "Horn Filter-1 Gain (shelf) [dB]" }, { "name" : "Horn Filter-2 Type:" }, { "name" : "Horn Filter-2 Frequency [Hz]" }, { "name" : "Horn Filter-2 Quality" }, { "name" : "Horn Filter-2 Gain (shelf) [dB]" }, { "name" : "Drum Speed Slow [rpm]" }, { "name" : "Drum Speed Fast [rpm]" }, { "name" : "Drum Acceleration [s]" }, { "name" : "Drum Deceleration [s]" }, { "name" : "Drum Brake Position" }, { "name" : "Drum Filter Type:" }, { "name" : "Drum Filter Frequency [Hz]" }, { "name" : "Drum Filter Quality" }, { "name" : "Drum Filter Gain (shelf) [dB]" } ] }, { "code" : "elv2:http://gareus.org/oss/lv2/b_whirl#simple", "count" : 4, "display" : "field", "name" : "B Organ Whirl Speaker", "number" : "4", "params" : [ { "name" : "Motors (horn, drum speed: off/slow/fast)" }, { "name" : "Horn Level [dB]" }, { "name" : "Drum Level [dB]" }, { "name" : "Drum Stereo Width" } ] }, { "code" : "elv2:http://hyperglitch.com/dev/VocProc", "count" : 21, "display" : "field", "name" : "VocProc", "number" : "5", "params" : [ { "name" : "Pitch Factor" }, { "name" : "Robotize/Whisperize" }, { "name" : "formant correction/vocoder" }, { "name" : "0 - formant correction, 1 - vocoder" }, { "name" : "Automatic pitch correction" }, { "name" : "Threshold" }, { "name" : "Attack" }, { "name" : "Transpose" }, { "name" : "C" }, { "name" : "C#" }, { "name" : "D" }, { "name" : "D#" }, { "name" : "E" }, { "name" : "F" }, { "name" : "F#" }, { "name" : "G" }, { "name" : "G#" }, { "name" : "A" }, { "name" : "A#" }, { "name" : "B" }, { "name" : "Offset from tone" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/alaw", "count" : 0, "display" : "field", "name" : "A-Law Compressor", "number" : "6", "params" : [] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/alias", "count" : 1, "display" : "field", "name" : "Aliasing", "number" : "7", "params" : [ { "name" : "Aliasing level" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/allpass_c", "count" : 3, "display" : "field", "name" : "Allpass delay line, cubic spline interpolation", "number" : "8", "params" : [ { "name" : "Max Delay (s)" }, { "name" : "Delay Time (s)" }, { "name" : "Decay Time (s)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/allpass_l", "count" : 3, "display" : "field", "name" : "Allpass delay line, linear interpolation", "number" : "9", "params" : [ { "name" : "Max Delay (s)" }, { "name" : "Delay Time (s)" }, { "name" : "Decay Time (s)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/allpass_n", "count" : 3, "display" : "field", "name" : "Allpass delay line, noninterpolating", "number" : "10", "params" : [ { "name" : "Max Delay (s)" }, { "name" : "Delay Time (s)" }, { "name" : "Decay Time (s)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/amPitchshift", "count" : 3, "display" : "field", "name" : "AM pitchshifter", "number" : "11", "params" : [ { "name" : "Pitch shift" }, { "name" : "Buffer size" }, { "name" : "latency" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/amp", "count" : 1, "display" : "field", "name" : "Simple amplifier", "number" : "12", "params" : [ { "name" : "Amps gain (dB)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/analogueOsc", "count" : 4, "display" : "field", "name" : "Analogue Oscillator", "number" : "13", "params" : [ { "name" : "Waveform (1=sin, 2=tri, 3=squ, 4=saw)" }, { "name" : "Frequency (Hz)" }, { "name" : "Warmth" }, { "name" : "Instability" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/artificialLatency", "count" : 2, "display" : "field", "name" : "Artificial latency", "number" : "14", "params" : [ { "name" : "Delay (ms)" }, { "name" : "latency" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/autoPhaser", "count" : 5, "display" : "field", "name" : "Auto phaser", "number" : "15", "params" : [ { "name" : "Attack time (s)" }, { "name" : "Decay time (s)" }, { "name" : "Modulation depth" }, { "name" : "Feedback" }, { "name" : "Spread (octaves)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/bandpass_a_iir", "count" : 2, "display" : "field", "name" : "Glame Bandpass Analog Filter", "number" : "16", "params" : [ { "name" : "Center Frequency (Hz)" }, { "name" : "Bandwidth (Hz)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/bandpass_iir", "count" : 3, "display" : "field", "name" : "Glame Bandpass Filter", "number" : "17", "params" : [ { "name" : "Center Frequency (Hz)" }, { "name" : "Bandwidth (Hz)" }, { "name" : "Stages(2 poles per stage)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/bodeShifter", "count" : 2, "display" : "field", "name" : "Bode frequency shifter", "number" : "18", "params" : [ { "name" : "Frequency shift" }, { "name" : "latency" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/bodeShifterCV", "count" : 4, "display" : "field", "name" : "Bode frequency shifter (CV)", "number" : "19", "params" : [ { "name" : "Base shift" }, { "name" : "Mix (-1=down, +1=up)" }, { "name" : "CV Attenuation" }, { "name" : "latency" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/butthigh_iir", "count" : 2, "display" : "field", "name" : "GLAME Butterworth Highpass", "number" : "20", "params" : [ { "name" : "Cutoff Frequency (Hz)" }, { "name" : "Resonance" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/buttlow_iir", "count" : 2, "display" : "field", "name" : "GLAME Butterworth Lowpass", "number" : "21", "params" : [ { "name" : "Cutoff Frequency (Hz)" }, { "name" : "Resonance" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/bwxover_iir", "count" : 2, "display" : "field", "name" : "Glame Butterworth X-over Filter", "number" : "22", "params" : [ { "name" : "Cutoff Frequency (Hz)" }, { "name" : "Resonance" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/chebstortion", "count" : 1, "display" : "field", "name" : "Chebyshev distortion", "number" : "23", "params" : [ { "name" : "Distortion" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/comb", "count" : 2, "display" : "field", "name" : "Comb Filter", "number" : "24", "params" : [ { "name" : "Band separation (Hz)" }, { "name" : "Feedback" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/combSplitter", "count" : 1, "display" : "field", "name" : "Comb Splitter", "number" : "25", "params" : [ { "name" : "Band separation (Hz)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/comb_c", "count" : 3, "display" : "field", "name" : "Comb delay line, cubic spline interpolation", "number" : "26", "params" : [ { "name" : "Max Delay (s)" }, { "name" : "Delay Time (s)" }, { "name" : "Decay Time (s)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/comb_l", "count" : 3, "display" : "field", "name" : "Comb delay line, linear interpolation", "number" : "27", "params" : [ { "name" : "Max Delay (s)" }, { "name" : "Delay Time (s)" }, { "name" : "Decay Time (s)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/comb_n", "count" : 3, "display" : "field", "name" : "Comb delay line, noninterpolating", "number" : "28", "params" : [ { "name" : "Max Delay (s)" }, { "name" : "Delay Time (s)" }, { "name" : "Decay Time (s)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/const", "count" : 1, "display" : "field", "name" : "Constant Signal Generator", "number" : "29", "params" : [ { "name" : "Signal amplitude" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/crossoverDist", "count" : 2, "display" : "field", "name" : "Crossover distortion", "number" : "30", "params" : [ { "name" : "Crossover amplitude" }, { "name" : "Smoothing" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/dcRemove", "count" : 0, "display" : "field", "name" : "DC Offset Remover", "number" : "31", "params" : [] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/decay", "count" : 1, "display" : "field", "name" : "Exponential signal decay", "number" : "32", "params" : [ { "name" : "Decay Time (s)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/decimator", "count" : 2, "display" : "field", "name" : "Decimator", "number" : "33", "params" : [ { "name" : "Bit depth" }, { "name" : "Sample rate (Hz)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/declip", "count" : 0, "display" : "field", "name" : "Declipper", "number" : "34", "params" : [] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/delay_c", "count" : 2, "display" : "field", "name" : "Simple delay line, cubic spline interpolation", "number" : "35", "params" : [ { "name" : "Max Delay (s)" }, { "name" : "Delay Time (s)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/delay_l", "count" : 2, "display" : "field", "name" : "Simple delay line, linear interpolation", "number" : "36", "params" : [ { "name" : "Max Delay (s)" }, { "name" : "Delay Time (s)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/delay_n", "count" : 2, "display" : "field", "name" : "Simple delay line, noninterpolating", "number" : "37", "params" : [ { "name" : "Max Delay (s)" }, { "name" : "Delay Time (s)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/delayorama", "count" : 11, "display" : "field", "name" : "Delayorama", "number" : "38", "params" : [ { "name" : "Random seed" }, { "name" : "Input gain (dB)" }, { "name" : "Feedback (%)" }, { "name" : "Number of taps" }, { "name" : "First delay (s)" }, { "name" : "Delay range (s)" }, { "name" : "Delay change" }, { "name" : "Delay random (%)" }, { "name" : "Amplitude change" }, { "name" : "Amplitude random (%)" }, { "name" : "Dry/wet mix" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/diode", "count" : 1, "display" : "field", "name" : "Diode Processor", "number" : "39", "params" : [ { "name" : "Mode (0 for none, 1 for half wave, 2 for full wave)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/divider", "count" : 1, "display" : "field", "name" : "Audio Divider (Suboctave Generator)", "number" : "40", "params" : [ { "name" : "Denominator" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/djFlanger", "count" : 4, "display" : "field", "name" : "DJ flanger", "number" : "41", "params" : [ { "name" : "LFO sync" }, { "name" : "LFO period (s)" }, { "name" : "LFO depth (ms)" }, { "name" : "Feedback (%)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/dj_eq", "count" : 4, "display" : "field", "name" : "DJ EQ", "number" : "42", "params" : [ { "name" : "Lo gain (dB)" }, { "name" : "Mid gain (dB)" }, { "name" : "Hi gain (dB)" }, { "name" : "latency" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/dj_eq_mono", "count" : 4, "display" : "field", "name" : "DJ EQ (mono)", "number" : "43", "params" : [ { "name" : "Lo gain (dB)" }, { "name" : "Mid gain (dB)" }, { "name" : "Hi gain (dB)" }, { "name" : "latency" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/dysonCompress", "count" : 4, "display" : "field", "name" : "Dyson compressor", "number" : "44", "params" : [ { "name" : "Peak limit (dB)" }, { "name" : "Release time (s)" }, { "name" : "Fast compression ratio" }, { "name" : "Compression ratio" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/fadDelay", "count" : 2, "display" : "field", "name" : "Fractionally Addressed Delay Line", "number" : "45", "params" : [ { "name" : "Delay (seconds)" }, { "name" : "Feedback (dB)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/fastLookaheadLimiter", "count" : 5, "display" : "field", "name" : "Fast Lookahead limiter", "number" : "46", "params" : [ { "name" : "Input gain (dB)" }, { "name" : "Limit (dB)" }, { "name" : "Release time (s)" }, { "name" : "Attenuation (dB)" }, { "name" : "latency" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/flanger", "count" : 4, "display" : "field", "name" : "Flanger", "number" : "47", "params" : [ { "name" : "Delay base (ms)" }, { "name" : "Max slowdown (ms)" }, { "name" : "LFO frequency (Hz)" }, { "name" : "Feedback" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/fmOsc", "count" : 1, "display" : "field", "name" : "FM Oscillator", "number" : "48", "params" : [ { "name" : "Waveform (1=sin, 2=tri, 3=squ, 4=saw)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/foldover", "count" : 2, "display" : "field", "name" : "Foldover distortion", "number" : "49", "params" : [ { "name" : "Drive" }, { "name" : "Skew" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/fourByFourPole", "count" : 8, "display" : "field", "name" : "4 x 4 pole allpass", "number" : "50", "params" : [ { "name" : "Frequency 1" }, { "name" : "Feedback 1" }, { "name" : "Frequency 2" }, { "name" : "Feedback 2" }, { "name" : "Frequency 3" }, { "name" : "Feedback 3" }, { "name" : "Frequency 4" }, { "name" : "Feedback 4" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/foverdrive", "count" : 1, "display" : "field", "name" : "Fast overdrive", "number" : "51", "params" : [ { "name" : "Drive level" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/freqTracker", "count" : 1, "display" : "field", "name" : "Frequency tracker", "number" : "52", "params" : [ { "name" : "Tracking speed" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/gate", "count" : 10, "display" : "field", "name" : "Gate", "number" : "53", "params" : [ { "name" : "LF key filter (Hz)" }, { "name" : "HF key filter (Hz)" }, { "name" : "Threshold (dB)" }, { "name" : "Attack (ms)" }, { "name" : "Hold (ms)" }, { "name" : "Decay (ms)" }, { "name" : "Range (dB)" }, { "name" : "Output select (-1 = key listen, 0 = gate, 1 = bypass)" }, { "name" : "Key level (dB)" }, { "name" : "Gate state" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/giantFlange", "count" : 7, "display" : "field", "name" : "Giant flange", "number" : "54", "params" : [ { "name" : "Double delay" }, { "name" : "LFO frequency 1 (Hz)" }, { "name" : "Delay 1 range (s)" }, { "name" : "LFO frequency 2 (Hz)" }, { "name" : "Delay 2 range (s)" }, { "name" : "Feedback" }, { "name" : "Dry/Wet level" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/gong", "count" : 27, "display" : "field", "name" : "Gong model", "number" : "55", "params" : [ { "name" : "Inner damping" }, { "name" : "Outer damping" }, { "name" : "Mic position" }, { "name" : "Inner size 1" }, { "name" : "Inner stiffness 1 +" }, { "name" : "Inner stiffness 1 -" }, { "name" : "Inner size 2" }, { "name" : "Inner stiffness 2 +" }, { "name" : "Inner stiffness 2 -" }, { "name" : "Inner size 3" }, { "name" : "Inner stiffness 3 +" }, { "name" : "Inner stiffness 3 -" }, { "name" : "Inner size 4" }, { "name" : "Inner stiffness 4 +" }, { "name" : "Inner stiffness 4 -" }, { "name" : "Outer size 1" }, { "name" : "Outer stiffness 1 +" }, { "name" : "Outer stiffness 1 -" }, { "name" : "Outer size 2" }, { "name" : "Outer stiffness 2 +" }, { "name" : "Outer stiffness 2 -" }, { "name" : "Outer size 3" }, { "name" : "Outer stiffness 3 +" }, { "name" : "Outer stiffness 3 -" }, { "name" : "Outer size 4" }, { "name" : "Outer stiffness 4 +" }, { "name" : "Outer stiffness 4 -" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/gongBeater", "count" : 3, "display" : "field", "name" : "Gong beater", "number" : "56", "params" : [ { "name" : "Impulse gain (dB)" }, { "name" : "Strike gain (dB)" }, { "name" : "Strike duration (s)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/gverb", "count" : 7, "display" : "field", "name" : "GVerb", "number" : "57", "params" : [ { "name" : "Roomsize (m)" }, { "name" : "Reverb time (s)" }, { "name" : "Damping" }, { "name" : "Input bandwidth" }, { "name" : "Dry signal level (dB)" }, { "name" : "Early reflection level (dB)" }, { "name" : "Tail level (dB)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/hardLimiter", "count" : 3, "display" : "field", "name" : "Hard Limiter", "number" : "58", "params" : [ { "name" : "dB limit" }, { "name" : "Wet level" }, { "name" : "Residue level" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/harmonicGen", "count" : 10, "display" : "field", "name" : "Harmonic generator", "number" : "59", "params" : [ { "name" : "Fundamental magnitude" }, { "name" : "2nd harmonic magnitude" }, { "name" : "3rd harmonic magnitude" }, { "name" : "4th harmonic magnitude" }, { "name" : "5th harmonic magnitude" }, { "name" : "6th harmonic magnitude" }, { "name" : "7th harmonic magnitude" }, { "name" : "8th harmonic magnitude" }, { "name" : "9th harmonic magnitude" }, { "name" : "10th harmonic magnitude" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/hermesFilter", "count" : 52, "display" : "field", "name" : "Hermes Filter", "number" : "60", "params" : [ { "name" : "LFO1 freq (Hz)" }, { "name" : "LFO1 wave (0 = sin, 1 = tri, 2 = saw, 3 = squ, 4 = s&h)" }, { "name" : "LFO2 freq (Hz)" }, { "name" : "LFO2 wave (0 = sin, 1 = tri, 2 = saw, 3 = squ, 4 = s&h)" }, { "name" : "Osc1 freq (Hz)" }, { "name" : "Osc1 wave (0 = sin, 1 = tri, 2 = saw, 3 = squ, 4 = noise)" }, { "name" : "Osc2 freq (Hz)" }, { "name" : "Osc2 wave (0 = sin, 1 = tri, 2 = saw, 3 = squ, 4 = noise)" }, { "name" : "Ringmod 1 depth (0=none, 1=AM, 2=RM)" }, { "name" : "Ringmod 2 depth (0=none, 1=AM, 2=RM)" }, { "name" : "Ringmod 3 depth (0=none, 1=AM, 2=RM)" }, { "name" : "Osc1 gain (dB)" }, { "name" : "RM1 gain (dB)" }, { "name" : "Osc2 gain (dB)" }, { "name" : "RM2 gain (dB)" }, { "name" : "Input gain (dB)" }, { "name" : "RM3 gain (dB)" }, { "name" : "Xover lower freq" }, { "name" : "Xover upper freq" }, { "name" : "Dist1 drive" }, { "name" : "Dist2 drive" }, { "name" : "Dist3 drive" }, { "name" : "Filt1 type (0=none, 1=LP, 2=HP, 3=BP, 4=BR, 5=AP)" }, { "name" : "Filt1 freq" }, { "name" : "Filt1 q" }, { "name" : "Filt1 resonance" }, { "name" : "Filt1 LFO1 level" }, { "name" : "Filt1 LFO2 level" }, { "name" : "Filt2 type (0=none, 1=LP, 2=HP, 3=BP, 4=BR, 5=AP)" }, { "name" : "Filt2 freq" }, { "name" : "Filt2 q" }, { "name" : "Filt2 resonance" }, { "name" : "Filt2 LFO1 level" }, { "name" : "Filt2 LFO2 level" }, { "name" : "Filt3 type (0=none, 1=LP, 2=HP, 3=BP, 4=BR, 5=AP)" }, { "name" : "Filt3 freq" }, { "name" : "Filt3 q" }, { "name" : "Filt3 resonance" }, { "name" : "Filt3 LFO1 level" }, { "name" : "Filt3 LFO2 level" }, { "name" : "Delay1 length (s)" }, { "name" : "Delay1 feedback" }, { "name" : "Delay1 wetness" }, { "name" : "Delay2 length (s)" }, { "name" : "Delay2 feedback" }, { "name" : "Delay2 wetness" }, { "name" : "Delay3 length (s)" }, { "name" : "Delay3 feedback" }, { "name" : "Delay3 wetness" }, { "name" : "Band 1 gain (dB)" }, { "name" : "Band 2 gain (dB)" }, { "name" : "Band 3 gain (dB)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/highpass_iir", "count" : 2, "display" : "field", "name" : "Glame Highpass Filter", "number" : "61", "params" : [ { "name" : "Cutoff Frequency" }, { "name" : "Stages(2 poles per stage)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/hilbert", "count" : 1, "display" : "field", "name" : "Hilbert transformer", "number" : "62", "params" : [ { "name" : "latency" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/impulse_fc", "count" : 1, "display" : "field", "name" : "Non-bandlimited single-sample impulses", "number" : "63", "params" : [ { "name" : "Frequency (Hz)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/inv", "count" : 0, "display" : "field", "name" : "Inverter", "number" : "64", "params" : [] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/karaoke", "count" : 1, "display" : "field", "name" : "Karaoke", "number" : "65", "params" : [ { "name" : "Vocal volume (dB)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/lcrDelay", "count" : 11, "display" : "field", "name" : "L/C/R Delay", "number" : "66", "params" : [ { "name" : "L delay (ms)" }, { "name" : "L level" }, { "name" : "C delay (ms)" }, { "name" : "C level" }, { "name" : "R delay (ms)" }, { "name" : "R level" }, { "name" : "Feedback" }, { "name" : "High damp (%)" }, { "name" : "Low damp (%)" }, { "name" : "Spread" }, { "name" : "Dry/Wet level" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/lfoPhaser", "count" : 4, "display" : "field", "name" : "LFO Phaser", "number" : "67", "params" : [ { "name" : "LFO rate (Hz)" }, { "name" : "LFO depth" }, { "name" : "Feedback" }, { "name" : "Spread (octaves)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/lookaheadLimiter", "count" : 4, "display" : "field", "name" : "Lookahead limiter", "number" : "68", "params" : [ { "name" : "Limit (dB)" }, { "name" : "Lookahead delay" }, { "name" : "Attenuation (dB)" }, { "name" : "latency" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/lookaheadLimiterConst", "count" : 4, "display" : "field", "name" : "Lookahead limiter (fixed latency)", "number" : "69", "params" : [ { "name" : "Limit (dB)" }, { "name" : "Lookahead time (s)" }, { "name" : "Attenuation (dB)" }, { "name" : "latency" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/lowpass_iir", "count" : 2, "display" : "field", "name" : "Glame Lowpass Filter", "number" : "70", "params" : [ { "name" : "Cutoff Frequency" }, { "name" : "Stages(2 poles per stage)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/lsFilter", "count" : 3, "display" : "field", "name" : "LS Filter", "number" : "71", "params" : [ { "name" : "Filter type (0=LP, 1=BP, 2=HP)" }, { "name" : "Cutoff frequency (Hz)" }, { "name" : "Resonance" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/matrixMSSt", "count" : 1, "display" : "field", "name" : "Matrix: MS to Stereo", "number" : "72", "params" : [ { "name" : "Width" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/matrixSpatialiser", "count" : 1, "display" : "field", "name" : "Matrix Spatialiser", "number" : "73", "params" : [ { "name" : "Width" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/matrixStMS", "count" : 0, "display" : "field", "name" : "Matrix: Stereo to MS", "number" : "74", "params" : [] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/mbeq", "count" : 16, "display" : "field", "name" : "Multiband EQ", "number" : "75", "params" : [ { "name" : "50Hz gain (low shelving)" }, { "name" : "100Hz gain" }, { "name" : "156Hz gain" }, { "name" : "220Hz gain" }, { "name" : "311Hz gain" }, { "name" : "440Hz gain" }, { "name" : "622Hz gain" }, { "name" : "880Hz gain" }, { "name" : "1250Hz gain" }, { "name" : "1750Hz gain" }, { "name" : "2500Hz gain" }, { "name" : "3500Hz gain" }, { "name" : "5000Hz gain" }, { "name" : "10000Hz gain" }, { "name" : "20000Hz gain" }, { "name" : "latency" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/modDelay", "count" : 1, "display" : "field", "name" : "Modulatable delay", "number" : "76", "params" : [ { "name" : "Base delay (s)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/multivoiceChorus", "count" : 6, "display" : "field", "name" : "Multivoice Chorus", "number" : "77", "params" : [ { "name" : "Number of voices" }, { "name" : "Delay base (ms)" }, { "name" : "Voice separation (ms)" }, { "name" : "Detune (%)" }, { "name" : "LFO frequency (Hz)" }, { "name" : "Output attenuation (dB)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/pitchScaleHQ", "count" : 2, "display" : "field", "name" : "Higher Quality Pitch Scaler", "number" : "78", "params" : [ { "name" : "Pitch co-efficient" }, { "name" : "latency" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/plate", "count" : 3, "display" : "field", "name" : "Plate reverb", "number" : "79", "params" : [ { "name" : "Reverb time" }, { "name" : "Damping" }, { "name" : "Dry/wet mix" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/pointerCastDistortion", "count" : 2, "display" : "field", "name" : "Pointer cast distortion", "number" : "80", "params" : [ { "name" : "Effect cutoff freq (Hz)" }, { "name" : "Dry/wet mix" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/rateShifter", "count" : 1, "display" : "field", "name" : "Rate shifter", "number" : "81", "params" : [ { "name" : "Rate" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/retroFlange", "count" : 2, "display" : "field", "name" : "Retro Flanger", "number" : "82", "params" : [ { "name" : "Average stall (ms)" }, { "name" : "Flange frequency (Hz)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/revdelay", "count" : 5, "display" : "field", "name" : "Reverse Delay (5s max)", "number" : "83", "params" : [ { "name" : "Delay Time (s)" }, { "name" : "Dry Level (dB)" }, { "name" : "Wet Level (dB)" }, { "name" : "Feedback" }, { "name" : "Crossfade samples" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/ringmod_1i1o1l", "count" : 6, "display" : "field", "name" : "Ringmod with LFO", "number" : "84", "params" : [ { "name" : "Modulation depth (0=none, 1=AM, 2=RM)" }, { "name" : "Frequency (Hz)" }, { "name" : "Sine level" }, { "name" : "Triangle level" }, { "name" : "Sawtooth level" }, { "name" : "Square level" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/ringmod_2i1o", "count" : 1, "display" : "field", "name" : "Ringmod with two inputs", "number" : "85", "params" : [ { "name" : "Modulation depth (0=none, 1=AM, 2=RM)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/satanMaximiser", "count" : 2, "display" : "field", "name" : "Barry's Satan Maximiser", "number" : "86", "params" : [ { "name" : "Decay time (samples)" }, { "name" : "Knee point (dB)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/sc1", "count" : 6, "display" : "field", "name" : "SC1", "number" : "87", "params" : [ { "name" : "Attack time (ms)" }, { "name" : "Release time (ms)" }, { "name" : "Threshold level (dB)" }, { "name" : "Ratio (1:n)" }, { "name" : "Knee radius (dB)" }, { "name" : "Makeup gain (dB)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/sc2", "count" : 6, "display" : "field", "name" : "SC2", "number" : "88", "params" : [ { "name" : "Attack time (ms)" }, { "name" : "Release time (ms)" }, { "name" : "Threshold level (dB)" }, { "name" : "Ratio (1:n)" }, { "name" : "Knee radius (dB)" }, { "name" : "Makeup gain (dB)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/sc3", "count" : 7, "display" : "field", "name" : "SC3", "number" : "89", "params" : [ { "name" : "Attack time (ms)" }, { "name" : "Release time (ms)" }, { "name" : "Threshold level (dB)" }, { "name" : "Ratio (1:n)" }, { "name" : "Knee radius (dB)" }, { "name" : "Makeup gain (dB)" }, { "name" : "Chain balance" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/sc4", "count" : 9, "display" : "field", "name" : "SC4", "number" : "90", "params" : [ { "name" : "RMS/peak" }, { "name" : "Attack time (ms)" }, { "name" : "Release time (ms)" }, { "name" : "Threshold level (dB)" }, { "name" : "Ratio (1:n)" }, { "name" : "Knee radius (dB)" }, { "name" : "Makeup gain (dB)" }, { "name" : "Amplitude (dB)" }, { "name" : "Gain reduction (dB)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/se4", "count" : 9, "display" : "field", "name" : "SE4", "number" : "91", "params" : [ { "name" : "RMS/peak" }, { "name" : "Attack time (ms)" }, { "name" : "Release time (ms)" }, { "name" : "Threshold level (dB)" }, { "name" : "Ratio (1:n)" }, { "name" : "Knee radius (dB)" }, { "name" : "Attenuation (dB)" }, { "name" : "Amplitude (dB)" }, { "name" : "Gain expansion (dB)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/shaper", "count" : 1, "display" : "field", "name" : "Wave shaper", "number" : "92", "params" : [ { "name" : "Waveshape" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/sifter", "count" : 1, "display" : "field", "name" : "Signal sifter", "number" : "93", "params" : [ { "name" : "Sift size" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/sinCos", "count" : 2, "display" : "field", "name" : "Sine + cosine oscillator", "number" : "94", "params" : [ { "name" : "Base frequency (Hz)" }, { "name" : "Pitch offset" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/singlePara", "count" : 3, "display" : "field", "name" : "Single band parametric", "number" : "95", "params" : [ { "name" : "Gain (dB)" }, { "name" : "Frequency (Hz)" }, { "name" : "Bandwidth (octaves)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/sinusWavewrapper", "count" : 1, "display" : "field", "name" : "Sinus wavewrapper", "number" : "96", "params" : [ { "name" : "Wrap degree" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/smoothDecimate", "count" : 2, "display" : "field", "name" : "Smooth Decimator", "number" : "97", "params" : [ { "name" : "Resample rate" }, { "name" : "Smoothing" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/split", "count" : 0, "display" : "field", "name" : "Mono to Stereo splitter", "number" : "98", "params" : [] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/surroundEncoder", "count" : 0, "display" : "field", "name" : "Surround matrix encoder", "number" : "99", "params" : [] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/svf", "count" : 4, "display" : "field", "name" : "State Variable Filter", "number" : "100", "params" : [ { "name" : "Filter type (0=none, 1=LP, 2=HP, 3=BP, 4=BR, 5=AP)" }, { "name" : "Filter freq" }, { "name" : "Filter Q" }, { "name" : "Filter resonance" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/tapeDelay", "count" : 10, "display" : "field", "name" : "Tape Delay Simulation", "number" : "101", "params" : [ { "name" : "Tape speed (inches/sec, 1=normal)" }, { "name" : "Dry level (dB)" }, { "name" : "Tap 1 distance (inches)" }, { "name" : "Tap 1 level (dB)" }, { "name" : "Tap 2 distance (inches)" }, { "name" : "Tap 2 level (dB)" }, { "name" : "Tap 3 distance (inches)" }, { "name" : "Tap 3 level (dB)" }, { "name" : "Tap 4 distance (inches)" }, { "name" : "Tap 4 level (dB)" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/transient", "count" : 2, "display" : "field", "name" : "Transient mangler", "number" : "102", "params" : [ { "name" : "Attack speed" }, { "name" : "Sustain time" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/triplePara", "count" : 15, "display" : "field", "name" : "Triple band parametric with shelves", "number" : "103", "params" : [ { "name" : "Low-shelving gain (dB)" }, { "name" : "Low-shelving frequency (Hz)" }, { "name" : "Low-shelving slope" }, { "name" : "Band 1 gain (dB)" }, { "name" : "Band 1 frequency (Hz)" }, { "name" : "Band 1 bandwidth (octaves)" }, { "name" : "Band 2 gain (dB)" }, { "name" : "Band 2 frequency (Hz)" }, { "name" : "Band 2 bandwidth (octaves)" }, { "name" : "Band 3 gain (dB)" }, { "name" : "Band 3 frequency (Hz)" }, { "name" : "Band 3 bandwidth (octaves)" }, { "name" : "High-shelving gain (dB)" }, { "name" : "High-shelving frequency (Hz)" }, { "name" : "High-shelving slope" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/ulaw", "count" : 0, "display" : "field", "name" : "μ-Law Compressor", "number" : "104", "params" : [] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/valve", "count" : 2, "display" : "field", "name" : "Valve saturation", "number" : "105", "params" : [ { "name" : "Distortion level" }, { "name" : "Distortion character" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/valveRect", "count" : 2, "display" : "field", "name" : "Valve rectifier", "number" : "106", "params" : [ { "name" : "Sag level" }, { "name" : "Distortion" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/vynil", "count" : 5, "display" : "field", "name" : "VyNil (Vinyl Effect)", "number" : "107", "params" : [ { "name" : "Year" }, { "name" : "RPM" }, { "name" : "Surface warping" }, { "name" : "Crackle" }, { "name" : "Wear" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/waveTerrain", "count" : 0, "display" : "field", "name" : "Wave Terrain Oscillator", "number" : "108", "params" : [] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/xfade", "count" : 1, "display" : "field", "name" : "Crossfade", "number" : "109", "params" : [ { "name" : "Crossfade" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/xfade4", "count" : 1, "display" : "field", "name" : "Crossfade (4 outs)", "number" : "110", "params" : [ { "name" : "Crossfade" } ] }, { "code" : "elv2:http://plugin.org.uk/swh-plugins/zm1", "count" : 0, "display" : "field", "name" : "z-1", "number" : "111", "params" : [] } ], "split" : { "cop" : { "a" : 1, "z" : 47 }, "ctrl" : { "a" : 299, "z" : 306 }, "ladspa" : { "a" : 48, "b" : 95, "c" : 143, "d" : 191, "z" : 242 }, "lv2" : { "a" : 307, "z" : 417 }, "preset" : { "a" : 243, "b" : 270, "z" : 298 } }, "user_help" : [ "LV2 B Organ Overdrive -elv2:http://gareus.org/oss/lv2/b_overdrive, Bias, Feedback, SagToBias, Postdiff feedback, Global feedback, Input Gain, Output Gain", "LV2 B Organ Reverb -elv2:http://gareus.org/oss/lv2/b_reverb, Dry/Wet, Input Gain", "LV2 B Organ Whirl Speaker Extended Version -elv2:http://gareus.org/oss/lv2/b_whirl#extended, Motors (horn, drum speed: off/slow/fast), Horn Level [dB], Drum Level [dB], Drum Stereo Width, Horn Speed Slow [rpm], Horn Speed Fast [rpm], Horn Acceleration [s], Horn Deceleration [s], Horn Brake, Horn Filter-1 Type:, Horn Filter-1 Frequency [Hz], Horn Filter-1 Quality, Horn Filter-1 Gain (shelf) [dB], Horn Filter-2 Type:, Horn Filter-2 Frequency [Hz], Horn Filter-2 Quality, Horn Filter-2 Gain (shelf) [dB], Drum Speed Slow [rpm], Drum Speed Fast [rpm], Drum Acceleration [s], Drum Deceleration [s], Drum Brake Position, Drum Filter Type:, Drum Filter Frequency [Hz], Drum Filter Quality, Drum Filter Gain (shelf) [dB]", "LV2 B Organ Whirl Speaker -elv2:http://gareus.org/oss/lv2/b_whirl#simple, Motors (horn, drum speed: off/slow/fast), Horn Level [dB], Drum Level [dB], Drum Stereo Width", "LV2 VocProc -elv2:http://hyperglitch.com/dev/VocProc, Pitch Factor, Robotize/Whisperize, formant correction/vocoder, 0 - formant correction, 1 - vocoder, Automatic pitch correction, Threshold, Attack, Transpose, C, C#, D, D#, E, F, F#, G, G#, A, A#, B, Offset from tone", "LV2 A-Law Compressor -elv2:http://plugin.org.uk/swh-plugins/alaw, ", "LV2 Aliasing -elv2:http://plugin.org.uk/swh-plugins/alias, Aliasing level", "LV2 Allpass delay line, cubic spline interpolation -elv2:http://plugin.org.uk/swh-plugins/allpass_c, Max Delay (s), Delay Time (s), Decay Time (s)", "LV2 Allpass delay line, linear interpolation -elv2:http://plugin.org.uk/swh-plugins/allpass_l, Max Delay (s), Delay Time (s), Decay Time (s)", "LV2 Allpass delay line, noninterpolating -elv2:http://plugin.org.uk/swh-plugins/allpass_n, Max Delay (s), Delay Time (s), Decay Time (s)", "LV2 AM pitchshifter -elv2:http://plugin.org.uk/swh-plugins/amPitchshift, Pitch shift, Buffer size, latency", "LV2 Simple amplifier -elv2:http://plugin.org.uk/swh-plugins/amp, Amps gain (dB)", "LV2 Analogue Oscillator -elv2:http://plugin.org.uk/swh-plugins/analogueOsc, Waveform (1=sin, 2=tri, 3=squ, 4=saw), Frequency (Hz), Warmth, Instability", "LV2 Artificial latency -elv2:http://plugin.org.uk/swh-plugins/artificialLatency, Delay (ms), latency", "LV2 Auto phaser -elv2:http://plugin.org.uk/swh-plugins/autoPhaser, Attack time (s), Decay time (s), Modulation depth, Feedback, Spread (octaves)", "LV2 Glame Bandpass Analog Filter -elv2:http://plugin.org.uk/swh-plugins/bandpass_a_iir, Center Frequency (Hz), Bandwidth (Hz)", "LV2 Glame Bandpass Filter -elv2:http://plugin.org.uk/swh-plugins/bandpass_iir, Center Frequency (Hz), Bandwidth (Hz), Stages(2 poles per stage)", "LV2 Bode frequency shifter -elv2:http://plugin.org.uk/swh-plugins/bodeShifter, Frequency shift, latency", "LV2 Bode frequency shifter (CV) -elv2:http://plugin.org.uk/swh-plugins/bodeShifterCV, Base shift, Mix (-1=down, +1=up), CV Attenuation, latency", "LV2 GLAME Butterworth Highpass -elv2:http://plugin.org.uk/swh-plugins/butthigh_iir, Cutoff Frequency (Hz), Resonance", "LV2 GLAME Butterworth Lowpass -elv2:http://plugin.org.uk/swh-plugins/buttlow_iir, Cutoff Frequency (Hz), Resonance", "LV2 Glame Butterworth X-over Filter -elv2:http://plugin.org.uk/swh-plugins/bwxover_iir, Cutoff Frequency (Hz), Resonance", "LV2 Chebyshev distortion -elv2:http://plugin.org.uk/swh-plugins/chebstortion, Distortion", "LV2 Comb Filter -elv2:http://plugin.org.uk/swh-plugins/comb, Band separation (Hz), Feedback", "LV2 Comb Splitter -elv2:http://plugin.org.uk/swh-plugins/combSplitter, Band separation (Hz)", "LV2 Comb delay line, cubic spline interpolation -elv2:http://plugin.org.uk/swh-plugins/comb_c, Max Delay (s), Delay Time (s), Decay Time (s)", "LV2 Comb delay line, linear interpolation -elv2:http://plugin.org.uk/swh-plugins/comb_l, Max Delay (s), Delay Time (s), Decay Time (s)", "LV2 Comb delay line, noninterpolating -elv2:http://plugin.org.uk/swh-plugins/comb_n, Max Delay (s), Delay Time (s), Decay Time (s)", "LV2 Constant Signal Generator -elv2:http://plugin.org.uk/swh-plugins/const, Signal amplitude", "LV2 Crossover distortion -elv2:http://plugin.org.uk/swh-plugins/crossoverDist, Crossover amplitude, Smoothing", "LV2 DC Offset Remover -elv2:http://plugin.org.uk/swh-plugins/dcRemove, ", "LV2 Exponential signal decay -elv2:http://plugin.org.uk/swh-plugins/decay, Decay Time (s)", "LV2 Decimator -elv2:http://plugin.org.uk/swh-plugins/decimator, Bit depth, Sample rate (Hz)", "LV2 Declipper -elv2:http://plugin.org.uk/swh-plugins/declip, ", "LV2 Simple delay line, cubic spline interpolation -elv2:http://plugin.org.uk/swh-plugins/delay_c, Max Delay (s), Delay Time (s)", "LV2 Simple delay line, linear interpolation -elv2:http://plugin.org.uk/swh-plugins/delay_l, Max Delay (s), Delay Time (s)", "LV2 Simple delay line, noninterpolating -elv2:http://plugin.org.uk/swh-plugins/delay_n, Max Delay (s), Delay Time (s)", "LV2 Delayorama -elv2:http://plugin.org.uk/swh-plugins/delayorama, Random seed, Input gain (dB), Feedback (%), Number of taps, First delay (s), Delay range (s), Delay change, Delay random (%), Amplitude change, Amplitude random (%), Dry/wet mix", "LV2 Diode Processor -elv2:http://plugin.org.uk/swh-plugins/diode, Mode (0 for none, 1 for half wave, 2 for full wave)", "LV2 Audio Divider (Suboctave Generator) -elv2:http://plugin.org.uk/swh-plugins/divider, Denominator", "LV2 DJ flanger -elv2:http://plugin.org.uk/swh-plugins/djFlanger, LFO sync, LFO period (s), LFO depth (ms), Feedback (%)", "LV2 DJ EQ -elv2:http://plugin.org.uk/swh-plugins/dj_eq, Lo gain (dB), Mid gain (dB), Hi gain (dB), latency", "LV2 DJ EQ (mono) -elv2:http://plugin.org.uk/swh-plugins/dj_eq_mono, Lo gain (dB), Mid gain (dB), Hi gain (dB), latency", "LV2 Dyson compressor -elv2:http://plugin.org.uk/swh-plugins/dysonCompress, Peak limit (dB), Release time (s), Fast compression ratio, Compression ratio", "LV2 Fractionally Addressed Delay Line -elv2:http://plugin.org.uk/swh-plugins/fadDelay, Delay (seconds), Feedback (dB)", "LV2 Fast Lookahead limiter -elv2:http://plugin.org.uk/swh-plugins/fastLookaheadLimiter, Input gain (dB), Limit (dB), Release time (s), Attenuation (dB), latency", "LV2 Flanger -elv2:http://plugin.org.uk/swh-plugins/flanger, Delay base (ms), Max slowdown (ms), LFO frequency (Hz), Feedback", "LV2 FM Oscillator -elv2:http://plugin.org.uk/swh-plugins/fmOsc, Waveform (1=sin, 2=tri, 3=squ, 4=saw)", "LV2 Foldover distortion -elv2:http://plugin.org.uk/swh-plugins/foldover, Drive, Skew", "LV2 4 x 4 pole allpass -elv2:http://plugin.org.uk/swh-plugins/fourByFourPole, Frequency 1, Feedback 1, Frequency 2, Feedback 2, Frequency 3, Feedback 3, Frequency 4, Feedback 4", "LV2 Fast overdrive -elv2:http://plugin.org.uk/swh-plugins/foverdrive, Drive level", "LV2 Frequency tracker -elv2:http://plugin.org.uk/swh-plugins/freqTracker, Tracking speed", "LV2 Gate -elv2:http://plugin.org.uk/swh-plugins/gate, LF key filter (Hz), HF key filter (Hz), Threshold (dB), Attack (ms), Hold (ms), Decay (ms), Range (dB), Output select (-1 = key listen, 0 = gate, 1 = bypass), Key level (dB), Gate state", "LV2 Giant flange -elv2:http://plugin.org.uk/swh-plugins/giantFlange, Double delay, LFO frequency 1 (Hz), Delay 1 range (s), LFO frequency 2 (Hz), Delay 2 range (s), Feedback, Dry/Wet level", "LV2 Gong model -elv2:http://plugin.org.uk/swh-plugins/gong, Inner damping, Outer damping, Mic position, Inner size 1, Inner stiffness 1 +, Inner stiffness 1 -, Inner size 2, Inner stiffness 2 +, Inner stiffness 2 -, Inner size 3, Inner stiffness 3 +, Inner stiffness 3 -, Inner size 4, Inner stiffness 4 +, Inner stiffness 4 -, Outer size 1, Outer stiffness 1 +, Outer stiffness 1 -, Outer size 2, Outer stiffness 2 +, Outer stiffness 2 -, Outer size 3, Outer stiffness 3 +, Outer stiffness 3 -, Outer size 4, Outer stiffness 4 +, Outer stiffness 4 -", "LV2 Gong beater -elv2:http://plugin.org.uk/swh-plugins/gongBeater, Impulse gain (dB), Strike gain (dB), Strike duration (s)", "LV2 GVerb -elv2:http://plugin.org.uk/swh-plugins/gverb, Roomsize (m), Reverb time (s), Damping, Input bandwidth, Dry signal level (dB), Early reflection level (dB), Tail level (dB)", "LV2 Hard Limiter -elv2:http://plugin.org.uk/swh-plugins/hardLimiter, dB limit, Wet level, Residue level", "LV2 Harmonic generator -elv2:http://plugin.org.uk/swh-plugins/harmonicGen, Fundamental magnitude, 2nd harmonic magnitude, 3rd harmonic magnitude, 4th harmonic magnitude, 5th harmonic magnitude, 6th harmonic magnitude, 7th harmonic magnitude, 8th harmonic magnitude, 9th harmonic magnitude, 10th harmonic magnitude", "LV2 Hermes Filter -elv2:http://plugin.org.uk/swh-plugins/hermesFilter, LFO1 freq (Hz), LFO1 wave (0 = sin, 1 = tri, 2 = saw, 3 = squ, 4 = s&h), LFO2 freq (Hz), LFO2 wave (0 = sin, 1 = tri, 2 = saw, 3 = squ, 4 = s&h), Osc1 freq (Hz), Osc1 wave (0 = sin, 1 = tri, 2 = saw, 3 = squ, 4 = noise), Osc2 freq (Hz), Osc2 wave (0 = sin, 1 = tri, 2 = saw, 3 = squ, 4 = noise), Ringmod 1 depth (0=none, 1=AM, 2=RM), Ringmod 2 depth (0=none, 1=AM, 2=RM), Ringmod 3 depth (0=none, 1=AM, 2=RM), Osc1 gain (dB), RM1 gain (dB), Osc2 gain (dB), RM2 gain (dB), Input gain (dB), RM3 gain (dB), Xover lower freq, Xover upper freq, Dist1 drive, Dist2 drive, Dist3 drive, Filt1 type (0=none, 1=LP, 2=HP, 3=BP, 4=BR, 5=AP), Filt1 freq, Filt1 q, Filt1 resonance, Filt1 LFO1 level, Filt1 LFO2 level, Filt2 type (0=none, 1=LP, 2=HP, 3=BP, 4=BR, 5=AP), Filt2 freq, Filt2 q, Filt2 resonance, Filt2 LFO1 level, Filt2 LFO2 level, Filt3 type (0=none, 1=LP, 2=HP, 3=BP, 4=BR, 5=AP), Filt3 freq, Filt3 q, Filt3 resonance, Filt3 LFO1 level, Filt3 LFO2 level, Delay1 length (s), Delay1 feedback, Delay1 wetness, Delay2 length (s), Delay2 feedback, Delay2 wetness, Delay3 length (s), Delay3 feedback, Delay3 wetness, Band 1 gain (dB), Band 2 gain (dB), Band 3 gain (dB)", "LV2 Glame Highpass Filter -elv2:http://plugin.org.uk/swh-plugins/highpass_iir, Cutoff Frequency, Stages(2 poles per stage)", "LV2 Hilbert transformer -elv2:http://plugin.org.uk/swh-plugins/hilbert, latency", "LV2 Non-bandlimited single-sample impulses -elv2:http://plugin.org.uk/swh-plugins/impulse_fc, Frequency (Hz)", "LV2 Inverter -elv2:http://plugin.org.uk/swh-plugins/inv, ", "LV2 Karaoke -elv2:http://plugin.org.uk/swh-plugins/karaoke, Vocal volume (dB)", "LV2 L/C/R Delay -elv2:http://plugin.org.uk/swh-plugins/lcrDelay, L delay (ms), L level, C delay (ms), C level, R delay (ms), R level, Feedback, High damp (%), Low damp (%), Spread, Dry/Wet level", "LV2 LFO Phaser -elv2:http://plugin.org.uk/swh-plugins/lfoPhaser, LFO rate (Hz), LFO depth, Feedback, Spread (octaves)", "LV2 Lookahead limiter -elv2:http://plugin.org.uk/swh-plugins/lookaheadLimiter, Limit (dB), Lookahead delay, Attenuation (dB), latency", "LV2 Lookahead limiter (fixed latency) -elv2:http://plugin.org.uk/swh-plugins/lookaheadLimiterConst, Limit (dB), Lookahead time (s), Attenuation (dB), latency", "LV2 Glame Lowpass Filter -elv2:http://plugin.org.uk/swh-plugins/lowpass_iir, Cutoff Frequency, Stages(2 poles per stage)", "LV2 LS Filter -elv2:http://plugin.org.uk/swh-plugins/lsFilter, Filter type (0=LP, 1=BP, 2=HP), Cutoff frequency (Hz), Resonance", "LV2 Matrix: MS to Stereo -elv2:http://plugin.org.uk/swh-plugins/matrixMSSt, Width", "LV2 Matrix Spatialiser -elv2:http://plugin.org.uk/swh-plugins/matrixSpatialiser, Width", "LV2 Matrix: Stereo to MS -elv2:http://plugin.org.uk/swh-plugins/matrixStMS, ", "LV2 Multiband EQ -elv2:http://plugin.org.uk/swh-plugins/mbeq, 50Hz gain (low shelving), 100Hz gain, 156Hz gain, 220Hz gain, 311Hz gain, 440Hz gain, 622Hz gain, 880Hz gain, 1250Hz gain, 1750Hz gain, 2500Hz gain, 3500Hz gain, 5000Hz gain, 10000Hz gain, 20000Hz gain, latency", "LV2 Modulatable delay -elv2:http://plugin.org.uk/swh-plugins/modDelay, Base delay (s)", "LV2 Multivoice Chorus -elv2:http://plugin.org.uk/swh-plugins/multivoiceChorus, Number of voices, Delay base (ms), Voice separation (ms), Detune (%), LFO frequency (Hz), Output attenuation (dB)", "LV2 Higher Quality Pitch Scaler -elv2:http://plugin.org.uk/swh-plugins/pitchScaleHQ, Pitch co-efficient, latency", "LV2 Plate reverb -elv2:http://plugin.org.uk/swh-plugins/plate, Reverb time, Damping, Dry/wet mix", "LV2 Pointer cast distortion -elv2:http://plugin.org.uk/swh-plugins/pointerCastDistortion, Effect cutoff freq (Hz), Dry/wet mix", "LV2 Rate shifter -elv2:http://plugin.org.uk/swh-plugins/rateShifter, Rate", "LV2 Retro Flanger -elv2:http://plugin.org.uk/swh-plugins/retroFlange, Average stall (ms), Flange frequency (Hz)", "LV2 Reverse Delay (5s max) -elv2:http://plugin.org.uk/swh-plugins/revdelay, Delay Time (s), Dry Level (dB), Wet Level (dB), Feedback, Crossfade samples", "LV2 Ringmod with LFO -elv2:http://plugin.org.uk/swh-plugins/ringmod_1i1o1l, Modulation depth (0=none, 1=AM, 2=RM), Frequency (Hz), Sine level, Triangle level, Sawtooth level, Square level", "LV2 Ringmod with two inputs -elv2:http://plugin.org.uk/swh-plugins/ringmod_2i1o, Modulation depth (0=none, 1=AM, 2=RM)", "LV2 Barrys Satan Maximiser -elv2:http://plugin.org.uk/swh-plugins/satanMaximiser, Decay time (samples), Knee point (dB)", "LV2 SC1 -elv2:http://plugin.org.uk/swh-plugins/sc1, Attack time (ms), Release time (ms), Threshold level (dB), Ratio (1:n), Knee radius (dB), Makeup gain (dB)", "LV2 SC2 -elv2:http://plugin.org.uk/swh-plugins/sc2, Attack time (ms), Release time (ms), Threshold level (dB), Ratio (1:n), Knee radius (dB), Makeup gain (dB)", "LV2 SC3 -elv2:http://plugin.org.uk/swh-plugins/sc3, Attack time (ms), Release time (ms), Threshold level (dB), Ratio (1:n), Knee radius (dB), Makeup gain (dB), Chain balance", "LV2 SC4 -elv2:http://plugin.org.uk/swh-plugins/sc4, RMS/peak, Attack time (ms), Release time (ms), Threshold level (dB), Ratio (1:n), Knee radius (dB), Makeup gain (dB), Amplitude (dB), Gain reduction (dB)", "LV2 SE4 -elv2:http://plugin.org.uk/swh-plugins/se4, RMS/peak, Attack time (ms), Release time (ms), Threshold level (dB), Ratio (1:n), Knee radius (dB), Attenuation (dB), Amplitude (dB), Gain expansion (dB)", "LV2 Wave shaper -elv2:http://plugin.org.uk/swh-plugins/shaper, Waveshape", "LV2 Signal sifter -elv2:http://plugin.org.uk/swh-plugins/sifter, Sift size", "LV2 Sine + cosine oscillator -elv2:http://plugin.org.uk/swh-plugins/sinCos, Base frequency (Hz), Pitch offset", "LV2 Single band parametric -elv2:http://plugin.org.uk/swh-plugins/singlePara, Gain (dB), Frequency (Hz), Bandwidth (octaves)", "LV2 Sinus wavewrapper -elv2:http://plugin.org.uk/swh-plugins/sinusWavewrapper, Wrap degree", "LV2 Smooth Decimator -elv2:http://plugin.org.uk/swh-plugins/smoothDecimate, Resample rate, Smoothing", "LV2 Mono to Stereo splitter -elv2:http://plugin.org.uk/swh-plugins/split, ", "LV2 Surround matrix encoder -elv2:http://plugin.org.uk/swh-plugins/surroundEncoder, ", "LV2 State Variable Filter -elv2:http://plugin.org.uk/swh-plugins/svf, Filter type (0=none, 1=LP, 2=HP, 3=BP, 4=BR, 5=AP), Filter freq, Filter Q, Filter resonance", "LV2 Tape Delay Simulation -elv2:http://plugin.org.uk/swh-plugins/tapeDelay, Tape speed (inches/sec, 1=normal), Dry level (dB), Tap 1 distance (inches), Tap 1 level (dB), Tap 2 distance (inches), Tap 2 level (dB), Tap 3 distance (inches), Tap 3 level (dB), Tap 4 distance (inches), Tap 4 level (dB)", "LV2 Transient mangler -elv2:http://plugin.org.uk/swh-plugins/transient, Attack speed, Sustain time", "LV2 Triple band parametric with shelves -elv2:http://plugin.org.uk/swh-plugins/triplePara, Low-shelving gain (dB), Low-shelving frequency (Hz), Low-shelving slope, Band 1 gain (dB), Band 1 frequency (Hz), Band 1 bandwidth (octaves), Band 2 gain (dB), Band 2 frequency (Hz), Band 2 bandwidth (octaves), Band 3 gain (dB), Band 3 frequency (Hz), Band 3 bandwidth (octaves), High-shelving gain (dB), High-shelving frequency (Hz), High-shelving slope", "LV2 μ-Law Compressor -elv2:http://plugin.org.uk/swh-plugins/ulaw, ", "LV2 Valve saturation -elv2:http://plugin.org.uk/swh-plugins/valve, Distortion level, Distortion character", "LV2 Valve rectifier -elv2:http://plugin.org.uk/swh-plugins/valveRect, Sag level, Distortion", "LV2 VyNil (Vinyl Effect) -elv2:http://plugin.org.uk/swh-plugins/vynil, Year, RPM, Surface warping, Crackle, Wear", "LV2 Wave Terrain Oscillator -elv2:http://plugin.org.uk/swh-plugins/waveTerrain, ", "LV2 Crossfade -elv2:http://plugin.org.uk/swh-plugins/xfade, Crossfade", "LV2 Crossfade (4 outs) -elv2:http://plugin.org.uk/swh-plugins/xfade4, Crossfade", "LV2 z-1 -elv2:http://plugin.org.uk/swh-plugins/zm1, ", "dyn_compress_brutal, -pn:dyn_compress_brutal:gain-%\n", "dyn_compress_hard, -pn:dyn_compress_hard:gain-%\n", "dyn_compress_infinite, -pn:dyn_compress_infinite:gain-%\n", "dyn_compress_medium, -pn:dyn_compress_medium:gain-%\n", "dyn_compress_soft, -pn:dyn_compress_soft:gain-%\n", "dyn_compress_supersoft, -pn:dyn_compress_supersoft:gain-%\n", "eq_template, -pn:eq_template:10hz, 40hz, 100hz, 220hz, 460hz, 940hz, 1900hz, 3800hz, 7620hz, 15300hz\n", "eq_template2, -pn:eq_template2:1000hz, 4000hz\n", "f_bandpass, -pn:f_bandpass:freq, width\n", "f_filtertest, -pn:f_filtertest:freq1, freq2\n", "f_high_and_low, -pn:f_high_and_low\n", "f_highpass, -pn:f_highpass\n", "f_inverse_comb, -pn:f_inverse_comb\n", "f_lowp_sine, -pn:f_lowp_sine\n", "f_lowp_sine2, -pn:f_lowp_sine2\n", "f_lowpass, -pn:f_lowpass:freq_hz\n", "f_rejectband, -pn:f_rejectband\n", "f_res_bandpass, -pn:f_res_bandpass:freq, width\n", "f_res_lowpass, -pn:f_res_lowpass:arg-1\n", "f_resonator, -pn:f_resonator\n", "f_two_filters, -pn:f_two_filters:lowgain, highgain\n", "f_two_filters_pareq, -pn:f_two_filters_pareq:lowfreq, lowgain, highfreq, highgain\n", "gate_crop, -pn:gate_crop\n", "gate_noisegate_1, -pn:gate_noisegate_1\n", "gate_noisegate_delanalog, -pn:gate_noisegate_delanalog\n", "gate_threshold, -pn:gate_threshold\n", "lad_hermes, -pn:lad_hermes:p1, p2, p3, p4\n", "lad_metronome, -pn:lad_metronome:bpm\n", "lad_oscillator_stack, -pn:lad_oscillator_stack:freq, osctype1, osctype2, gain1, gain2\n", "lad_oscillator_test, -pn:lad_oscillator_test:freq, gain1\n", "lad_sc4, -pn:lad_sc4:output-amplitude-dB, output-gain-reduction-dB\n", "lad_sc4_rg, -pn:lad_sc4_rg:ratio, gain-dB, output-amplitude-dB, output-gain-reduction-dB\n", "metronome, -pn:metronome:bpm\n", "time_chorus1, -pn:time_chorus1\n", "time_delay1, -pn:time_delay1\n", "time_delay2, -pn:time_delay2\n", "time_flanger1, -pn:time_flanger1\n", "time_phaser1, -pn:time_phaser1\n", "time_reverb1, -pn:time_reverb1\n", "time_reverb2, -pn:time_reverb2\n", "time_reverb3, -pn:time_reverb3\n", "time_reverb4, -pn:time_reverb4\n", "time_wicked_dub, -pn:time_wicked_dub\n", "var_aw, -pn:var_aw:speed\n", "var_aw_custom, -pn:var_aw_custom:low, high, speed\n", "var_aw_ksv, -pn:var_aw_ksv\n", "var_aw_tri, -pn:var_aw_tri:speed\n", "var_aw_tri_custom, -pn:var_aw_tri_custom:speed\n", "var_chipmunk, -pn:var_chipmunk\n", "var_dali, -pn:var_dali\n", "var_molten_tape, -pn:var_molten_tape\n", "var_paralmadness, -pn:var_paralmadness:freq1, freq2, freq3\n", "var_parchip, -pn:var_parchip:pitch, modfreq\n", "var_stretched_tape, -pn:var_stretched_tape\n", "var_sweeping_pan, -pn:var_sweeping_pan:speed_hz\n", "var_switching_pan, -pn:var_switching_pan:speed_hz\n", ". Diode Processor -el:diode, Mode (0 for none, 1 for half wave, 2 for full wave)\n", ". White Noise Source -el:noise_white, Amplitude\n", ". TAP Reflector -el:tap_reflector, Fragment Length [ms], Dry Level [dB], Wet Level [dB]\n", ". TAP Pink/Fractal Noise -el:tap_pinknoise, Fractal Dimension, Signal Level [dB], Noise Level [dB]\n", ". AMB order 1, 1 cube decoder -el:Ambisonics-11-cube-decoder, Shelf filt, HF XYZ gain, LF XYZ gain, Shelf freq, Distance\n", ". AMB order 1, 1 hexagon decoder -el:Ambisonics-11-hexagon-decoder, Front spkr, Shelf filt, HF XY gain, LF XY gain, Shelf freq, Distance\n", ". AMB order 1, 1 square decoder -el:Ambisonics-11-square-decoder, Front spkr, Shelf filt, HF XY gain, LF XY gain, Shelf freq, Distance\n", ". AMB order 1, 1 rotator -el:Ambisonics-11-rotator, Angle\n", ". AMB order 1, 1 stereo panner -el:Ambisonics-11-stereo-panner, Elevation, Width, Azimuth\n", ". AMB order 1, 1 mono panner -el:Ambisonics-11-mono-panner, Elevation, Azimuth\n", ". Crossover distortion -el:crossoverDist, Crossover amplitude, Smoothing\n", ". Higher Quality Pitch Scaler -el:pitchScaleHQ, Pitch co-efficient, latency\n", ". UHJ Decoder -el:UHJ-decoder", ". UHJ Encoder -el:UHJ-encoder", ". Virtual stereo microphone -el:Virtualmic, Elevation, Azimuth, Angle, Polar\n", ". Three cardioids to AMB matrix -el:Tricardioid-to-AMB", ". Mag's Notch Filter -el:notch_iir, Center Frequency (Hz), Bandwidth (Hz), Stages(2 poles per stage)\n", ". Signal sifter -el:sifter, Sift size\n", ". Sine + cosine oscillator -el:sinCos, Base frequency (Hz), Pitch offset\n", ". TAP TubeWarmth -el:tap_tubewarmth, Drive, Tape--Tube Blend\n", ". Allpass delay line, cubic spline interpolation -el:allpass_c, Max Delay (s), Delay Time (s), Decay Time (s)\n", ". Allpass delay line, linear interpolation -el:allpass_l, Max Delay (s), Delay Time (s), Decay Time (s)\n", ". Allpass delay line, noninterpolating -el:allpass_n, Max Delay (s), Delay Time (s), Decay Time (s)\n", ". SC2 -el:sc2, Attack time (ms), Release time (ms), Threshold level (dB), Ratio (1:n), Knee radius (dB), Makeup gain (dB)\n", ". Comb Splitter -el:combSplitter, Band separation (Hz)\n", ". Modulatable delay -el:modDelay, Base delay (s)\n", ". Audio Divider (Suboctave Generator) -el:divider, Denominator\n", ". Surround matrix encoder -el:surroundEncoder", ". Chebyshev distortion -el:chebstortion, Distortion\n", ". Bode frequency shifter (CV) -el:bodeShifterCV, Base shift, Mix (-1=down, +1=up), CV Attenuation, latency\n", ". TAP DeEsser -el:tap_deesser, Threshold Level [dB], Frequency [Hz], Sidechain Filter, Monitor, Attenuation [dB]\n", ". Wave shaper -el:shaper, Waveshape\n", ". Tape Delay Simulation -el:tapeDelay, Tape speed (inches/sec, 1=normal), Dry level (dB), Tap 1 distance (inches), Tap 1 level (dB), Tap 2 distance (inches), Tap 2 level (dB), Tap 3 distance (inches), Tap 3 level (dB), Tap 4 distance (inches), Tap 4 level (dB)\n", ". Comb Filter -el:comb, Band separation (Hz), Feedback\n", ". Ringmod with LFO -el:ringmod_1i1o1l, Modulation depth (0=none, 1=AM, 2=RM), Frequency (Hz), Sine level, Triangle level, Sawtooth level, Square level\n", ". Ringmod with two inputs -el:ringmod_2i1o, Modulation depth (0=none, 1=AM, 2=RM)\n", ". Simple High Pass Filter -el:hpf, Cutoff Frequency (Hz)\n", ". Simple Low Pass Filter -el:lpf, Cutoff Frequency (Hz)\n", ". TAP Dynamics (St) -el:tap_dynamics_st, Attack [ms], Release [ms], Offset Gain [dB], Makeup Gain [dB], Envelope Volume (L) [dB], Envelope Volume (R) [dB], Gain Adjustment (L) [dB], Gain Adjustment (R) [dB], Stereo Mode, Function\n", ". Crossfade (4 outs) -el:xfade4, Crossfade\n", ". Crossfade -el:xfade, Crossfade\n", ". TAP Dynamics (M) -el:tap_dynamics_m, Attack [ms], Release [ms], Offset Gain [dB], Makeup Gain [dB], Envelope Volume [dB], Gain Adjustment [dB], Function\n", ". Triple band parametric with shelves -el:triplePara, Low-shelving gain (dB), Low-shelving frequency (Hz), Low-shelving slope, Band 1 gain (dB), Band 1 frequency (Hz), Band 1 bandwidth (octaves), Band 2 gain (dB), Band 2 frequency (Hz), Band 2 bandwidth (octaves), Band 3 gain (dB), Band 3 frequency (Hz), Band 3 bandwidth (octaves), High-shelving gain (dB), High-shelving frequency (Hz), High-shelving slope\n", ". Plate reverb -el:plate, Reverb time, Damping, Dry/wet mix\n", ". Mono to Stereo splitter -el:split", ". TAP Reverberator -el:tap_reverb, Decay [ms], Dry Level [dB], Wet Level [dB], Comb Filters, Allpass Filters, Bandpass Filter, Enhanced Stereo, Reverb Type\n", ". Flanger -el:flanger, Delay base (ms), Max slowdown (ms), LFO frequency (Hz), Feedback\n", ". TAP AutoPanner -el:tap_autopan, Frequency [Hz], Depth [%], Gain [dB]\n", ". zita-reverb-amb -el:zita-reverb-amb, Delay, Xover, RT-low, RT-mid, Damping, F1-freq, F1-gain, F2-freq, F2-gain, XYZ gain\n", ". zita-reverb -el:zita-reverb, Delay, Xover, RT-low, RT-mid, Damping, F1-freq, F1-gain, F2-freq, F2-gain, Output mix\n", ". Stereo reverb -el:G2reverb, Room size, Reverb time, Input BW, Damping, Dry sound, Reflections, Reverb tail\n", ". Pitch Scaler -el:pitchScale, Pitch co-efficient, latency\n", ". Glame Highpass Filter -el:highpass_iir, Cutoff Frequency, Stages(2 poles per stage)\n", ". Impulse convolver -el:imp, Impulse ID, High latency mode, Gain (dB), latency\n", ". :: Invada :: ER Reverb - Sum L+R In -el:invada_sum_reverbER_module_0_1, Room Length, Room Width, Room Height, Source (L/R), Source (F/B), Listener (L/R), Listener (F/B), HPF (Hz), Warmth, Diffusion\n", ". :: Invada :: ER Reverb - Mono In -el:invada_mono_reverbER_module_0_1, Room Length, Room Width, Room Height, Source (L/R), Source (F/B), Listener (L/R), Listener (F/B), HPF (Hz), Warmth, Diffusion\n", ". Matrix Spatialiser -el:matrixSpatialiser, Width\n", ". LS Filter -el:lsFilter, Filter type (0=LP, 1=BP, 2=HP), Cutoff frequency (Hz), Resonance\n", ". Hard Limiter -el:hardLimiter, dB limit, Wet level, Residue level\n", ". Foldover distortion -el:foldover, Drive, Skew\n", ". SC1 -el:sc1, Attack time (ms), Release time (ms), Threshold level (dB), Ratio (1:n), Knee radius (dB), Makeup gain (dB)\n", ". AMB order 3, 3 rotator -el:Ambisonics-33-rotator, Angle\n", ". AMB order 3, 3 panner -el:Ambisonics-33-panner, Elevation, Azimuth\n", ". AMB order 3, 1 rotator -el:Ambisonics-31-rotator, Angle\n", ". AMB order 3, 1 panner -el:Ambisonics-31-panner, Elevation, Azimuth\n", ". DC Offset Remover -el:dcRemove", ". Karaoke -el:karaoke, Vocal volume (dB)\n", ". Reverse Delay (5s max) -el:revdelay, Delay Time (s), Dry Level (dB), Wet Level (dB), Feedback, Crossfade samples\n", ". GSM simulator -el:gsm, Dry/wet mix, Number of passes, Error rate (bits/block), latency\n", ". TAP Equalizer/BW -el:tap_equalizer_bw, Band 1 Gain [dB], Band 2 Gain [dB], Band 3 Gain [dB], Band 4 Gain [dB], Band 5 Gain [dB], Band 6 Gain [dB], Band 7 Gain [dB], Band 8 Gain [dB], Band 1 Freq [Hz], Band 2 Freq [Hz], Band 3 Freq [Hz], Band 4 Freq [Hz], Band 5 Freq [Hz], Band 6 Freq [Hz], Band 7 Freq [Hz], Band 8 Freq [Hz], Band 1 Bandwidth [octaves], Band 2 Bandwidth [octaves], Band 3 Bandwidth [octaves], Band 4 Bandwidth [octaves], Band 5 Bandwidth [octaves], Band 6 Bandwidth [octaves], Band 7 Bandwidth [octaves], Band 8 Bandwidth [octaves]\n", ". Pointer cast distortion -el:pointerCastDistortion, Effect cutoff freq (Hz), Dry/wet mix\n", ". Frequency tracker -el:freqTracker, Tracking speed\n", ". GVerb -el:gverb, Roomsize (m), Reverb time (s), Damping, Input bandwidth, Dry signal level (dB), Early reflection level (dB), Tail level (dB)\n", ". Aliasing -el:alias, Aliasing level\n", ". Sinus wavewrapper -el:sinusWavewrapper, Wrap degree\n", ". Dyson compressor -el:dysonCompress, Peak limit (dB), Release time (s), Fast compression ratio, Compression ratio\n", ". DJ flanger -el:djFlanger, LFO sync, LFO period (s), LFO depth (ms), Feedback (%)\n", ". DJ EQ -el:dj_eq, Lo gain (dB), Mid gain (dB), Hi gain (dB), latency\n", ". DJ EQ (mono) -el:dj_eq_mono, Lo gain (dB), Mid gain (dB), Hi gain (dB), latency\n", ". Step Demuxer -el:stepMuxer, Crossfade time (in ms)\n", ". Gate -el:gate, LF key filter (Hz), HF key filter (Hz), Threshold (dB), Attack (ms), Hold (ms), Decay (ms), Range (dB), Output select (-1 = key listen, 0 = gate, 1 = bypass)\n", ". :: Invada :: Compressor - Stereo -el:invada_stereo_compressor_module_0_1, Tight / Sloppy, Attack (ms), Release (ms), Threshold (dB), Ratio, Gain (dB), Soft Clip, Gain Reduction\n", ". :: Invada :: Compressor - Mono -el:invada_mono_compressor_module_0_1, Tight / Sloppy, Attack (ms), Release (ms), Threshold (dB), Ratio, Gain (dB), Soft Clip, Gain Reduction\n", ". Gong model -el:gong, Inner damping, Outer damping, Mic position, Inner size 1, Inner stiffness 1 +, Inner stiffness 1 -, Inner size 2, Inner stiffness 2 +, Inner stiffness 2 -, Inner size 3, Inner stiffness 3 +, Inner stiffness 3 -, Inner size 4, Inner stiffness 4 +, Inner stiffness 4 -, Outer size 1, Outer stiffness 1 +, Outer stiffness 1 -, Outer size 2, Outer stiffness 2 +, Outer stiffness 2 -, Outer size 3, Outer stiffness 3 +, Outer stiffness 3 -, Outer size 4, Outer stiffness 4 +, Outer stiffness 4 -\n", ". TAP Fractal Doubler -el:tap_doubler, Time Tracking, Pitch Tracking, Dry Level [dB], Dry Left Position, Dry Right Position, Wet Level [dB], Wet Left Position, Wet Right Position\n", ". Valve rectifier -el:valveRect, Sag level, Distortion\n", ". AM pitchshifter -el:amPitchshift, Pitch shift, Buffer size, latency\n", ". SE4 -el:se4, RMS/peak, Attack time (ms), Release time (ms), Threshold level (dB), Ratio (1:n), Knee radius (dB), Attenuation (dB), Amplitude (dB), Gain expansion (dB)\n", ". Auto phaser -el:autoPhaser, Attack time (s), Decay time (s), Modulation depth, Feedback, Spread (octaves)\n", ". 4 x 4 pole allpass -el:fourByFourPole, Frequency 1, Feedback 1, Frequency 2, Feedback 2, Frequency 3, Feedback 3, Frequency 4, Feedback 4\n", ". LFO Phaser -el:lfoPhaser, LFO rate (Hz), LFO depth, Feedback, Spread (octaves)\n", ". Matrix: Stereo to MS -el:matrixStMS", ". Constant Signal Generator -el:const, Signal amplitude\n", ". Simple amplifier -el:amp, Amps gain (dB)\n", ". :: Invada :: Input Module -el:invada_stereo_input_module_0_1, Phase Reverse (Left), Phase Reverse (Right), Gain (dB), Pan (L-R), Width (M-S), Soft Clip\n", ". Rate shifter -el:rateShifter, Rate\n", ". Simple Delay Line -el:delay_5s, Delay (Seconds), Dry/Wet Balance\n", ". AMB order 2, 2 rotator -el:Ambisonics-22-rotator, Angle\n", ". AMB order 2, 2 panner -el:Ambisonics-22-panner, Elevation, Azimuth\n", ". AMB order 2, 1 rotator -el:Ambisonics-21-rotator, Angle\n", ". AMB order 2, 1 panner -el:Ambisonics-21-panner, Elevation, Azimuth\n", ". Giant flange -el:giantFlange, Double delay, LFO frequency 1 (Hz), Delay 1 range (s), LFO frequency 2 (Hz), Delay 2 range (s), Feedback, Dry/Wet level\n", ". State Variable Filter -el:svf, Filter type (0=none, 1=LP, 2=HP, 3=BP, 4=BR, 5=AP), Filter freq, Filter Q, Filter resonance\n", ". Multiband EQ -el:mbeq, 50Hz gain (low shelving), 100Hz gain, 156Hz gain, 220Hz gain, 311Hz gain, 440Hz gain, 622Hz gain, 880Hz gain, 1250Hz gain, 1750Hz gain, 2500Hz gain, 3500Hz gain, 5000Hz gain, 10000Hz gain, 20000Hz gain, latency\n", ". Decimator -el:decimator, Bit depth, Sample rate (Hz)\n", ". Single band parametric -el:singlePara, Gain (dB), Frequency (Hz), Bandwidth (octaves)\n", ". Barry's Satan Maximiser -el:satanMaximiser, Decay time (samples), Knee point (dB)\n", ". Artificial latency -el:artificialLatency, Delay (ms), latency\n", ". Glame Bandpass Filter -el:bandpass_iir, Center Frequency (Hz), Bandwidth (Hz), Stages(2 poles per stage)\n", ". Declipper -el:declip", ". :: Invada :: Tube - Stereo -el:invada_stereo_tube_module_0_1, Drive (dB), DC Offset, Phase, Wet/Dry Mix (%)\n", ". :: Invada :: Tube - Mono -el:invada_mono_tube_module_0_1, Drive (dB), DC Offset, Phase, Wet/Dry Mix (%)\n", ". Matrix: MS to Stereo -el:matrixMSSt, Width\n", ". TAP Equalizer -el:tap_equalizer, Band 1 Gain [dB], Band 2 Gain [dB], Band 3 Gain [dB], Band 4 Gain [dB], Band 5 Gain [dB], Band 6 Gain [dB], Band 7 Gain [dB], Band 8 Gain [dB], Band 1 Freq [Hz], Band 2 Freq [Hz], Band 3 Freq [Hz], Band 4 Freq [Hz], Band 5 Freq [Hz], Band 6 Freq [Hz], Band 7 Freq [Hz], Band 8 Freq [Hz]\n", ". 4-band parametric filter -el:Parametric1, Filter, Gain, Section 1, Frequency 1, Bandwidth 1, Gain 1, Section 2, Frequency 2, Bandwidth 2, Gain 2, Section 3, Frequency 3, Bandwidth 3, Gain 3, Section 4, Frequency 4, Bandwidth 4, Gain 4\n", ". Harmonic generator -el:harmonicGen, Fundamental magnitude, 2nd harmonic magnitude, 3rd harmonic magnitude, 4th harmonic magnitude, 5th harmonic magnitude, 6th harmonic magnitude, 7th harmonic magnitude, 8th harmonic magnitude, 9th harmonic magnitude, 10th harmonic magnitude\n", ". Retro Flanger -el:retroFlange, Average stall (ms), Flange frequency (Hz)\n", ". VyNil (Vinyl Effect) -el:vynil, Year, RPM, Surface warping, Crackle, Wear\n", ". TAP Chorus/Flanger -el:tap_chorusflanger, Frequency [Hz], L/R Phase Shift [deg], Depth [%], Delay [ms], Contour [Hz], Dry Level [dB], Wet Level [dB]\n", ". Smooth Decimator -el:smoothDecimate, Resample rate, Smoothing\n", ". TAP Stereo Echo -el:tap_stereo_echo, L Delay [ms], L Feedback [%], R/Haas Delay [ms], R/Haas Feedback [%], L Echo Level [dB], R Echo Level [dB], Dry Level [dB], Cross Mode, Haas Effect, Swap Outputs\n", ". Fractionally Addressed Delay Line -el:fadDelay, Delay (seconds), Feedback (dB)\n", ". Wave Terrain Oscillator -el:waveTerrain", ". Transient mangler -el:transient, Attack speed, Sustain time\n", ". C* CEO - Chief Executive Oscillator -el:CEO, ppm, volume, damping\n", ". C* Click - Metronome -el:Click, model, bpm, volume, damping\n", ". C* Fractal - Audio stream from deterministic chaos -el:Fractal, rate, mode, x, y, z, hp, volume\n", ". C* White - Noise generator -el:White, volume\n", ". C* Sin - Sine wave generator -el:Sin, f (Hz), volume\n", ". C* Narrower - Stereo image width reduction -el:Narrower, mode, strength\n", ". C* Wider - Stereo image synthesis -el:Wider, pan, width\n", ". C* Eq4p - 4-band parametric equaliser -el:Eq4p, a.mode, a.f (Hz), a.Q, a.gain (dB), b.mode, b.f (Hz), b.Q, b.gain (dB), c.mode, c.f (Hz), c.Q, c.gain (dB), d.mode, d.f (Hz), d.Q, d.gain (dB)\n", ". C* Eq10X2 - Stereo 10-band equaliser -el:Eq10X2, 31 Hz, 63 Hz, 125 Hz, 250 Hz, 500 Hz, 1 kHz, 2 kHz, 4 kHz, 8 kHz, 16 kHz\n", ". C* Eq10 - 10-band equaliser -el:Eq10, 31 Hz, 63 Hz, 125 Hz, 250 Hz, 500 Hz, 1 kHz, 2 kHz, 4 kHz, 8 kHz, 16 kHz\n", ". C* Scape - Stereo delay with chromatic resonances -el:Scape, bpm, divider, feedback, dry, blend, tune (Hz)\n", ". C* AutoFilter - Modulated filter cascade -el:AutoFilter, over, mode, filter, gain (dB), f (Hz), Q, range, lfo/env, rate\n", ". C* PhaserII - Mono phaser -el:PhaserII, rate, lfo, depth, spread, resonance\n", ". C* ChorusI - Mono chorus/flanger -el:ChorusI, t (ms), width (ms), rate (Hz), blend, feedforward, feedback\n", ". C* SpiceX2 - Not an exciter either -el:SpiceX2, lo.f (Hz), lo.compress, lo.gain, hi.f (Hz), hi.gain\n", ". C* Spice - Not an exciter -el:Spice, lo.f (Hz), lo.compress, lo.gain, hi.f (Hz), hi.gain\n", ". C* Saturate - Various overdrive models, 8x oversampled -el:Saturate, mode, gain (dB), bias\n", ". C* PlateX2 - Versatile plate reverb, stereo inputs -el:PlateX2, bandwidth, tail, damping, blend\n", ". C* Plate - Versatile plate reverb -el:Plate, bandwidth, tail, damping, blend\n", ". C* CabinetIV - Idealised loudspeaker cabinet -el:CabinetIV, model, gain (dB)\n", ". C* AmpVTS - Idealised guitar amplification -el:AmpVTS, over, gain, bright, power, tonestack, bass, mid, treble, attack, squash, lowcut\n", ". C* ToneStack - Classic amplifier tone stack emulation -el:ToneStack, model, bass, mid, treble\n", ". C* CompressX2 - Stereo compressor and saturating limiter -el:CompressX2, measure, mode, threshold, strength, attack, release, gain (dB)\n", ". C* Compress - Compressor and saturating limiter -el:Compress, measure, mode, threshold, strength, attack, release, gain (dB)\n", ". C* NoiseGate - Attenuate hum and noise -el:NoiseGate, open (dB), attack (ms), close (dB), mains (Hz)\n", ". TAP Pitch Shifter -el:tap_pitch, Semitone Shift, Rate Shift [%], Dry Level [dB], Wet Level [dB], latency\n", ". Glame Lowpass Filter -el:lowpass_iir, Cutoff Frequency, Stages(2 poles per stage)\n", ". Nonbandlimited single-sample impulses (Frequency: Control) -el:impulse_fc, Frequency (Hz)\n", ". Fast Lookahead limiter -el:fastLookaheadLimiter, Input gain (dB), Limit (dB), Release time (s), Attenuation (dB), latency\n", ". TAP Vibrato -el:tap_vibrato, Frequency [Hz], Depth [%], Dry Level [dB], Wet Level [dB], latency\n", ". Simple delay line, cubic spline interpolation -el:delay_c, Max Delay (s), Delay Time (s)\n", ". Simple delay line, linear interpolation -el:delay_l, Max Delay (s), Delay Time (s)\n", ". Simple delay line, noninterpolating -el:delay_n, Max Delay (s), Delay Time (s)\n", ". Gong beater -el:gongBeater, Impulse gain (dB), Strike gain (dB), Strike duration (s)\n", ". SC4 mono -el:sc4m, RMS/peak, Attack time (ms), Release time (ms), Threshold level (dB), Ratio (1:n), Knee radius (dB), Makeup gain (dB), Amplitude (dB), Gain reduction (dB)\n", ". FM Oscillator -el:fmOsc, Waveform (1=sin, 2=tri, 3=squ, 4=saw)\n", ". Multivoice Chorus -el:multivoiceChorus, Number of voices, Delay base (ms), Voice separation (ms), Detune (%), LFO frequency (Hz), Output attenuation (dB)\n", ". z-1 -el:zm1", ". TAP Rotary Speaker -el:tap_rotspeak, Rotor Frequency [Hz], Horn Frequency [Hz], Mic Distance [%], Rotor/Horn Mix, latency\n", ". TAP Scaling Limiter -el:tap_limiter, Limit Level [dB], Output Volume [dB], latency\n", ". Sine Oscillator (Freq:control, Amp:control) -el:sine_fcac, Frequency (Hz), Amplitude\n", ". Sine Oscillator (Freq:control, Amp:audio) -el:sine_fcaa, Frequency (Hz)\n", ". Sine Oscillator (Freq:audio, Amp:control) -el:sine_faac, Amplitude\n", ". Sine Oscillator (Freq:audio, Amp:audio) -el:sine_faaa", ". Bode frequency shifter -el:bodeShifter, Frequency shift, latency\n", ". Hermes Filter -el:hermesFilter, LFO1 freq (Hz), LFO1 wave (0 = sin, 1 = tri, 2 = saw, 3 = squ, 4 = s&h), LFO2 freq (Hz), LFO2 wave (0 = sin, 1 = tri, 2 = saw, 3 = squ, 4 = s&h), Osc1 freq (Hz), Osc1 wave (0 = sin, 1 = tri, 2 = saw, 3 = squ, 4 = noise), Osc2 freq (Hz), Osc2 wave (0 = sin, 1 = tri, 2 = saw, 3 = squ, 4 = noise), Ringmod 1 depth (0=none, 1=AM, 2=RM), Ringmod 2 depth (0=none, 1=AM, 2=RM), Ringmod 3 depth (0=none, 1=AM, 2=RM), Osc1 gain (dB), RM1 gain (dB), Osc2 gain (dB), RM2 gain (dB), Input gain (dB), RM3 gain (dB), Xover lower freq, Xover upper freq, Dist1 drive, Dist2 drive, Dist3 drive, Filt1 type (0=none, 1=LP, 2=HP, 3=BP, 4=BR, 5=AP), Filt1 freq, Filt1 q, Filt1 resonance, Filt1 LFO1 level, Filt1 LFO2 level, Filt2 type (0=none, 1=LP, 2=HP, 3=BP, 4=BR, 5=AP), Filt2 freq, Filt2 q, Filt2 resonance, Filt2 LFO1 level, Filt2 LFO2 level, Filt3 type (0=none, 1=LP, 2=HP, 3=BP, 4=BR, 5=AP), Filt3 freq, Filt3 q, Filt3 resonance, Filt3 LFO1 level, Filt3 LFO2 level, Delay1 length (s), Delay1 feedback, Delay1 wetness, Delay2 length (s), Delay2 feedback, Delay2 wetness, Delay3 length (s), Delay3 feedback, Delay3 wetness, Band 1 gain (dB), Band 2 gain (dB), Band 3 gain (dB)\n", ". SC3 -el:sc3, Attack time (ms), Release time (ms), Threshold level (dB), Ratio (1:n), Knee radius (dB), Makeup gain (dB), Chain balance\n", ". Comb delay line, cubic spline interpolation -el:comb_c, Max Delay (s), Delay Time (s), Decay Time (s)\n", ". Comb delay line, linear interpolation -el:comb_l, Max Delay (s), Delay Time (s), Decay Time (s)\n", ". Comb delay line, noninterpolating -el:comb_n, Max Delay (s), Delay Time (s), Decay Time (s)\n", ". Delayorama -el:delayorama, Random seed, Input gain (dB), Feedback (%), Number of taps, First delay (s), Delay range (s), Delay change, Delay random (%), Amplitude change, Amplitude random (%), Dry/wet mix\n", ". Fast overdrive -el:foverdrive, Drive level\n", ". GLAME Butterworth Highpass -el:butthigh_iir, Cutoff Frequency (Hz), Resonance\n", ". GLAME Butterworth Lowpass -el:buttlow_iir, Cutoff Frequency (Hz), Resonance\n", ". Glame Butterworth X-over Filter -el:bwxover_iir, Cutoff Frequency (Hz), Resonance\n", ". Exponential signal decay -el:decay, Decay Time (s)\n", ". TAP Sigmoid Booster -el:tap_sigmoid, Pre Gain [dB], Post Gain [dB]\n", ". L/C/R Delay -el:lcrDelay, L delay (ms), L level, C delay (ms), C level, R delay (ms), R level, Feedback, High damp (%), Low damp (%), Spread, Dry/Wet level\n", ". Stereo Amplifier -el:amp_stereo, Gain\n", ". Mono Amplifier -el:amp_mono, Gain\n", ". SC4 -el:sc4, RMS/peak, Attack time (ms), Release time (ms), Threshold level (dB), Ratio (1:n), Knee radius (dB), Makeup gain (dB), Amplitude (dB), Gain reduction (dB)\n", ". Valve saturation -el:valve, Distortion level, Distortion character\n", ". TAP Tremolo -el:tap_tremolo, Frequency [Hz], Depth [%], Gain [dB]\n", ". Inverter -el:inv", ". Analogue Oscillator -el:analogueOsc, Waveform (1=sin, 2=tri, 3=squ, 4=saw), Frequency (Hz), Warmth, Instability\n", ". :: Invada :: Filter - High Pass Stereo -el:invada_hp_stereo_filter_module_0_1, Frequency (Hz), Gain (dB), Soft Clip\n", ". :: Invada :: Filter - Low Pass Stereo -el:invada_lp_stereo_filter_module_0_1, Frequency (Hz), Gain (dB), Soft Clip\n", ". :: Invada :: Filter - High Pass Mono -el:invada_hp_mono_filter_module_0_1, Frequency (Hz), Gain (dB), Soft Clip\n", ". :: Invada :: Filter - Low Pass Mono -el:invada_lp_mono_filter_module_0_1, Frequency (Hz), Gain (dB), Soft Clip\n", ". Hilbert transformer -el:hilbert, latency\n", ". Glame Bandpass Analog Filter -el:bandpass_a_iir, Center Frequency (Hz), Bandwidth (Hz)\n" ] } } Audio-Nama-1.216/inc/0000755000175000017500000000000013544212627013253 5ustar jrothjrothAudio-Nama-1.216/inc/Module/0000755000175000017500000000000013544212627014500 5ustar jrothjrothAudio-Nama-1.216/inc/Module/Install.pm0000644000175000017500000002714513412473751016456 0ustar jrothjroth#line 1 package Module::Install; # For any maintainers: # The load order for Module::Install is a bit magic. # It goes something like this... # # IF ( host has Module::Install installed, creating author mode ) { # 1. Makefile.PL calls "use inc::Module::Install" # 2. $INC{inc/Module/Install.pm} set to installed version of inc::Module::Install # 3. The installed version of inc::Module::Install loads # 4. inc::Module::Install calls "require Module::Install" # 5. The ./inc/ version of Module::Install loads # } ELSE { # 1. Makefile.PL calls "use inc::Module::Install" # 2. $INC{inc/Module/Install.pm} set to ./inc/ version of Module::Install # 3. The ./inc/ version of Module::Install loads # } use 5.006; use strict 'vars'; use Cwd (); use File::Find (); use File::Path (); use vars qw{$VERSION $MAIN}; BEGIN { # All Module::Install core packages now require synchronised versions. # This will be used to ensure we don't accidentally load old or # different versions of modules. # This is not enforced yet, but will be some time in the next few # releases once we can make sure it won't clash with custom # Module::Install extensions. $VERSION = '1.17'; # Storage for the pseudo-singleton $MAIN = undef; *inc::Module::Install::VERSION = *VERSION; @inc::Module::Install::ISA = __PACKAGE__; } sub import { my $class = shift; my $self = $class->new(@_); my $who = $self->_caller; #------------------------------------------------------------- # all of the following checks should be included in import(), # to allow "eval 'require Module::Install; 1' to test # installation of Module::Install. (RT #51267) #------------------------------------------------------------- # Whether or not inc::Module::Install is actually loaded, the # $INC{inc/Module/Install.pm} is what will still get set as long as # the caller loaded module this in the documented manner. # If not set, the caller may NOT have loaded the bundled version, and thus # they may not have a MI version that works with the Makefile.PL. This would # result in false errors or unexpected behaviour. And we don't want that. my $file = join( '/', 'inc', split /::/, __PACKAGE__ ) . '.pm'; unless ( $INC{$file} ) { die <<"END_DIE" } Please invoke ${\__PACKAGE__} with: use inc::${\__PACKAGE__}; not: use ${\__PACKAGE__}; END_DIE # This reportedly fixes a rare Win32 UTC file time issue, but # as this is a non-cross-platform XS module not in the core, # we shouldn't really depend on it. See RT #24194 for detail. # (Also, this module only supports Perl 5.6 and above). eval "use Win32::UTCFileTime" if $^O eq 'MSWin32' && $] >= 5.006; # If the script that is loading Module::Install is from the future, # then make will detect this and cause it to re-run over and over # again. This is bad. Rather than taking action to touch it (which # is unreliable on some platforms and requires write permissions) # for now we should catch this and refuse to run. if ( -f $0 ) { my $s = (stat($0))[9]; # If the modification time is only slightly in the future, # sleep briefly to remove the problem. my $a = $s - time; if ( $a > 0 and $a < 5 ) { sleep 5 } # Too far in the future, throw an error. my $t = time; if ( $s > $t ) { die <<"END_DIE" } Your installer $0 has a modification time in the future ($s > $t). This is known to create infinite loops in make. Please correct this, then run $0 again. END_DIE } # Build.PL was formerly supported, but no longer is due to excessive # difficulty in implementing every single feature twice. if ( $0 =~ /Build.PL$/i ) { die <<"END_DIE" } Module::Install no longer supports Build.PL. It was impossible to maintain duel backends, and has been deprecated. Please remove all Build.PL files and only use the Makefile.PL installer. END_DIE #------------------------------------------------------------- # To save some more typing in Module::Install installers, every... # use inc::Module::Install # ...also acts as an implicit use strict. $^H |= strict::bits(qw(refs subs vars)); #------------------------------------------------------------- unless ( -f $self->{file} ) { foreach my $key (keys %INC) { delete $INC{$key} if $key =~ /Module\/Install/; } local $^W; require "$self->{path}/$self->{dispatch}.pm"; File::Path::mkpath("$self->{prefix}/$self->{author}"); $self->{admin} = "$self->{name}::$self->{dispatch}"->new( _top => $self ); $self->{admin}->init; @_ = ($class, _self => $self); goto &{"$self->{name}::import"}; } local $^W; *{"${who}::AUTOLOAD"} = $self->autoload; $self->preload; # Unregister loader and worker packages so subdirs can use them again delete $INC{'inc/Module/Install.pm'}; delete $INC{'Module/Install.pm'}; # Save to the singleton $MAIN = $self; return 1; } sub autoload { my $self = shift; my $who = $self->_caller; my $cwd = Cwd::getcwd(); my $sym = "${who}::AUTOLOAD"; $sym->{$cwd} = sub { my $pwd = Cwd::getcwd(); if ( my $code = $sym->{$pwd} ) { # Delegate back to parent dirs goto &$code unless $cwd eq $pwd; } unless ($$sym =~ s/([^:]+)$//) { # XXX: it looks like we can't retrieve the missing function # via $$sym (usually $main::AUTOLOAD) in this case. # I'm still wondering if we should slurp Makefile.PL to # get some context or not ... my ($package, $file, $line) = caller; die <<"EOT"; Unknown function is found at $file line $line. Execution of $file aborted due to runtime errors. If you're a contributor to a project, you may need to install some Module::Install extensions from CPAN (or other repository). If you're a user of a module, please contact the author. EOT } my $method = $1; if ( uc($method) eq $method ) { # Do nothing return; } elsif ( $method =~ /^_/ and $self->can($method) ) { # Dispatch to the root M:I class return $self->$method(@_); } # Dispatch to the appropriate plugin unshift @_, ( $self, $1 ); goto &{$self->can('call')}; }; } sub preload { my $self = shift; unless ( $self->{extensions} ) { $self->load_extensions( "$self->{prefix}/$self->{path}", $self ); } my @exts = @{$self->{extensions}}; unless ( @exts ) { @exts = $self->{admin}->load_all_extensions; } my %seen; foreach my $obj ( @exts ) { while (my ($method, $glob) = each %{ref($obj) . '::'}) { next unless $obj->can($method); next if $method =~ /^_/; next if $method eq uc($method); $seen{$method}++; } } my $who = $self->_caller; foreach my $name ( sort keys %seen ) { local $^W; *{"${who}::$name"} = sub { ${"${who}::AUTOLOAD"} = "${who}::$name"; goto &{"${who}::AUTOLOAD"}; }; } } sub new { my ($class, %args) = @_; delete $INC{'FindBin.pm'}; { # to suppress the redefine warning local $SIG{__WARN__} = sub {}; require FindBin; } # ignore the prefix on extension modules built from top level. my $base_path = Cwd::abs_path($FindBin::Bin); unless ( Cwd::abs_path(Cwd::getcwd()) eq $base_path ) { delete $args{prefix}; } return $args{_self} if $args{_self}; $base_path = VMS::Filespec::unixify($base_path) if $^O eq 'VMS'; $args{dispatch} ||= 'Admin'; $args{prefix} ||= 'inc'; $args{author} ||= ($^O eq 'VMS' ? '_author' : '.author'); $args{bundle} ||= 'inc/BUNDLES'; $args{base} ||= $base_path; $class =~ s/^\Q$args{prefix}\E:://; $args{name} ||= $class; $args{version} ||= $class->VERSION; unless ( $args{path} ) { $args{path} = $args{name}; $args{path} =~ s!::!/!g; } $args{file} ||= "$args{base}/$args{prefix}/$args{path}.pm"; $args{wrote} = 0; bless( \%args, $class ); } sub call { my ($self, $method) = @_; my $obj = $self->load($method) or return; splice(@_, 0, 2, $obj); goto &{$obj->can($method)}; } sub load { my ($self, $method) = @_; $self->load_extensions( "$self->{prefix}/$self->{path}", $self ) unless $self->{extensions}; foreach my $obj (@{$self->{extensions}}) { return $obj if $obj->can($method); } my $admin = $self->{admin} or die <<"END_DIE"; The '$method' method does not exist in the '$self->{prefix}' path! Please remove the '$self->{prefix}' directory and run $0 again to load it. END_DIE my $obj = $admin->load($method, 1); push @{$self->{extensions}}, $obj; $obj; } sub load_extensions { my ($self, $path, $top) = @_; my $should_reload = 0; unless ( grep { ! ref $_ and lc $_ eq lc $self->{prefix} } @INC ) { unshift @INC, $self->{prefix}; $should_reload = 1; } foreach my $rv ( $self->find_extensions($path) ) { my ($file, $pkg) = @{$rv}; next if $self->{pathnames}{$pkg}; local $@; my $new = eval { local $^W; require $file; $pkg->can('new') }; unless ( $new ) { warn $@ if $@; next; } $self->{pathnames}{$pkg} = $should_reload ? delete $INC{$file} : $INC{$file}; push @{$self->{extensions}}, &{$new}($pkg, _top => $top ); } $self->{extensions} ||= []; } sub find_extensions { my ($self, $path) = @_; my @found; File::Find::find( {no_chdir => 1, wanted => sub { my $file = $File::Find::name; return unless $file =~ m!^\Q$path\E/(.+)\.pm\Z!is; my $subpath = $1; return if lc($subpath) eq lc($self->{dispatch}); $file = "$self->{path}/$subpath.pm"; my $pkg = "$self->{name}::$subpath"; $pkg =~ s!/!::!g; # If we have a mixed-case package name, assume case has been preserved # correctly. Otherwise, root through the file to locate the case-preserved # version of the package name. if ( $subpath eq lc($subpath) || $subpath eq uc($subpath) ) { my $content = Module::Install::_read($File::Find::name); my $in_pod = 0; foreach ( split /\n/, $content ) { $in_pod = 1 if /^=\w/; $in_pod = 0 if /^=cut/; next if ($in_pod || /^=cut/); # skip pod text next if /^\s*#/; # and comments if ( m/^\s*package\s+($pkg)\s*;/i ) { $pkg = $1; last; } } } push @found, [ $file, $pkg ]; }}, $path ) if -d $path; @found; } ##################################################################### # Common Utility Functions sub _caller { my $depth = 0; my $call = caller($depth); while ( $call eq __PACKAGE__ ) { $depth++; $call = caller($depth); } return $call; } sub _read { local *FH; open( FH, '<', $_[0] ) or die "open($_[0]): $!"; binmode FH; my $string = do { local $/; }; close FH or die "close($_[0]): $!"; return $string; } sub _readperl { my $string = Module::Install::_read($_[0]); $string =~ s/(?:\015{1,2}\012|\015|\012)/\n/sg; $string =~ s/(\n)\n*__(?:DATA|END)__\b.*\z/$1/s; $string =~ s/\n\n=\w+.+?\n\n=cut\b.+?\n+/\n\n/sg; return $string; } sub _readpod { my $string = Module::Install::_read($_[0]); $string =~ s/(?:\015{1,2}\012|\015|\012)/\n/sg; return $string if $_[0] =~ /\.pod\z/; $string =~ s/(^|\n=cut\b.+?\n+)[^=\s].+?\n(\n=\w+|\z)/$1$2/sg; $string =~ s/\n*=pod\b[^\n]*\n+/\n\n/sg; $string =~ s/\n*=cut\b[^\n]*\n+/\n\n/sg; $string =~ s/^\n+//s; return $string; } sub _write { local *FH; open( FH, '>', $_[0] ) or die "open($_[0]): $!"; binmode FH; foreach ( 1 .. $#_ ) { print FH $_[$_] or die "print($_[0]): $!"; } close FH or die "close($_[0]): $!"; } # _version is for processing module versions (eg, 1.03_05) not # Perl versions (eg, 5.8.1). sub _version { my $s = shift || 0; my $d =()= $s =~ /(\.)/g; if ( $d >= 2 ) { # Normalise multipart versions $s =~ s/(\.)(\d{1,3})/sprintf("$1%03d",$2)/eg; } $s =~ s/^(\d+)\.?//; my $l = $1 || 0; my @v = map { $_ . '0' x (3 - length $_) } $s =~ /(\d{1,3})\D?/g; $l = $l . '.' . join '', @v if @v; return $l + 0; } sub _cmp { _version($_[1]) <=> _version($_[2]); } # Cloned from Params::Util::_CLASS sub _CLASS { ( defined $_[0] and ! ref $_[0] and $_[0] =~ m/^[^\W\d]\w*(?:::\w+)*\z/s ) ? $_[0] : undef; } 1; # Copyright 2008 - 2012 Adam Kennedy. Audio-Nama-1.216/inc/Module/Install/0000755000175000017500000000000013544212627016106 5ustar jrothjrothAudio-Nama-1.216/inc/Module/Install/Makefile.pm0000644000175000017500000002743713412473751020177 0ustar jrothjroth#line 1 package Module::Install::Makefile; use strict 'vars'; use ExtUtils::MakeMaker (); use Module::Install::Base (); use Fcntl qw/:flock :seek/; use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.17'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } sub Makefile { $_[0] } my %seen = (); sub prompt { shift; # Infinite loop protection my @c = caller(); if ( ++$seen{"$c[1]|$c[2]|$_[0]"} > 3 ) { die "Caught an potential prompt infinite loop ($c[1]|$c[2]|$_[0])"; } # In automated testing or non-interactive session, always use defaults if ( ($ENV{AUTOMATED_TESTING} or -! -t STDIN) and ! $ENV{PERL_MM_USE_DEFAULT} ) { local $ENV{PERL_MM_USE_DEFAULT} = 1; goto &ExtUtils::MakeMaker::prompt; } else { goto &ExtUtils::MakeMaker::prompt; } } # Store a cleaned up version of the MakeMaker version, # since we need to behave differently in a variety of # ways based on the MM version. my $makemaker = eval $ExtUtils::MakeMaker::VERSION; # If we are passed a param, do a "newer than" comparison. # Otherwise, just return the MakeMaker version. sub makemaker { ( @_ < 2 or $makemaker >= eval($_[1]) ) ? $makemaker : 0 } # Ripped from ExtUtils::MakeMaker 6.56, and slightly modified # as we only need to know here whether the attribute is an array # or a hash or something else (which may or may not be appendable). my %makemaker_argtype = ( C => 'ARRAY', CONFIG => 'ARRAY', # CONFIGURE => 'CODE', # ignore DIR => 'ARRAY', DL_FUNCS => 'HASH', DL_VARS => 'ARRAY', EXCLUDE_EXT => 'ARRAY', EXE_FILES => 'ARRAY', FUNCLIST => 'ARRAY', H => 'ARRAY', IMPORTS => 'HASH', INCLUDE_EXT => 'ARRAY', LIBS => 'ARRAY', # ignore '' MAN1PODS => 'HASH', MAN3PODS => 'HASH', META_ADD => 'HASH', META_MERGE => 'HASH', PL_FILES => 'HASH', PM => 'HASH', PMLIBDIRS => 'ARRAY', PMLIBPARENTDIRS => 'ARRAY', PREREQ_PM => 'HASH', CONFIGURE_REQUIRES => 'HASH', SKIP => 'ARRAY', TYPEMAPS => 'ARRAY', XS => 'HASH', # VERSION => ['version',''], # ignore # _KEEP_AFTER_FLUSH => '', clean => 'HASH', depend => 'HASH', dist => 'HASH', dynamic_lib=> 'HASH', linkext => 'HASH', macro => 'HASH', postamble => 'HASH', realclean => 'HASH', test => 'HASH', tool_autosplit => 'HASH', # special cases where you can use makemaker_append CCFLAGS => 'APPENDABLE', DEFINE => 'APPENDABLE', INC => 'APPENDABLE', LDDLFLAGS => 'APPENDABLE', LDFROM => 'APPENDABLE', ); sub makemaker_args { my ($self, %new_args) = @_; my $args = ( $self->{makemaker_args} ||= {} ); foreach my $key (keys %new_args) { if ($makemaker_argtype{$key}) { if ($makemaker_argtype{$key} eq 'ARRAY') { $args->{$key} = [] unless defined $args->{$key}; unless (ref $args->{$key} eq 'ARRAY') { $args->{$key} = [$args->{$key}] } push @{$args->{$key}}, ref $new_args{$key} eq 'ARRAY' ? @{$new_args{$key}} : $new_args{$key}; } elsif ($makemaker_argtype{$key} eq 'HASH') { $args->{$key} = {} unless defined $args->{$key}; foreach my $skey (keys %{ $new_args{$key} }) { $args->{$key}{$skey} = $new_args{$key}{$skey}; } } elsif ($makemaker_argtype{$key} eq 'APPENDABLE') { $self->makemaker_append($key => $new_args{$key}); } } else { if (defined $args->{$key}) { warn qq{MakeMaker attribute "$key" is overriden; use "makemaker_append" to append values\n}; } $args->{$key} = $new_args{$key}; } } return $args; } # For mm args that take multiple space-separated args, # append an argument to the current list. sub makemaker_append { my $self = shift; my $name = shift; my $args = $self->makemaker_args; $args->{$name} = defined $args->{$name} ? join( ' ', $args->{$name}, @_ ) : join( ' ', @_ ); } sub build_subdirs { my $self = shift; my $subdirs = $self->makemaker_args->{DIR} ||= []; for my $subdir (@_) { push @$subdirs, $subdir; } } sub clean_files { my $self = shift; my $clean = $self->makemaker_args->{clean} ||= {}; %$clean = ( %$clean, FILES => join ' ', grep { length $_ } ($clean->{FILES} || (), @_), ); } sub realclean_files { my $self = shift; my $realclean = $self->makemaker_args->{realclean} ||= {}; %$realclean = ( %$realclean, FILES => join ' ', grep { length $_ } ($realclean->{FILES} || (), @_), ); } sub libs { my $self = shift; my $libs = ref $_[0] ? shift : [ shift ]; $self->makemaker_args( LIBS => $libs ); } sub inc { my $self = shift; $self->makemaker_args( INC => shift ); } sub _wanted_t { } sub tests_recursive { my $self = shift; my $dir = shift || 't'; unless ( -d $dir ) { die "tests_recursive dir '$dir' does not exist"; } my %tests = map { $_ => 1 } split / /, ($self->tests || ''); require File::Find; File::Find::find( sub { /\.t$/ and -f $_ and $tests{"$File::Find::dir/*.t"} = 1 }, $dir ); $self->tests( join ' ', sort keys %tests ); } sub write { my $self = shift; die "&Makefile->write() takes no arguments\n" if @_; # Check the current Perl version my $perl_version = $self->perl_version; if ( $perl_version ) { eval "use $perl_version; 1" or die "ERROR: perl: Version $] is installed, " . "but we need version >= $perl_version"; } # Make sure we have a new enough MakeMaker require ExtUtils::MakeMaker; if ( $perl_version and $self->_cmp($perl_version, '5.006') >= 0 ) { # This previous attempted to inherit the version of # ExtUtils::MakeMaker in use by the module author, but this # was found to be untenable as some authors build releases # using future dev versions of EU:MM that nobody else has. # Instead, #toolchain suggests we use 6.59 which is the most # stable version on CPAN at time of writing and is, to quote # ribasushi, "not terminally fucked, > and tested enough". # TODO: We will now need to maintain this over time to push # the version up as new versions are released. $self->build_requires( 'ExtUtils::MakeMaker' => 6.59 ); $self->configure_requires( 'ExtUtils::MakeMaker' => 6.59 ); } else { # Allow legacy-compatibility with 5.005 by depending on the # most recent EU:MM that supported 5.005. $self->build_requires( 'ExtUtils::MakeMaker' => 6.36 ); $self->configure_requires( 'ExtUtils::MakeMaker' => 6.36 ); } # Generate the MakeMaker params my $args = $self->makemaker_args; $args->{DISTNAME} = $self->name; $args->{NAME} = $self->module_name || $self->name; $args->{NAME} =~ s/-/::/g; $args->{VERSION} = $self->version or die <<'EOT'; ERROR: Can't determine distribution version. Please specify it explicitly via 'version' in Makefile.PL, or set a valid $VERSION in a module, and provide its file path via 'version_from' (or 'all_from' if you prefer) in Makefile.PL. EOT if ( $self->tests ) { my @tests = split ' ', $self->tests; my %seen; $args->{test} = { TESTS => (join ' ', grep {!$seen{$_}++} @tests), }; } elsif ( $Module::Install::ExtraTests::use_extratests ) { # Module::Install::ExtraTests doesn't set $self->tests and does its own tests via harness. # So, just ignore our xt tests here. } elsif ( -d 'xt' and ($Module::Install::AUTHOR or $ENV{RELEASE_TESTING}) ) { $args->{test} = { TESTS => join( ' ', map { "$_/*.t" } grep { -d $_ } qw{ t xt } ), }; } if ( $] >= 5.005 ) { $args->{ABSTRACT} = $self->abstract; $args->{AUTHOR} = join ', ', @{$self->author || []}; } if ( $self->makemaker(6.10) ) { $args->{NO_META} = 1; #$args->{NO_MYMETA} = 1; } if ( $self->makemaker(6.17) and $self->sign ) { $args->{SIGN} = 1; } unless ( $self->is_admin ) { delete $args->{SIGN}; } if ( $self->makemaker(6.31) and $self->license ) { $args->{LICENSE} = $self->license; } my $prereq = ($args->{PREREQ_PM} ||= {}); %$prereq = ( %$prereq, map { @$_ } # flatten [module => version] map { @$_ } grep $_, ($self->requires) ); # Remove any reference to perl, PREREQ_PM doesn't support it delete $args->{PREREQ_PM}->{perl}; # Merge both kinds of requires into BUILD_REQUIRES my $build_prereq = ($args->{BUILD_REQUIRES} ||= {}); %$build_prereq = ( %$build_prereq, map { @$_ } # flatten [module => version] map { @$_ } grep $_, ($self->configure_requires, $self->build_requires) ); # Remove any reference to perl, BUILD_REQUIRES doesn't support it delete $args->{BUILD_REQUIRES}->{perl}; # Delete bundled dists from prereq_pm, add it to Makefile DIR my $subdirs = ($args->{DIR} || []); if ($self->bundles) { my %processed; foreach my $bundle (@{ $self->bundles }) { my ($mod_name, $dist_dir) = @$bundle; delete $prereq->{$mod_name}; $dist_dir = File::Basename::basename($dist_dir); # dir for building this module if (not exists $processed{$dist_dir}) { if (-d $dist_dir) { # List as sub-directory to be processed by make push @$subdirs, $dist_dir; } # Else do nothing: the module is already present on the system $processed{$dist_dir} = undef; } } } unless ( $self->makemaker('6.55_03') ) { %$prereq = (%$prereq,%$build_prereq); delete $args->{BUILD_REQUIRES}; } if ( my $perl_version = $self->perl_version ) { eval "use $perl_version; 1" or die "ERROR: perl: Version $] is installed, " . "but we need version >= $perl_version"; if ( $self->makemaker(6.48) ) { $args->{MIN_PERL_VERSION} = $perl_version; } } if ($self->installdirs) { warn qq{old INSTALLDIRS (probably set by makemaker_args) is overriden by installdirs\n} if $args->{INSTALLDIRS}; $args->{INSTALLDIRS} = $self->installdirs; } my %args = map { ( $_ => $args->{$_} ) } grep {defined($args->{$_} ) } keys %$args; my $user_preop = delete $args{dist}->{PREOP}; if ( my $preop = $self->admin->preop($user_preop) ) { foreach my $key ( keys %$preop ) { $args{dist}->{$key} = $preop->{$key}; } } my $mm = ExtUtils::MakeMaker::WriteMakefile(%args); $self->fix_up_makefile($mm->{FIRST_MAKEFILE} || 'Makefile'); } sub fix_up_makefile { my $self = shift; my $makefile_name = shift; my $top_class = ref($self->_top) || ''; my $top_version = $self->_top->VERSION || ''; my $preamble = $self->preamble ? "# Preamble by $top_class $top_version\n" . $self->preamble : ''; my $postamble = "# Postamble by $top_class $top_version\n" . ($self->postamble || ''); local *MAKEFILE; open MAKEFILE, "+< $makefile_name" or die "fix_up_makefile: Couldn't open $makefile_name: $!"; eval { flock MAKEFILE, LOCK_EX }; my $makefile = do { local $/; }; $makefile =~ s/\b(test_harness\(\$\(TEST_VERBOSE\), )/$1'inc', /; $makefile =~ s/( -I\$\(INST_ARCHLIB\))/ -Iinc$1/g; $makefile =~ s/( "-I\$\(INST_LIB\)")/ "-Iinc"$1/g; $makefile =~ s/^(FULLPERL = .*)/$1 "-Iinc"/m; $makefile =~ s/^(PERL = .*)/$1 "-Iinc"/m; # Module::Install will never be used to build the Core Perl # Sometimes PERL_LIB and PERL_ARCHLIB get written anyway, which breaks # PREFIX/PERL5LIB, and thus, install_share. Blank them if they exist $makefile =~ s/^PERL_LIB = .+/PERL_LIB =/m; #$makefile =~ s/^PERL_ARCHLIB = .+/PERL_ARCHLIB =/m; # Perl 5.005 mentions PERL_LIB explicitly, so we have to remove that as well. $makefile =~ s/(\"?)-I\$\(PERL_LIB\)\1//g; # XXX - This is currently unused; not sure if it breaks other MM-users # $makefile =~ s/^pm_to_blib\s+:\s+/pm_to_blib :: /mg; seek MAKEFILE, 0, SEEK_SET; truncate MAKEFILE, 0; print MAKEFILE "$preamble$makefile$postamble" or die $!; close MAKEFILE or die $!; 1; } sub preamble { my ($self, $text) = @_; $self->{preamble} = $text . $self->{preamble} if defined $text; $self->{preamble}; } sub postamble { my ($self, $text) = @_; $self->{postamble} ||= $self->admin->postamble; $self->{postamble} .= $text if defined $text; $self->{postamble} } 1; __END__ #line 544 Audio-Nama-1.216/inc/Module/Install/Base.pm0000644000175000017500000000214713412473751017323 0ustar jrothjroth#line 1 package Module::Install::Base; use strict 'vars'; use vars qw{$VERSION}; BEGIN { $VERSION = '1.17'; } # Suspend handler for "redefined" warnings BEGIN { my $w = $SIG{__WARN__}; $SIG{__WARN__} = sub { $w }; } #line 42 sub new { my $class = shift; unless ( defined &{"${class}::call"} ) { *{"${class}::call"} = sub { shift->_top->call(@_) }; } unless ( defined &{"${class}::load"} ) { *{"${class}::load"} = sub { shift->_top->load(@_) }; } bless { @_ }, $class; } #line 61 sub AUTOLOAD { local $@; my $func = eval { shift->_top->autoload } or return; goto &$func; } #line 75 sub _top { $_[0]->{_top}; } #line 90 sub admin { $_[0]->_top->{admin} or Module::Install::Base::FakeAdmin->new; } #line 106 sub is_admin { ! $_[0]->admin->isa('Module::Install::Base::FakeAdmin'); } sub DESTROY {} package Module::Install::Base::FakeAdmin; use vars qw{$VERSION}; BEGIN { $VERSION = $Module::Install::Base::VERSION; } my $fake; sub new { $fake ||= bless(\@_, $_[0]); } sub AUTOLOAD {} sub DESTROY {} # Restore warning handler BEGIN { $SIG{__WARN__} = $SIG{__WARN__}->(); } 1; #line 159 Audio-Nama-1.216/inc/Module/Install/Scripts.pm0000644000175000017500000000101113412473751020065 0ustar jrothjroth#line 1 package Module::Install::Scripts; use strict 'vars'; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.17'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } sub install_script { my $self = shift; my $args = $self->makemaker_args; my $exe = $args->{EXE_FILES} ||= []; foreach ( @_ ) { if ( -f $_ ) { push @$exe, $_; } elsif ( -d 'script' and -f "script/$_" ) { push @$exe, "script/$_"; } else { die("Cannot find script '$_'"); } } } 1; Audio-Nama-1.216/inc/Module/Install/Win32.pm0000644000175000017500000000340313412473751017347 0ustar jrothjroth#line 1 package Module::Install::Win32; use strict; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.17'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } # determine if the user needs nmake, and download it if needed sub check_nmake { my $self = shift; $self->load('can_run'); $self->load('get_file'); require Config; return unless ( $^O eq 'MSWin32' and $Config::Config{make} and $Config::Config{make} =~ /^nmake\b/i and ! $self->can_run('nmake') ); print "The required 'nmake' executable not found, fetching it...\n"; require File::Basename; my $rv = $self->get_file( url => 'http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15.exe', ftp_url => 'ftp://ftp.microsoft.com/Softlib/MSLFILES/Nmake15.exe', local_dir => File::Basename::dirname($^X), size => 51928, run => 'Nmake15.exe /o > nul', check_for => 'Nmake.exe', remove => 1, ); die <<'END_MESSAGE' unless $rv; ------------------------------------------------------------------------------- Since you are using Microsoft Windows, you will need the 'nmake' utility before installation. It's available at: http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15.exe or ftp://ftp.microsoft.com/Softlib/MSLFILES/Nmake15.exe Please download the file manually, save it to a directory in %PATH% (e.g. C:\WINDOWS\COMMAND\), then launch the MS-DOS command line shell, "cd" to that directory, and run "Nmake15.exe" from there; that will create the 'nmake.exe' file needed by this module. You may then resume the installation process described in README. ------------------------------------------------------------------------------- END_MESSAGE } 1; Audio-Nama-1.216/inc/Module/Install/Can.pm0000644000175000017500000000640513412473751017153 0ustar jrothjroth#line 1 package Module::Install::Can; use strict; use Config (); use ExtUtils::MakeMaker (); use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.17'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } # check if we can load some module ### Upgrade this to not have to load the module if possible sub can_use { my ($self, $mod, $ver) = @_; $mod =~ s{::|\\}{/}g; $mod .= '.pm' unless $mod =~ /\.pm$/i; my $pkg = $mod; $pkg =~ s{/}{::}g; $pkg =~ s{\.pm$}{}i; local $@; eval { require $mod; $pkg->VERSION($ver || 0); 1 }; } # Check if we can run some command sub can_run { my ($self, $cmd) = @_; my $_cmd = $cmd; return $_cmd if (-x $_cmd or $_cmd = MM->maybe_command($_cmd)); for my $dir ((split /$Config::Config{path_sep}/, $ENV{PATH}), '.') { next if $dir eq ''; require File::Spec; my $abs = File::Spec->catfile($dir, $cmd); return $abs if (-x $abs or $abs = MM->maybe_command($abs)); } return; } # Can our C compiler environment build XS files sub can_xs { my $self = shift; # Ensure we have the CBuilder module $self->configure_requires( 'ExtUtils::CBuilder' => 0.27 ); # Do we have the configure_requires checker? local $@; eval "require ExtUtils::CBuilder;"; if ( $@ ) { # They don't obey configure_requires, so it is # someone old and delicate. Try to avoid hurting # them by falling back to an older simpler test. return $self->can_cc(); } # Do we have a working C compiler my $builder = ExtUtils::CBuilder->new( quiet => 1, ); unless ( $builder->have_compiler ) { # No working C compiler return 0; } # Write a C file representative of what XS becomes require File::Temp; my ( $FH, $tmpfile ) = File::Temp::tempfile( "compilexs-XXXXX", SUFFIX => '.c', ); binmode $FH; print $FH <<'END_C'; #include "EXTERN.h" #include "perl.h" #include "XSUB.h" int main(int argc, char **argv) { return 0; } int boot_sanexs() { return 1; } END_C close $FH; # Can the C compiler access the same headers XS does my @libs = (); my $object = undef; eval { local $^W = 0; $object = $builder->compile( source => $tmpfile, ); @libs = $builder->link( objects => $object, module_name => 'sanexs', ); }; my $result = $@ ? 0 : 1; # Clean up all the build files foreach ( $tmpfile, $object, @libs ) { next unless defined $_; 1 while unlink; } return $result; } # Can we locate a (the) C compiler sub can_cc { my $self = shift; if ($^O eq 'VMS') { require ExtUtils::CBuilder; my $builder = ExtUtils::CBuilder->new( quiet => 1, ); return $builder->have_compiler; } my @chunks = split(/ /, $Config::Config{cc}) or return; # $Config{cc} may contain args; try to find out the program part while (@chunks) { return $self->can_run("@chunks") || (pop(@chunks), next); } return; } # Fix Cygwin bug on maybe_command(); if ( $^O eq 'cygwin' ) { require ExtUtils::MM_Cygwin; require ExtUtils::MM_Win32; if ( ! defined(&ExtUtils::MM_Cygwin::maybe_command) ) { *ExtUtils::MM_Cygwin::maybe_command = sub { my ($self, $file) = @_; if ($file =~ m{^/cygdrive/}i and ExtUtils::MM_Win32->can('maybe_command')) { ExtUtils::MM_Win32->maybe_command($file); } else { ExtUtils::MM_Unix->maybe_command($file); } } } } 1; __END__ #line 245 Audio-Nama-1.216/inc/Module/Install/Fetch.pm0000644000175000017500000000462713412473751017507 0ustar jrothjroth#line 1 package Module::Install::Fetch; use strict; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.17'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } sub get_file { my ($self, %args) = @_; my ($scheme, $host, $path, $file) = $args{url} =~ m|^(\w+)://([^/]+)(.+)/(.+)| or return; if ( $scheme eq 'http' and ! eval { require LWP::Simple; 1 } ) { $args{url} = $args{ftp_url} or (warn("LWP support unavailable!\n"), return); ($scheme, $host, $path, $file) = $args{url} =~ m|^(\w+)://([^/]+)(.+)/(.+)| or return; } $|++; print "Fetching '$file' from $host... "; unless (eval { require Socket; Socket::inet_aton($host) }) { warn "'$host' resolve failed!\n"; return; } return unless $scheme eq 'ftp' or $scheme eq 'http'; require Cwd; my $dir = Cwd::getcwd(); chdir $args{local_dir} or return if exists $args{local_dir}; if (eval { require LWP::Simple; 1 }) { LWP::Simple::mirror($args{url}, $file); } elsif (eval { require Net::FTP; 1 }) { eval { # use Net::FTP to get past firewall my $ftp = Net::FTP->new($host, Passive => 1, Timeout => 600); $ftp->login("anonymous", 'anonymous@example.com'); $ftp->cwd($path); $ftp->binary; $ftp->get($file) or (warn("$!\n"), return); $ftp->quit; } } elsif (my $ftp = $self->can_run('ftp')) { eval { # no Net::FTP, fallback to ftp.exe require FileHandle; my $fh = FileHandle->new; local $SIG{CHLD} = 'IGNORE'; unless ($fh->open("|$ftp -n")) { warn "Couldn't open ftp: $!\n"; chdir $dir; return; } my @dialog = split(/\n/, <<"END_FTP"); open $host user anonymous anonymous\@example.com cd $path binary get $file $file quit END_FTP foreach (@dialog) { $fh->print("$_\n") } $fh->close; } } else { warn "No working 'ftp' program available!\n"; chdir $dir; return; } unless (-f $file) { warn "Fetching failed: $@\n"; chdir $dir; return; } return if exists $args{size} and -s $file != $args{size}; system($args{run}) if exists $args{run}; unlink($file) if $args{remove}; print(((!exists $args{check_for} or -e $args{check_for}) ? "done!" : "failed! ($!)"), "\n"); chdir $dir; return !$?; } 1; Audio-Nama-1.216/inc/Module/Install/WriteAll.pm0000644000175000017500000000237613412473751020200 0ustar jrothjroth#line 1 package Module::Install::WriteAll; use strict; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.17'; @ISA = qw{Module::Install::Base}; $ISCORE = 1; } sub WriteAll { my $self = shift; my %args = ( meta => 1, sign => 0, inline => 0, check_nmake => 1, @_, ); $self->sign(1) if $args{sign}; $self->admin->WriteAll(%args) if $self->is_admin; $self->check_nmake if $args{check_nmake}; unless ( $self->makemaker_args->{PL_FILES} ) { # XXX: This still may be a bit over-defensive... unless ($self->makemaker(6.25)) { $self->makemaker_args( PL_FILES => {} ) if -f 'Build.PL'; } } # Until ExtUtils::MakeMaker support MYMETA.yml, make sure # we clean it up properly ourself. $self->realclean_files('MYMETA.yml'); if ( $args{inline} ) { $self->Inline->write; } else { $self->Makefile->write; } # The Makefile write process adds a couple of dependencies, # so write the META.yml files after the Makefile. if ( $args{meta} ) { $self->Meta->write; } # Experimental support for MYMETA if ( $ENV{X_MYMETA} ) { if ( $ENV{X_MYMETA} eq 'JSON' ) { $self->Meta->write_mymeta_json; } else { $self->Meta->write_mymeta_yaml; } } return 1; } 1; Audio-Nama-1.216/inc/Module/Install/Metadata.pm0000644000175000017500000004330213412473751020167 0ustar jrothjroth#line 1 package Module::Install::Metadata; use strict 'vars'; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.17'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } my @boolean_keys = qw{ sign }; my @scalar_keys = qw{ name module_name abstract version distribution_type tests installdirs }; my @tuple_keys = qw{ configure_requires build_requires requires recommends bundles resources }; my @resource_keys = qw{ homepage bugtracker repository }; my @array_keys = qw{ keywords author }; *authors = \&author; sub Meta { shift } sub Meta_BooleanKeys { @boolean_keys } sub Meta_ScalarKeys { @scalar_keys } sub Meta_TupleKeys { @tuple_keys } sub Meta_ResourceKeys { @resource_keys } sub Meta_ArrayKeys { @array_keys } foreach my $key ( @boolean_keys ) { *$key = sub { my $self = shift; if ( defined wantarray and not @_ ) { return $self->{values}->{$key}; } $self->{values}->{$key} = ( @_ ? $_[0] : 1 ); return $self; }; } foreach my $key ( @scalar_keys ) { *$key = sub { my $self = shift; return $self->{values}->{$key} if defined wantarray and !@_; $self->{values}->{$key} = shift; return $self; }; } foreach my $key ( @array_keys ) { *$key = sub { my $self = shift; return $self->{values}->{$key} if defined wantarray and !@_; $self->{values}->{$key} ||= []; push @{$self->{values}->{$key}}, @_; return $self; }; } foreach my $key ( @resource_keys ) { *$key = sub { my $self = shift; unless ( @_ ) { return () unless $self->{values}->{resources}; return map { $_->[1] } grep { $_->[0] eq $key } @{ $self->{values}->{resources} }; } return $self->{values}->{resources}->{$key} unless @_; my $uri = shift or die( "Did not provide a value to $key()" ); $self->resources( $key => $uri ); return 1; }; } foreach my $key ( grep { $_ ne "resources" } @tuple_keys) { *$key = sub { my $self = shift; return $self->{values}->{$key} unless @_; my @added; while ( @_ ) { my $module = shift or last; my $version = shift || 0; push @added, [ $module, $version ]; } push @{ $self->{values}->{$key} }, @added; return map {@$_} @added; }; } # Resource handling my %lc_resource = map { $_ => 1 } qw{ homepage license bugtracker repository }; sub resources { my $self = shift; while ( @_ ) { my $name = shift or last; my $value = shift or next; if ( $name eq lc $name and ! $lc_resource{$name} ) { die("Unsupported reserved lowercase resource '$name'"); } $self->{values}->{resources} ||= []; push @{ $self->{values}->{resources} }, [ $name, $value ]; } $self->{values}->{resources}; } # Aliases for build_requires that will have alternative # meanings in some future version of META.yml. sub test_requires { shift->build_requires(@_) } sub install_requires { shift->build_requires(@_) } # Aliases for installdirs options sub install_as_core { $_[0]->installdirs('perl') } sub install_as_cpan { $_[0]->installdirs('site') } sub install_as_site { $_[0]->installdirs('site') } sub install_as_vendor { $_[0]->installdirs('vendor') } sub dynamic_config { my $self = shift; my $value = @_ ? shift : 1; if ( $self->{values}->{dynamic_config} ) { # Once dynamic we never change to static, for safety return 0; } $self->{values}->{dynamic_config} = $value ? 1 : 0; return 1; } # Convenience command sub static_config { shift->dynamic_config(0); } sub perl_version { my $self = shift; return $self->{values}->{perl_version} unless @_; my $version = shift or die( "Did not provide a value to perl_version()" ); # Normalize the version $version = $self->_perl_version($version); # We don't support the really old versions unless ( $version >= 5.005 ) { die "Module::Install only supports 5.005 or newer (use ExtUtils::MakeMaker)\n"; } $self->{values}->{perl_version} = $version; } sub all_from { my ( $self, $file ) = @_; unless ( defined($file) ) { my $name = $self->name or die( "all_from called with no args without setting name() first" ); $file = join('/', 'lib', split(/-/, $name)) . '.pm'; $file =~ s{.*/}{} unless -e $file; unless ( -e $file ) { die("all_from cannot find $file from $name"); } } unless ( -f $file ) { die("The path '$file' does not exist, or is not a file"); } $self->{values}{all_from} = $file; # Some methods pull from POD instead of code. # If there is a matching .pod, use that instead my $pod = $file; $pod =~ s/\.pm$/.pod/i; $pod = $file unless -e $pod; # Pull the different values $self->name_from($file) unless $self->name; $self->version_from($file) unless $self->version; $self->perl_version_from($file) unless $self->perl_version; $self->author_from($pod) unless @{$self->author || []}; $self->license_from($pod) unless $self->license; $self->abstract_from($pod) unless $self->abstract; return 1; } sub provides { my $self = shift; my $provides = ( $self->{values}->{provides} ||= {} ); %$provides = (%$provides, @_) if @_; return $provides; } sub auto_provides { my $self = shift; return $self unless $self->is_admin; unless (-e 'MANIFEST') { warn "Cannot deduce auto_provides without a MANIFEST, skipping\n"; return $self; } # Avoid spurious warnings as we are not checking manifest here. local $SIG{__WARN__} = sub {1}; require ExtUtils::Manifest; local *ExtUtils::Manifest::manicheck = sub { return }; require Module::Build; my $build = Module::Build->new( dist_name => $self->name, dist_version => $self->version, license => $self->license, ); $self->provides( %{ $build->find_dist_packages || {} } ); } sub feature { my $self = shift; my $name = shift; my $features = ( $self->{values}->{features} ||= [] ); my $mods; if ( @_ == 1 and ref( $_[0] ) ) { # The user used ->feature like ->features by passing in the second # argument as a reference. Accomodate for that. $mods = $_[0]; } else { $mods = \@_; } my $count = 0; push @$features, ( $name => [ map { ref($_) ? ( ref($_) eq 'HASH' ) ? %$_ : @$_ : $_ } @$mods ] ); return @$features; } sub features { my $self = shift; while ( my ( $name, $mods ) = splice( @_, 0, 2 ) ) { $self->feature( $name, @$mods ); } return $self->{values}->{features} ? @{ $self->{values}->{features} } : (); } sub no_index { my $self = shift; my $type = shift; push @{ $self->{values}->{no_index}->{$type} }, @_ if $type; return $self->{values}->{no_index}; } sub read { my $self = shift; $self->include_deps( 'YAML::Tiny', 0 ); require YAML::Tiny; my $data = YAML::Tiny::LoadFile('META.yml'); # Call methods explicitly in case user has already set some values. while ( my ( $key, $value ) = each %$data ) { next unless $self->can($key); if ( ref $value eq 'HASH' ) { while ( my ( $module, $version ) = each %$value ) { $self->can($key)->($self, $module => $version ); } } else { $self->can($key)->($self, $value); } } return $self; } sub write { my $self = shift; return $self unless $self->is_admin; $self->admin->write_meta; return $self; } sub version_from { require ExtUtils::MM_Unix; my ( $self, $file ) = @_; $self->version( ExtUtils::MM_Unix->parse_version($file) ); # for version integrity check $self->makemaker_args( VERSION_FROM => $file ); } sub abstract_from { require ExtUtils::MM_Unix; my ( $self, $file ) = @_; $self->abstract( bless( { DISTNAME => $self->name }, 'ExtUtils::MM_Unix' )->parse_abstract($file) ); } # Add both distribution and module name sub name_from { my ($self, $file) = @_; if ( Module::Install::_read($file) =~ m/ ^ \s* package \s* ([\w:]+) [\s|;]* /ixms ) { my ($name, $module_name) = ($1, $1); $name =~ s{::}{-}g; $self->name($name); unless ( $self->module_name ) { $self->module_name($module_name); } } else { die("Cannot determine name from $file\n"); } } sub _extract_perl_version { if ( $_[0] =~ m/ ^\s* (?:use|require) \s* v? ([\d_\.]+) \s* ; /ixms ) { my $perl_version = $1; $perl_version =~ s{_}{}g; return $perl_version; } else { return; } } sub perl_version_from { my $self = shift; my $perl_version=_extract_perl_version(Module::Install::_read($_[0])); if ($perl_version) { $self->perl_version($perl_version); } else { warn "Cannot determine perl version info from $_[0]\n"; return; } } sub author_from { my $self = shift; my $content = Module::Install::_read($_[0]); if ($content =~ m/ =head \d \s+ (?:authors?)\b \s* ([^\n]*) | =head \d \s+ (?:licen[cs]e|licensing|copyright|legal)\b \s* .*? copyright .*? \d\d\d[\d.]+ \s* (?:\bby\b)? \s* ([^\n]*) /ixms) { my $author = $1 || $2; # XXX: ugly but should work anyway... if (eval "require Pod::Escapes; 1") { # Pod::Escapes has a mapping table. # It's in core of perl >= 5.9.3, and should be installed # as one of the Pod::Simple's prereqs, which is a prereq # of Pod::Text 3.x (see also below). $author =~ s{ E<( (\d+) | ([A-Za-z]+) )> } { defined $2 ? chr($2) : defined $Pod::Escapes::Name2character_number{$1} ? chr($Pod::Escapes::Name2character_number{$1}) : do { warn "Unknown escape: E<$1>"; "E<$1>"; }; }gex; } elsif (eval "require Pod::Text; 1" && $Pod::Text::VERSION < 3) { # Pod::Text < 3.0 has yet another mapping table, # though the table name of 2.x and 1.x are different. # (1.x is in core of Perl < 5.6, 2.x is in core of # Perl < 5.9.3) my $mapping = ($Pod::Text::VERSION < 2) ? \%Pod::Text::HTML_Escapes : \%Pod::Text::ESCAPES; $author =~ s{ E<( (\d+) | ([A-Za-z]+) )> } { defined $2 ? chr($2) : defined $mapping->{$1} ? $mapping->{$1} : do { warn "Unknown escape: E<$1>"; "E<$1>"; }; }gex; } else { $author =~ s{E}{<}g; $author =~ s{E}{>}g; } $self->author($author); } else { warn "Cannot determine author info from $_[0]\n"; } } #Stolen from M::B my %license_urls = ( perl => 'http://dev.perl.org/licenses/', apache => 'http://apache.org/licenses/LICENSE-2.0', apache_1_1 => 'http://apache.org/licenses/LICENSE-1.1', artistic => 'http://opensource.org/licenses/artistic-license.php', artistic_2 => 'http://opensource.org/licenses/artistic-license-2.0.php', lgpl => 'http://opensource.org/licenses/lgpl-license.php', lgpl2 => 'http://opensource.org/licenses/lgpl-2.1.php', lgpl3 => 'http://opensource.org/licenses/lgpl-3.0.html', bsd => 'http://opensource.org/licenses/bsd-license.php', gpl => 'http://opensource.org/licenses/gpl-license.php', gpl2 => 'http://opensource.org/licenses/gpl-2.0.php', gpl3 => 'http://opensource.org/licenses/gpl-3.0.html', mit => 'http://opensource.org/licenses/mit-license.php', mozilla => 'http://opensource.org/licenses/mozilla1.1.php', open_source => undef, unrestricted => undef, restrictive => undef, unknown => undef, ); sub license { my $self = shift; return $self->{values}->{license} unless @_; my $license = shift or die( 'Did not provide a value to license()' ); $license = __extract_license($license) || lc $license; $self->{values}->{license} = $license; # Automatically fill in license URLs if ( $license_urls{$license} ) { $self->resources( license => $license_urls{$license} ); } return 1; } sub _extract_license { my $pod = shift; my $matched; return __extract_license( ($matched) = $pod =~ m/ (=head \d \s+ L(?i:ICEN[CS]E|ICENSING)\b.*?) (=head \d.*|=cut.*|)\z /xms ) || __extract_license( ($matched) = $pod =~ m/ (=head \d \s+ (?:C(?i:OPYRIGHTS?)|L(?i:EGAL))\b.*?) (=head \d.*|=cut.*|)\z /xms ); } sub __extract_license { my $license_text = shift or return; my @phrases = ( '(?:under )?the same (?:terms|license) as (?:perl|the perl (?:\d )?programming language)' => 'perl', 1, '(?:under )?the terms of (?:perl|the perl programming language) itself' => 'perl', 1, 'Artistic and GPL' => 'perl', 1, 'GNU general public license' => 'gpl', 1, 'GNU public license' => 'gpl', 1, 'GNU lesser general public license' => 'lgpl', 1, 'GNU lesser public license' => 'lgpl', 1, 'GNU library general public license' => 'lgpl', 1, 'GNU library public license' => 'lgpl', 1, 'GNU Free Documentation license' => 'unrestricted', 1, 'GNU Affero General Public License' => 'open_source', 1, '(?:Free)?BSD license' => 'bsd', 1, 'Artistic license 2\.0' => 'artistic_2', 1, 'Artistic license' => 'artistic', 1, 'Apache (?:Software )?license' => 'apache', 1, 'GPL' => 'gpl', 1, 'LGPL' => 'lgpl', 1, 'BSD' => 'bsd', 1, 'Artistic' => 'artistic', 1, 'MIT' => 'mit', 1, 'Mozilla Public License' => 'mozilla', 1, 'Q Public License' => 'open_source', 1, 'OpenSSL License' => 'unrestricted', 1, 'SSLeay License' => 'unrestricted', 1, 'zlib License' => 'open_source', 1, 'proprietary' => 'proprietary', 0, ); while ( my ($pattern, $license, $osi) = splice(@phrases, 0, 3) ) { $pattern =~ s#\s+#\\s+#gs; if ( $license_text =~ /\b$pattern\b/i ) { return $license; } } return ''; } sub license_from { my $self = shift; if (my $license=_extract_license(Module::Install::_read($_[0]))) { $self->license($license); } else { warn "Cannot determine license info from $_[0]\n"; return 'unknown'; } } sub _extract_bugtracker { my @links = $_[0] =~ m#L<( https?\Q://rt.cpan.org/\E[^>]+| https?\Q://github.com/\E[\w_]+/[\w_]+/issues| https?\Q://code.google.com/p/\E[\w_\-]+/issues/list )>#gx; my %links; @links{@links}=(); @links=keys %links; return @links; } sub bugtracker_from { my $self = shift; my $content = Module::Install::_read($_[0]); my @links = _extract_bugtracker($content); unless ( @links ) { warn "Cannot determine bugtracker info from $_[0]\n"; return 0; } if ( @links > 1 ) { warn "Found more than one bugtracker link in $_[0]\n"; return 0; } # Set the bugtracker bugtracker( $links[0] ); return 1; } sub requires_from { my $self = shift; my $content = Module::Install::_readperl($_[0]); my @requires = $content =~ m/^use\s+([^\W\d]\w*(?:::\w+)*)\s+(v?[\d\.]+)/mg; while ( @requires ) { my $module = shift @requires; my $version = shift @requires; $self->requires( $module => $version ); } } sub test_requires_from { my $self = shift; my $content = Module::Install::_readperl($_[0]); my @requires = $content =~ m/^use\s+([^\W\d]\w*(?:::\w+)*)\s+([\d\.]+)/mg; while ( @requires ) { my $module = shift @requires; my $version = shift @requires; $self->test_requires( $module => $version ); } } # Convert triple-part versions (eg, 5.6.1 or 5.8.9) to # numbers (eg, 5.006001 or 5.008009). # Also, convert double-part versions (eg, 5.8) sub _perl_version { my $v = $_[-1]; $v =~ s/^([1-9])\.([1-9]\d?\d?)$/sprintf("%d.%03d",$1,$2)/e; $v =~ s/^([1-9])\.([1-9]\d?\d?)\.(0|[1-9]\d?\d?)$/sprintf("%d.%03d%03d",$1,$2,$3 || 0)/e; $v =~ s/(\.\d\d\d)000$/$1/; $v =~ s/_.+$//; if ( ref($v) ) { # Numify $v = $v + 0; } return $v; } sub add_metadata { my $self = shift; my %hash = @_; for my $key (keys %hash) { warn "add_metadata: $key is not prefixed with 'x_'.\n" . "Use appopriate function to add non-private metadata.\n" unless $key =~ /^x_/; $self->{values}->{$key} = $hash{$key}; } } ###################################################################### # MYMETA Support sub WriteMyMeta { die "WriteMyMeta has been deprecated"; } sub write_mymeta_yaml { my $self = shift; # We need YAML::Tiny to write the MYMETA.yml file unless ( eval { require YAML::Tiny; 1; } ) { return 1; } # Generate the data my $meta = $self->_write_mymeta_data or return 1; # Save as the MYMETA.yml file print "Writing MYMETA.yml\n"; YAML::Tiny::DumpFile('MYMETA.yml', $meta); } sub write_mymeta_json { my $self = shift; # We need JSON to write the MYMETA.json file unless ( eval { require JSON; 1; } ) { return 1; } # Generate the data my $meta = $self->_write_mymeta_data or return 1; # Save as the MYMETA.yml file print "Writing MYMETA.json\n"; Module::Install::_write( 'MYMETA.json', JSON->new->pretty(1)->canonical->encode($meta), ); } sub _write_mymeta_data { my $self = shift; # If there's no existing META.yml there is nothing we can do return undef unless -f 'META.yml'; # We need Parse::CPAN::Meta to load the file unless ( eval { require Parse::CPAN::Meta; 1; } ) { return undef; } # Merge the perl version into the dependencies my $val = $self->Meta->{values}; my $perl = delete $val->{perl_version}; if ( $perl ) { $val->{requires} ||= []; my $requires = $val->{requires}; # Canonize to three-dot version after Perl 5.6 if ( $perl >= 5.006 ) { $perl =~ s{^(\d+)\.(\d\d\d)(\d*)}{join('.', $1, int($2||0), int($3||0))}e } unshift @$requires, [ perl => $perl ]; } # Load the advisory META.yml file my @yaml = Parse::CPAN::Meta::LoadFile('META.yml'); my $meta = $yaml[0]; # Overwrite the non-configure dependency hashes delete $meta->{requires}; delete $meta->{build_requires}; delete $meta->{recommends}; if ( exists $val->{requires} ) { $meta->{requires} = { map { @$_ } @{ $val->{requires} } }; } if ( exists $val->{build_requires} ) { $meta->{build_requires} = { map { @$_ } @{ $val->{build_requires} } }; } return $meta; } 1; Audio-Nama-1.216/script/0000755000175000017500000000000013544212627014006 5ustar jrothjrothAudio-Nama-1.216/script/nama0000755000175000017500000023514113544212614014652 0ustar jrothjroth#!/usr/bin/env perl require 5.10.1; use Audio::Nama; Audio::Nama::main(); __END__ =head1 NAME B - multitrack recorder and digital audio workstation =head1 SYNOPSIS B [I] [I] =head1 DESCRIPTION A multitrack audio application for recording, effects processing, editing, mixing, mastering and live performance. It can also perform general-purpose audio processing, such as 5.1 to stereo conversion. Nama uses Ecasound as the audio processing engine. =head2 Audio projects Projects in Nama are audio networks of tracks and buses. Tracks may contain one or more RIFF (.wav) files, as well as effects, sends, inserts, marks, regions, fades, edits and sequences. Tracks can host LADSPA, LV2 and Ecasound plugins. Audio regions may be altered, duplicated, time-shifted or replaced. Audio processing is performed in realtime when a track is played and may be cached (frozen) to a new audio file. Project data is serialized as JSON. The complete history is tracked by the git version control system. As a result, projects can be managed using branches and tags, and provide undo/redo. Nama supports some MIDI functionality via midish. =head2 Presets and templates To facilitate reuse, a track's plugins and inserts can be stored as an I. I apply effect chains to groups of tracks. I are for duplicating an entire project sans audio files. =head2 Audio device Nama performs Audio IO via JACK or ALSA. Soundcard IO goes via JACK, if running, with transparent fallback to ALSA. Nama supports Ladish Level 1 session handling. =head2 User interfaces Nama has fully featured terminal command prompt, a Tk GUI, and experimental OSC and remote-command modes. The command prompt can run Nama commands, Ecasound interactive-mode commands, commands for the midish MIDI recorder/player, shell commands and perl code. Commands and filenames can be autocompleted using the TAB key. Command history is available to browse with up and down arrows. The help system provides documentation and keyword search covering Nama commands and effects-processing plugins. The hotkey mode provides a convenient way to select, view, and modify effect parameters. =head1 OPTIONS =over 12 =item B<--gui, -g> Start Nama in GUI mode (default when Tk is available) =item B<--text, -t> Start Nama in text mode =item B<--config, -f> Specify configuration file (default: ~/.namarc) =item B<--project-root, -d> Specify project root directory =item B<--use-pwd, -p> Use current dir for all WAV and project files =item B<--create-project, -c> Create project if it doesn't exist =item B<--net-eci, -n> Use Ecasound's Net-ECI interface =item B<--libecasoundc, -l> Use Ecasound's libecasoundc interface =item B<--save-alsa, -a> Save/restore alsa state with project data =item B<--help, -h> This help display =item B<--regenerate-effects-cache, -r> Regenerate the effects data cache =back Debugging options: =over 12 =item B<--no-state, -M> Don't load project state =item B<--no-static-effects-data, -S> Don't load effects data =item B<--no-static-effects-cache, -C> Don't load the effects data cache =item B<--no-reconfigure-engine, -R> Don't autosave, autoreconfigure or git snapshot =item B<--fake-jack, -J> Simulate JACK environment =item B<--fake-alsa, -A> Simulate ALSA environment =item B<--no-ecasound, -E> Don't spawn Ecasound process =item B<--execute-command, -X> Supply a command to execute =item B<--no-terminal, -T> Don't initialize terminal =item B<--no-fades, -F> No fades on transport start/stop =item B<--log, -L> Log these (comma separated) categories =back =head1 CONTROLLING NAMA/ECASOUND The Ecasound audio engine is configured through use of I that define the signal processing network. Nama serves as an intermediary, taking high-level user commands, generating chain setups for user tasks such as recording, playback, mixing, etc., and running the audio engine. =head2 Configuration Commands Configuration commands affect I runs of the audio engine. For example, B and B determine whether the current track will get its audio stream from an external (e.g. live) source, whether an existing audio file will be played back, and whether a new audio file will be recorded. Nama responds to these commands by reconfiguring the engine and displaying the updated track status. See 'man ::ChainSetup' for details on how the chain setup created. =head2 Realtime Commands Once a chain setup is loaded and the engine is launched, commands can be issued to control the realtime behavior of the audio processing engine. These commands include transport C and C, playback head repositioning commands such C, C and C. Effects may be added, modified or removed while the engine is running. =head2 Configuration General configuration of sound devices and program options is performed by editing the F<.namarc> file, which is formatted as YAML. On Nama's first run, a default version of F<.namarc> is placed in the user's home directory. =head1 Tk GRAPHICAL UI Invoked by default if Tk is installed, this interface provides a subset of Nama's functionality on two windows: =head2 Main Window The top section has buttons for creating, loading and saving projects, adding tracks, adding effects to tracks. In short, for setup. Below are buttons for controlling the transport (start, stop and friends) and for setting marks. The GUI project name bar and time display change color to indicate whether the upcoming operation will include live recording (red), mixdown (yellow) or playback (green). =head2 Effects Window The B provides sliders for each effect parameter of each track. Parameter range, defaults, and log/linear scaling hints are automatically detected. Text-entry widgets are used to enter parameters values for plugins without hinted ranges. Any parameter label can be clicked to add a parameter controller. =head2 Waveform Window Provides a conventional view with waveform and playback head. =head2 Terminal Window The command prompt is available the terminal window and provides access to all of Nama's functions. =head1 TEXT USER INTERFACE Press the I key if necessary to get the command prompt, which will look something like this: =over 12 C> =back In this instance, 'sax' is the current track in the 'untitled' default project. When using buses, the bus is indicated before the track: =over 12 C> =back At the prompt, you can enter Nama and Ecasound commands, Perl code preceded by C or shell code preceded by C. Multiple commands on a single line are allowed if delimited by semicolons. Usually the lines are split on semicolons and the parts are executed sequentially, however if the line begins with C or C the entire line (up to double semicolons ';;' if present) will be given to the corresponding interpreter. You can access command history using up-arrow/down-arrow. Type C for general help, C for help with C, C for help with commands containing the string C. C lists all plugins/presets/controller containing both I and I. Tab-completion is provided for Nama commands, Ecasound-iam commands, plugin/preset/controller names, and project names. Most commands have abbreviations, such as 'afx' for 'add-effect'. These are shown in the help listings. =head1 TRACKS Each track has a descriptive name (i.e. vocal) and an integer track-number assigned when the track is created. New user tracks initially belong to the Main bus. Track output signals are usually mixed and pass through the Main track on the way to soundcard for monitoring. The following sections describes track attributes and their effects. =head2 Width Specifying 'mono' means the track has one input channel, which will be recorded as a mono WAV file. Mono track signals are automatically duplicated to stereo and a pan effect is provided. Specifying 'stereo' for a track means that two channels of audio input will be recorded as an interleaved stereo WAV file. You can also use a 'stereo' declaration to avoid the automatic channel copy usually applied to single-channel sources. Specifying N channels for a track ('set width N') means N successive input channels will be recorded as an N-channel interleaved WAV file. =head2 REC/PLAY/MON/OFF Basic signal routing for each track is controlled by its setting to REC, MON, PLAY or OFF. The I setting prepares to connect the live track source. The I setting prepares to connect the live track source and record it to an audio file. The I setting enqueues an audio file for playback from disk as the track source. The I setting tells Nama to remove the track from the audio network. I status may result for a track when no audio source is available. For example, a track with no recorded audio files will show OFF status when set to PLAY. =head2 Track output By default a track belongs to the Main bus and is routed to the Main track. This track provides a final fader before the signal goes via ALSA or JACK to the audio device. =head2 Bus setting Buses can force the status of their member tracks to OFF. Nama provides MON and OFF settings for buses. OFF (set by C) removes all member tracks from the chain setup, MON (set by C restores them. The B command sets the Mixdown track to PLAY and the Main bus to OFF. =head2 Version Numbers Multiple audio files ("takes") can be recorded for each track. These are distinguished by a version number that increments with each recording run, i.e. F, F, etc. All audio files recorded in the same run have the same version number. The version numbers for track playback can be selected at the bus or track level. By setting the bus version to 5, you can play back version 5 of several tracks at once. Version 5 could signify the fifth take of a song, or the fifth song of a live recording session. The track version setting, if present, overrides the bus version setting. Setting the track version to zero restores control of the version number to the bus. The Main bus version setting does I propagate to other buses. =head2 Marks Marks in Nama are similar to those in other audio editing software. One limitation is that mark positions are relative to the beginning of an Ecasound chain setup. If your project involves a single track, and you will be shortening the stream by setting a region to play, set any marks you need I defining the region. =head2 Regions The C command allows you to define endpoints for a portion of an audio file. You can then use the C command to move the region to the desired time position. If you use named marks as endpoints, the region will change if the mark's position is adjusted. Each track can have one region definition. To create multiple regions, the C command takes a pair of marks to create a read-only copy of the current track with the specified region definition. You can control this region as you would any other other track, shifting the start time, applying effects, adjusting volume, etc. =head3 Using Tracks from Other Projects The C clones a read-only track from another track, which may belong to a different project. =head2 Effects Each track gets volume and pan effects by default. New effects added using C are applied before pan volume controls. You can position effects anywhere you choose using C or C. =head3 Fades Fades can be placed on any track. They defined by mark position and duration. An additional volume operator, -eadb, is applied to each track to host the envelope controller that implements fades. =head3 Sends and Inserts The C command can route a track's post-fader output to a soundcard channel or JACK client in addition to the normal mixer input. Nama currently allows one aux send per track. The C command configures a pre- or post-fader send-and-return to soundcard channels or JACK clients. Wet and dry signal paths are provided, with a default setting of 100% wet. Each track can have one pre-fader and one post-fader insert. =head2 Bunches A bunch is just a list of track names. Using the C keyword with the name of a bunch will apply the commands that follow to all of the tracks in the bunch. A bunch can be created with the C command. A bus name can also be treated as a bunch. =head2 Buses B enable multiple tracks to be routed through a single mix track before feeding the Main mixer bus (or possibly, another bus.) The following commands create a bus and assign three tracks to it. The mix track takes the name of the bus and is stereo by default. # create a bus named Strings with a same-named mix track add-bus Strings # create tracks for the bus add-tracks violin cello bass # move the tracks from the Main bus (default) to the Strings bus for violin cello bass; move-to-bus Strings # use the mix track to control bus output volume Strings vol - 10 =head3 Submixes B are a type of bus used to provide instrument monitors, or to send the outputs from multiple user tracks to an external program such as jconverter. =head1 ROUTING =head2 General Notes While Nama can address tracks by either name and track number, Ecasound chain setups use the track number exclusively. The Main track (mixer output) is always chain 1, the Mixdown track is always chain 2. Nama uses Ecasound loop devices where necessary to connect two tracks, or to allow one track to have multiple inputs or outputs. Each loop device adds one buffer, which increases latency. =head2 Flow Diagrams Let's examine the signal flow from track 3, the first available user track. Assume track 3 is named "sax". We will divide the signal flow into track and mixer sections. Parentheses show the chain id. The stereo outputs of each user track terminate at Main_in, a loop device at the mixer input. =head3 Track, REC status Sound device --+---(3)----> Main_in /JACK client | +---(R3)---> sax_1.wav REC status indicates that the source of the signal is the soundcard or JACK client. The input signal will be written directly to a file except in the special preview and doodle modes. =head3 Track, PLAY status sax_1.wav ------(3)----> Main_in =head3 Mixer, with mixdown enabled In the second part of the flow graph, the mixed signal is delivered to an output device through the Main chain, which can host effects. Usually the Main track provides final control before audio output or mixdown. Main_in --(1)--> Main_out --+--------> Sound device | +-->(2)--> Mixdown_1.wav =head3 Mastering Mode In mastering mode (invoked by C and released C) the following network receives the Main track signal as input and provides an output to the soundcard or WAV file. +- Low -+ | | Main_in --- Eq --+- Mid -+--- Boost -> soundcard/wav_out | | +- High + The B track hosts an equalizer. The B, B and B tracks each apply a bandpass filter, a compressor and a spatialiser. The B track applies gain and a limiter. These effects and their default parameters are defined in the configuration file F<.namarc>. =head2 Mixdown The C command configures Nama for mixdown. The Mixdown track is set to REC (equivalent to C) and the audio monitoring output is turned off (equivalent to C
). Mixdown proceeds after you start the transport. As a convenience, Mixdown_nn.wav will be symlinked to F<_nn.wav> or F<_nn.wav> in the project directory. The latter format is used branches other than the default F branch.) Corresponding encoded files are created if the F option is set in F<.namarc>. Acceptable values are a space-separated list. The default is "mixdown_encodings: ogg mp3". =head2 Preview, Doodle and Eager Modes These non-recording modes, invoked by C and C commands tweak the routing rules for special purposes. B disables recording of WAV files to disk. B disables PLAY inputs while excluding any tracks with the same source as a currently routed track. The C command releases both preview and doodle modes. The eager setting causes the engine to start immediately following a reconfiguration. =head2 Saving Projects If git is available, projects are saved automatically after each command and it is not necessary to explicitly save your work. When you type C, Settings related to the state of the project are saved in the file F in the project directory. F is tracked by git. C updates several other data files as well: F, also in the project directory, contains data that is part of the project (such as command history, track comments, and current operating modes) but with no direct effect on the project audio. F, in the project root directory (usually F<~/nama>) contains system and user defined effect chains. =head3 Save without Git C will save project state to a file of that name. Similarly C will load the corresponding file. The F<.json> suffix may be omitted if "use_git: 0" is set in F<.namarc>. =head3 Save with Git If git is available, Nama uses it to store snapshots of every step in the history of your project. When you type C, the latest snapshot is tagged with the name "initial-mix", which you can recall later with the command C. You can include a comment with the snapshot: C Nama lets you create new branches, starting at any snapshot. To start a new branch called I starting at a snapshot called I you would say: C If you want to go back to working on the master branch, use C. You can also issue native git commands at the Nama prompt. =head3 Git history example All projects begin on the "master" branch. Because this is the default branch, it is not displayed in the prompt. Otherwise "master" is not special in any way. In the graphs below, the letters indicate named snapshots. create test-project ... save a ... save b ... save c ---a---b---c (master) get a ... save d ... save e ... save f d---e---f (a-branch) / -----a----b---c (master) Now, you want to go back to try something different at "c": get c ... save g d---e---f (a-branch) / ----a----b---c (master) \ g (c-branch CURRENT HEAD) You could also go back to master, and restart from there: get master ... save h ... save i d---e---f (a-branch) / ----a----b---c---h---i (master CURRENT HEAD) \ g (c-branch) Merging of branches is not supported. =head2 Exiting When you type C Nama will automatically save your work to F. If you I want this behavior, use Ctrl-C to exit Nama. =head2 Jack ports list file Use I to ask Nama to connect multiple JACK ports listed in a file F to the input port(s) of that track. If the track is stereo, ports from the list are alternately connected to left and right channels. =head2 Track edits An edit consists of audio clips and data structures associated with a particular track and version. The edit replaces part of the original audio file, allowing you to fix wrong notes, or substitute one phrase for another. Behind the scenes, the host track becomes the mix track to a bus. Sources for the bus are the original audio track, and zero or more edits, each represented by a hidden track. Each track can host multiple edits. Edits are non-destructive; they are achieved by using Ecasound's ability to crossfade and sequence. Before creating the edit, select the track and version to be edited. You will now need to create three marks: =over 4 =item * play start point =item * rec start point =item * rec end point =back The edit will replace the audio between the rec start and rec end points. There are two ways to set these points. =head3 set-edit-points command Position the playback head a few seconds before the edit. Enter the I command. This will start the engine. Hit the B

key three times to designate the playback start, punch-in and punch-out positions. =head3 Specify points individually Position the playback head at the position you want playback for the edit to start. Enter the I command. Use the same procedure to set the rec start and rec end positions using the I and I commands. =head3 Create the edit Enter the I command to create the necessary tracks and data structures. Use I to confirm the edit positions. The engine will run and you will hear the host track with the target region removed. Playback will be restricted to the edit region. You may use I to hear the clip to be removed. Use I to see the edit marks and I to nudge them into perfect position. Once you are satisfied with the mark positions, you are ready to record your edit. Enter I. Playback will begin at first mark. The replacement clip will be recorded from the source specified in the original track. Each I command will record an additional version on the edit track. I will delete (destructively) the most recent audio clip and begin recording anew. I deletes (destructively) the current edit. You may specify another range for editing and use the editing procedure again as many times as you like. Edits on one track may not overlap. =head3 Merging edits I will recursively merge all edits applied to the current track and version, creating a new version for the track. This will freeze your edits against an accidental change in mark, region or version settings. I will label the edits by index and time. I will restore normal playback mode =head1 REMOTE CONTROL You can send commands to Nama from a remote process, and retrieve the result. Understand that this code opens a remote execution hole. In F<.namarc> you need something like: remote_control_port: 57000 Then Nama will set up a listener for remote commands. The usual return value will be a single newline. However, if you send an 'eval' command followed by perl code, the return value will be the result of the perl code executed with a newline appended. If the result is a list, the items will be joined by spaces into a single string. For example, if you send this string: eval $this_track->name The return value will be the name of the current track. If the result is an object or data structure, it will be returned in a serialized form. =head1 TEXT COMMANDS =head2 Help commands =head4 B (h) - Display help on Nama commands. =over 8 help [ | | ] help marks # display the help category marks and all commands containing marks help 6 # display help on the effects category help mfx # display help on modify-effect - shortcut mfx =back =head4 B (hfx he) - Display detailed help on LADSPA or LV2 effects. =over 8 help-effect | help-effect 1970 # display help on Fons Adriaensen's parametric EQ (LADSPA) help-effect etd # prints a short message to consult Ecasound manpage, # where the etd chain operator is documented. hfx lv2-vocProc # display detailed help on the LV2 VocProc effect =back =head4 B (ffx fe) - Display one-line help for effects matching the search string(s). =over 8 find-effect [ ... ] find-effect compressor # List all effects containing "compressor" in their name or parameters fe feedback # List all effects matching "feedback" # (for example a delay with a feedback parameter) =back =head2 General commands =head4 B (quit q) - Exit Nama, saving settings (the current project). =over 8 exit =back =head4 B - Cache WAV directory contents (default) =over 8 memoize =back =head4 B - Disable WAV directory caching. =over 8 unmemoize =back =head2 Transport commands =head4 B (s) - Stop transport. Stop the engine, when recording or playing back. =over 8 stop =back =head4 B (t) - Start the transport rolling =over 8 start rec # prepare the curent track to be recorded. start # Start the engine/transport rolling (play now!) stop # Stop the engine, cleanup, prepare to review =back =head4 B (gp) - Get the current playhead position (in seconds). =over 8 getpos start # Start the engine. gp # Get the current position of the playhead. Where am I? =back =head4 B (sp) - Set current playhead position (in seconds). =over 8 setpos setpos 65.5 =back =head4 B (fw) - Move playback position forwards (in seconds). =over 8 forward fw 23.7 =back =head4 B (rw) - Move playback position backwards (in seconds). =over 8 rewind rewind 6.5 =back =head4 B (beg) - Set the playback head to the start. A synonym for setpos 0. =over 8 to-start =back =head4 B (end) - Set the playback head to end minus 10 seconds. =over 8 to-end =back =head4 B - Ecasound-only start. Nama will not monitor the transport. For diagnostic use. =over 8 ecasound-start =back =head4 B - Ecasound-only stop. Nama will not monitor the transport. For diagnostic use. =over 8 ecasound-stop =back =head4 B - Restart the Ecasound process. May help if Ecasound has crashed or is behaving oddly. =over 8 restart-ecasound =back =head4 B (song) - Enter the preview mode. Configure Nama for playback and passthru of live inputs without recording (for mic test, rehearsal, etc.) =over 8 preview rec # Set the current track to record from its source. preview # Enter the preview mode. start # Playback begins. You can play live, adjust effects, # forward, rewind, etc. stop # Stop processing audio. arm # Restore to normal recording/playback mode. =back =head4 B (live) - Enter doodle mode. Passthru of live inputs without recording. No playback. Intended for rehearsing and adjusting effects. =over 8 doodle doodle # Switch into doodle mode. start # start the audio engine. (fool around) stop # Stop processing audio. arm # Return to normal mode, allowing playback and record to disk =back =head2 Mix commands =head4 B (mxd) - Enter mixdown mode for subsequent engine runs. You will record a new mix each time you use the start command until you leave the mixdown mode using "mixoff". =over 8 mixdown mixdown # Enter mixdown mode start # Start the transport. The mix will be recorded by the # Mixdown track. The engine will run until the # longest track ends. (After mixdown Nama places # a symlink to the WAV file and possibly ogg/mp3 # encoded versions in the project directory.) mixoff # Return to the normal mode. =back =head4 B (mxp) - Enter Mixdown play mode, setting user tracks to OFF and only playing the Mixdown track. Use "mixoff" to leave this mode. =over 8 mixplay mixplay # Enter the Mixdown play mode. start # Play the Mixdown track. stop # Stop playback. mixoff # Return to normal mode. =back =head4 B (mxo) - Leave the mixdown or mixplay mode. Sets Mixdown track to OFF, user tracks to MON. =over 8 mixoff =back =head4 B - Normalize track volume levels and fix DC-offsets, then mixdown. =over 8 automix =back =head4 B (mr) - Turn on the mastering mode, adding tracks Eq, Low, Mid, High and Boost, if necessary. The mastering setup allows for one EQ and a three-band multiband compression and a final boosting stage. Using "master-off" to leave the mastering mode. =over 8 master-on mr # Turn on master mode. start # Start the playback. # Now you can adjust the Boost or global EQ. stop # Stop the engine. =back =head4 B (mro) - Leave mastering mode. The mastering network is disabled. =over 8 master-off =back =head2 Track commands =head4 B (add new) - Create a new audio track. =over 8 add-track add-track clarinet # create a mono track called clarinet with input # from soundcard channel 1. =back =head4 B (amt) - Create a new midi track. =over 8 add-midi-track add-midi-track clarinet =back =head4 B - Create one or more new tracks in one go. =over 8 add-tracks [ ... ] add-tracks violin viola contra_bass =back =head4 B (link) - Create a read-only track, that uses audio files from another track. =over 8 link-track [] link my_song_part1 Mixdown part_1 # Create a read-only track "part_1" in the current project # using files from track "Mixdown" in project "my_song_part_1". # link-track compressed_piano piano # Create a read-only track "compressed_piano" using files from # track "piano". This is one way to provide wet and dry # (processed and unprocessed) versions of same source. # Another way would be to use inserts. =back =head4 B (import) - Import a sound file (wav, ogg, mp3, etc.) to the current track, resampling it if necessary. The imported file is set as current version. =over 8 import-audio [ ] import /home/samples/bells.flac # import the file bells.flac to the current track import /home/music/song.mp3 44100 # import song.mp3, specifying the frequency =back =head2 Midi commands =head4 B (im) - Import a MIDI song file (MIDI tracks only) =over 8 import-midi =back =head2 Track commands =head4 B (route rt) - Set source and send for a track (see 'source' and 'send' commands) =over 8 route-track =back =head4 B - Directly set current track parameters (use with care!). =over 8 set-track =back =head4 B (rec) - Set the current track to record its source. Creates the monitoring route if necessary. Recording to disk will begin on the next engine start. Use the "mon" or "off" commands to disable recording. =over 8 record rec # Set the current track to record. start # A new version (take) will be written to disk, # creating a file such as sax_1.wav. Other tracks # may be recording or playing back as well. stop # Stop the recording/playback, automatically enter playback mode =back =head4 B - Set the current track to playback the currently selected version. Creates the monitoring route if necessary. The selected audio file will play the next time the engine starts. =over 8 play =back =head4 B - Create a monitoring route for the current track at the next opportunity. =over 8 mon =back =head4 B - Remove the monitoring route for the current track and all track I/O at the next opportunity. You can re-include it using "mon", "play" or "rec" commands. =over 8 off =back =head4 B (src r) - Set the current track's input (source), for example to a soundcard channel, or JACK client name =over 8 source | | | | | | 'jack' | 'null' source 3 # Take input from soundcard channel 3 (3/4 if track is stereo) # source null # Track's input is silence. This is useful for when an effect such # as a metronome or signal generator provides a source. # source bus Strings # set the Strings bus as source # source track trumpet # set the track trumpet as source # source LinuxSampler # set the JACK client named LinuxSampler as source # source synth:output_3 # use the signal from the JACK client synth, using the # port output_3 (see the jackd and jack_lsp manpages # for more information). # source jack # This leaves the track input exposed as JACK ports # such as Nama:sax_in_1 for manual connection. # source kit.ports # The JACK ports listed in the file kit.ports (if it exists) # will be connected to the track input. # # Ports are listed pairwise in the .ports files for stereo tracks. # This is convenient for use with the Hydrogen drumkit, # whose outputs use one JACK port per voice. =back =head4 B (aux) - Set an aux send for the current track. Remove sends with remove-send . =over 8 send | | send 3 # Send the track output to soundcard channel 3. send jconvolver # Send the track output to the jconvolver JACK client. =back =head4 B (nosend noaux) - Remove aux send from the current track. =over 8 remove-send =back =head4 B - Configure the current track to record two channels of audio =over 8 stereo =back =head4 B - Configure the current track to record one channel of audio =over 8 mono =back =head4 B (version ver) - Select a WAV file, by version number, for current track playback (Overrides a bus-level version setting) =over 8 set-version piano # Select the piano track. version 2 # Select the second recorded version sh # Display information about the current track =back =head4 B - Remove the currently selected recording version from the current track after confirming user intent. This DESTRUCTIVE command removes the underlying audio file from your disk. Use with caution. =over 8 destroy-current-wav =back =head4 B (lver) - List WAV versions of the current track. This will print the numbers. =over 8 list-versions list-versions # May print something like: 1 2 5 7 9 # The other versions might have been deleted earlier by you. =back =head4 B (v) - Change or show the current track's volume. =over 8 vol [ [ + | - | / | * ] ] vol * 1.5 # Multiply the current volume by 1.5 vol 75 # Set the current volume to 75 # Depending on your namarc configuration, this means # either 75% of full volume (-ea) or 75 dB (-eadb). vol - 5.7 # Decrease current volume by 5.7 (percent or dB) vol # Display the volume setting of the current track. =back =head4 B (c cut) - Mute the current track by reducing the volume parameter. Use "unmute" to restore the former volume level. =over 8 mute =back =head4 B (nomute C uncut) - Restore previous volume level. It can be used after mute or solo. =over 8 unmute =back =head4 B - Set the current track's volume to unity. This will change the volume to the default value (100% or 0 dB). =over 8 unity vol 55 # Set volume to 55 unity # Set volume to the unity value. vol # Display the current volume (should be 100 or 0, # depending on your settings in namarc.) =back =head4 B (sl) - Mute all tracks but the current track or the tracks or bunches specified. You can reverse this with nosolo. =over 8 solo [ | ] [ ] ... solo # Mute all tracks but the current track. nosolo # Unmute all tracks, restoring prior state. solo piano bass Drums # Mute everything but piano, bass and Drums. =back =head4 B (nsl) - Unmute all tracks which have been muted by a solo command. Tracks that had been muted before the solo command stay muted. =over 8 nosolo =back =head4 B - Unmute all tracks that are currently muted =over 8 all piano # Select track piano mute # Mute the piano track. sax # Select the track sax. solo # Mute other tracks nosolo # Unmute other tracks (piano is still muted) all # all tracks play =back =head4 B (p) - Change or display the current panning position of the current track. Panning is moving the audio in the stereo panorama between right and left. Position is given in percent. 0 is hard left and 100 hard right, 50% is dead centre. =over 8 pan [ ] pan 75 # Pan the track to a position between centre and hard right p 50 # Move the current track to the centre. pan # Show the current position of the track in the stereo panorama. =back =head4 B (pr) - Pan the current track hard right. this is a synonym for pan 100. Can be reversed with pan-back. =over 8 pan-right =back =head4 B (pl) - Pan the current track hard left. This is a synonym for pan 0. Can be reversed with pan-back. =over 8 pan-left =back =head4 B (pc) - Pan the current track to the centre. This is a synonym for pan 50. Can be reversed with pan-back. =over 8 pan-center =back =head4 B (pb) - Restore the current track's pan position prior to pan-left, pan-right or pan-center commands. =over 8 pan-back =back =head4 B (lt show) - Show a list of tracks, including their index number, volume, pan position, recording status and source. =over 8 show-tracks =back =head4 B (sha showa) - Like show-tracks, but includes hidden tracks as well. Useful for debugging. =over 8 show-tracks-all =back =head2 Bus commands =head4 B (shb) - List tracks in current or named bus =over 8 show-bus [ ] =back =head2 Track commands =head4 B (sh -fart) - Display full information about the current track: index, recording status, effects and controllers, inserts, the selected WAV version, and signal width (channel count). =over 8 show-track =back =head2 Setup commands =head4 B (shm) - Display the current record/playback mode. this will indicate the mode (doodle, preview, etc.) and possible record/playback settings. =over 8 show-mode =back =head2 Track commands =head4 B (shl) - Display the latency information for the current track. =over 8 show-track-latency =back =head2 Diagnostics commands =head4 B (shla) - Dump all latency data. =over 8 show-latency-all =back =head2 Track commands =head4 B (srg) - Specify a playback region for the current track using marks. Can be reversed with remove-region. =over 8 set-region sax # Select "sax" as the current track. setpos 2.5 # Move the playhead to 2.5 seconds. mark sax_start # Create a mark sp 120.5 # Move playhead to 120.5 seconds. mark sax_end # Create another mark set-region sax_start sax_end # Play only the audio from 2.5 to 120.5 seconds. =back =head4 B - Make a copy of the current track using the supplied a region definition. The original track is untouched. =over 8 add-region | | [ ] sax # Select "sax" as the current track. add-region sax_start 66.7 trimmed_sax # Create "trimmed_sax", a copy of "sax" with a region defined # from mark "sax_start" to 66.7 seconds. =back =head4 B (rrg) - Remove the region definition from the current track. Remove the current track if it is an auxiliary track. =over 8 remove-region =back =head4 B (shift playat pat) - Choose an initial delay before playing a track or region. Can be reversed by unshift-track. =over 8 shift-track | piano # Select "piano" as current track. shift 6.7 # Move the start of track to 6.7 seconds. =back =head4 B (unshift) - Restore the playback start time of a track or region to 0. =over 8 unshift-track =back =head4 B (mods mod) - Add/show modifiers for the current track (man ecasound for details). This provides direct control over Ecasound track modifiers It is not needed for normal work. =over 8 modifiers [ Audio file sequencing parameters ] modifiers select 5 15.2 # Apply Ecasound's select modifier to the current track. # The usual way to accomplish this is with a region definition. =back =head4 B (nomods nomod) - Remove modifiers from the current track. =over 8 nomodifiers =back =head4 B (ecanormalize) - Apply ecanormalize to the current track version. This will raise the gain/volume of the current track as far as possible without clipping and store it that way on disk. Note: this will permanently change the file. =over 8 normalize =back =head4 B (ecafixdc) - Fix the DC-offset of the current track using ecafixdc. Note: This will permanently change the file. =over 8 fixdc =back =head4 B (autofix) - Apply ecafixdc and ecanormalize to all current versions of all tracks, set to playback (MON). =over 8 autofix-tracks =back =head4 B - Remove the current track with its effects, inserts, etc. Audio files are unchanged. =over 8 remove-track =back =head2 Group commands =head4 B (bver gver) - Set the default monitoring version for tracks in the current bus. =over 8 bus-version =back =head4 B - Restore tracks belonging to bus after bus-off =over 8 bus-on =back =head4 B - Turn off tracks belonging to current bus =over 8 bus-off =back =head4 B (abn) - =over 8 add-bunch [ | ] ... add-bunch strings violin cello bass # Create a bunch "strings" with tracks violin, cello and bass. for strings; mute # Mute all tracks in the strings bunch. for strings; vol * 0.8 # Lower the volume of all tracks in bunch "strings" by a # a factor of 0.8. =back =head4 B (lbn) - Display a list of all bunches and their tracks. =over 8 list-bunches =back =head4 B (rbn) - Remove the specified bunches. This does not remove the tracks, only the grouping. =over 8 remove-bunch [ ] ... =back =head4 B (atbn) - Add track(s) to an existing bunch. =over 8 add-to-bunch [ ] ... add-to-bunch woodwind oboe sax flute =back =head2 Project commands =head4 B (cm) - Commit Nama's current state =over 8 commit =back =head4 B - Git tag the current branch HEAD commit =over 8 tag [] =back =head4 B (br) - Change to named branch =over 8 branch =back =head4 B (lb lbr) - List branches =over 8 list-branches =back =head4 B (nbr) - Create a new branch =over 8 new-branch [] =back =head4 B (keep save) - Save the project settings as file or git snapshot =over 8 save-state [ [ ] ] =back =head4 B (get recall retrieve) - Retrieve project settings from file or snapshot =over 8 get-state =back =head4 B (lp) - List all projects. This will list all Nama projects, which are stored in the Nama project root directory. =over 8 list-projects =back =head4 B (create) - Create or open a new empty Nama project. =over 8 new-project create jam =back =head4 B (load) - Load an existing project. This will load the project from the default project state file. If you wish to load a project state saved to a user specific file, load the project and then use get-state. =over 8 load-project load my_old_song =back =head4 B (project name) - Display the name of the current project. =over 8 project-name =back =head4 B (npt) - Make a project template based on the current project. This will include tracks and busses. =over 8 new-project-template [ ] new-project_template my_band_setup "tracks and busses for bass, drums and me" =back =head4 B (upt apt) - Use a template to create tracks in a newly created, empty project. =over 8 use-project-template apt my_band_setup # Will add all the tracks for your basic band setup. =back =head4 B (lpt) - List all project templates. =over 8 list-project-templates =back =head4 B - Remove one or more project templates. =over 8 destroy-project-template [ ] ... =back =head2 Setup commands =head4 B (gen) - Generate an Ecasound chain setup for audio processing manually. Mainly useful for diagnostics and debugging. =over 8 generate =back =head4 B - Generate and connect a setup to record or playback. If you are in dodle or preview mode, this will bring you back to normal mode. =over 8 arm =back =head4 B (arms) - Generate and connect the setup and then start. This means, that you can directly record or listen to your tracks. =over 8 arm-start =back =head4 B (con) - Connect the setup, so everything is ready to run. Ifusing JACK, this means, that Nama will connect to all the necessary JACK ports. =over 8 connect =back =head4 B (dcon) - Disconnect the setup. If running with JACK, this will disconnect from all JACK ports. =over 8 disconnect =back =head4 B (chains) - Show the underlying Ecasound chain setup for the current working condition. Mainly useful for diagnostics and debugging. =over 8 show-chain-setup =back =head4 B (l) - Loop the playback between two points. Can be stopped with loop_disable =over 8 loop | | | | loop 1.5 10.0 # Loop between 1.5 and 10.0 seconds. loop 1 5 # Loop between marks with indices 1 and 5, see list-marks. loop sax_start 12.6 # Loop between mark sax_start and 12.6 seconds. =back =head4 B (nl) - Disable looping. =over 8 noloop =back =head2 Effect commands =head4 B (acl) - Add a controller to an effect (current effect, by default). Controllers can be modified by using mfx and removed using rfx. =over 8 add-controller [ ] [ ] ... add-effect etd 100 2 2 50 50 # Add a stero delay of 100ms. # the delay will get the effect ID E . # Now we want to slowly change the delay to 200ms. acl E klg 1 100 200 2 0 100 15 200 # Change the delay time linearly (klg) =back =head4 B (afx) - Add an effect =over 8 add-effect [ (before | first | last ) ] [ ... ] "before", "first" and "last" can be abbreviated "b", "f" and "l", respectively. We want to add the decimator effect (a LADSPA plugin). help-effect decimator # Print help about its paramters/controls. # We see two input controls: bitrate and samplerate afx decimator 12 22050 # prints "Added GO (Decimator)" # We have added the decimator with 12bits and a sample rate of 22050Hz. # GO is the effect ID, which you will need to modify it. =back =head4 B (afxl) - Same as add-effect last =over 8 add-effect-last [ ... ] =back =head4 B (afxf) - Same as add-effect first =over 8 add-effect-first [ ... ] =back =head4 B (afxb insert-effect ifx) - Same as add-effect before =over 8 add-effect-before [ ... ] =back =head4 B (mfx) - Modify effect parameter(s). =over 8 modify-effect [ ] [ + | - | * | / ] fx_alias can be: a position, effect ID, nickname or effect code To change the roomsize of our reverb effect to 62 lfx # We see that reverb has unique effect ID AF and roomsize is the # first parameter. mfx AF 1 62 # mfx AF,BG 1 75 # Change the first parameter of both AF and BG to 75. # mfx CE 6,10 -3 # Change parameters 6 and 10 of effect CE to -3 # mfx D 4 + 10 # Increase the fourth parameter of effect D by 10. # mfx A,B,C 3,6 * 5 # Adjust parameters 3 and 6 of effect A, B and C by a factor of 5. =back =head4 B (rfx) - Remove effects. They don't have to be on the current track. =over 8 remove-effect [ ] ... =back =head4 B (pfx) - Position an effect before another effect (use 'ZZZ' for end). =over 8 position-effect position-effect G F # Move effect with unique ID G immediately before effect F =back =head4 B (sfx) - Show information about an effect, defaulting to current effect =over 8 show-effect [ ] [ ] ... sfx # Display name, unique ID and parameters/controls of the current effect. sfx H # Display info on effect with unique ID H. H becomes the current effect. =back =head4 B (dfx) - Dump all data of current effect object =over 8 dump-effect =back =head4 B (lfx) - Print a short list of all effects on the current track, only including unique ID and effect name. =over 8 list-effects =back =head2 General commands =head4 B (hk) - Use this command to set the hotkey mode. (You may also use the hash symbol '#'.) Hit the Escape key to return to command mode. =over 8 hotkeys =back =head4 B (hka) - Activate hotkeys mode after each command. =over 8 hotkeys-always =back =head4 B (hko) - Disable hotkeys always mode =over 8 hotkeys-off =back =head4 B (hkl lhk) - List hotkey bindings =over 8 hotkeys-list =back =head2 Effect commands =head4 B (ain) - Add an external send/return insert to current track. =over 8 add-insert External: ( pre | post ) [ ] Local wet/dry: local add-insert pre jconvolver # Add a prefader insert. The current track signal is sent # to jconvolver and returned to the vol/pan controls. add-insert post jconvolver csound # Send the current track postfader signal (after vol/pan # controls) to jconvolver, getting the return from csound. guitar # Select the guitar track ain local # Create a local insert guitar-1-wet # Select the wet arm afx G2reverb 50 5.0 0.6 0.5 0 -16 -20 # add a reverb afx etc 6 100 45 2.5 # add a chorus effect on the reverbed signal guitar # Change back to the main guitar track wet 25 # Set the balance between wet/dry track to 25% wet, 75% dry. =back =head4 B (wet) - Set wet/dry balance of the insert for the current track. The balance is given in percent, 0 meaning dry and 100 wet signal only. =over 8 set-insert-wetness [ pre | post ] wet pre 50 # Set the prefader insert to be balanced 50/50 wet/dry. wet 100 # Simpler if there's only one insert =back =head4 B (rin) - Remove an insert from the current track. =over 8 remove-insert [ pre | post ] rin # If there is only one insert on the current track, remove it. remove-insert post # Remove the postfader insert from the current track. =back =head4 B (crg) - List all Ecasound controllers. Controllers include linear controllers and oscillators. =over 8 ctrl-register =back =head4 B (prg) - List all Ecasound effect presets. See the Ecasound manpage for more detail on effect_presets. =over 8 preset-register =back =head4 B (lrg) - List all LADSPA plugins, that Ecasound/Nama can find. =over 8 ladspa-register =back =head2 Mark commands =head4 B (lmk lm) - List all marks with index, name and their respective positions in time. =over 8 list-marks =back =head4 B (tmk tom) - Move the playhead to the named mark or mark index. =over 8 to-mark | to-mark sax_start # Jump to the position marked by sax_mark. tmk 2 # Move to the mark with the index 2. =back =head4 B (mark amk k) - Drop a new mark at the current playback position. this will fail, if a mark is already placed on that exact position. =over 8 add-mark [ ] mark start # Create a mark named "start" at the current position. =back =head4 B (rmk) - Remove a mark =over 8 remove-mark [ | ] remove-mark start # remove the mark named start rmk 16 # Remove the mark with the index 16. rmk # Remove the current mark =back =head4 B (nmk) - Move the playhead to the next mark. =over 8 next-mark =back =head4 B (pmk) - Move the playhead to the previous mark. =over 8 previous-mark =back =head4 B - Give a name to the current mark. =over 8 name-mark =back =head4 B (move-mark mmk) - Change the position (time) of the current mark. =over 8 modify-mark [ + | - ] move-mark + 2.3 # Move the current mark 2.3 seconds forward from its mmk 16.8 # Move the current mark to 16.8 seconds, no matter, where it is now. =back =head2 Diagnostics commands =head4 B (egs) - Display the Ecasound audio processing engine status. =over 8 engine-status =back =head4 B (dump) - Dump current track data. =over 8 dump-track =back =head4 B (dumpg) - Dump group settings for user tracks. =over 8 dump-group =back =head4 B (dumpa) - Dump most internal state data. =over 8 dump-all =back =head4 B - Show chain inputs and outputs. =over 8 dump-io =back =head2 Help commands =head4 B (lh) - List the command history. Every project stores its own command history. =over 8 list-history =back =head2 Bus commands =head4 B - Create a submix using all tracks in bus "Main" =over 8 add-submix-cooked add-submix-cooked front_of_house 7 # send a custom mix named "front_of_house" # to soundcard channels 7/8 =back =head4 B (asr) - Add a submix using tracks in Main bus (unprocessed signals, lower latency) =over 8 add-submix-raw asbr Reverb jconv # Add a raw send bus called Reverb, with its output =back =head4 B (abs) - Add a sub bus. This is a bus, as known from other DAWs. The default output goes to a mix track and that is routed to the mixer (the Main track). All busses begin with a capital letter! =over 8 add-bus [ | | ] abs Brass # Add a bus, "Brass", routed to the Main bus (e.g. mixer) abs special csound # Add a bus, "special" routed to JACK client "csound" =back =head4 B (usm) - Include tracks added since the submix was created. =over 8 update-submix update-submix Reverb =back =head4 B - Remove a bus or submix =over 8 remove-bus =back =head4 B (lbs) - List buses and their parameters (TODO). =over 8 list-buses =back =head4 B (sbs) - Set bus parameters. This command is intended for advanced users. =over 8 set-bus =back =head2 Effect commands =head4 B (oec) - Create a new effect chain, overwriting an existing one of the same name. =over 8 overwrite-effect-chain Same as for new-effect-chain =back =head4 B (nec) - Create an effect chain, a named list of effects with all parameter settings. Useful for storing effect setups for particular instruments. =over 8 new-effect-chain [ ... ] new-effect-chain my_piano # Create a new effect chain, "my_piano", storing all # effects and their settings from the current track # except the fader (vol/pan) settings. nec my_guitar A C F G H # Create a new effect chain, "my_guitar", # storing the effects with IDs A, C, F, G, H and # their respective settings. =back =head4 B (dec destroy-effect-chain) - Delete an effect chain definition. Does not affect the project state. This command is not reversible by undo. =over 8 delete-effect-chain =back =head4 B (fec lec) - Dump effect chains, filtering on key pairs (if provided) =over 8 find-effect-chains [ ] ... fec # List all effect chains with their effects. =back =head4 B (fuec leca) - List all *user* created effect chains, matching key/value pairs, if provided. =over 8 find-user-effect-chains [ ] ... =back =head4 B (bypass bfx) - Bypass effects on the current track. With no parameters default to bypassing the current effect. =over 8 bypass-effects [ ... | 'all' ] bypass all # Bypass all effects on the current track, except vol and pan. bypass AF # Only bypass the effect with the unique ID AF. =back =head4 B (restore-effects bbfx) - Restore effects. If no parameter is given, the default is to restore the current effect. =over 8 bring-back-effects [ ... | 'all' ] bbfx # Restore the current effect. restore_effect AF # Restore the effect with the unique ID AF. bring-back-effects all # Restore all effects. =back =head4 B (nep) - Create a new effect profile. An effect profile is a named group of effect chains for multiple tracks. Useful for storing a basic template of standard effects for a group of instruments, like a drum kit. =over 8 new-effect-profile [ ] add-bunch Drums snare toms kick # Create a buch called Drums. nep Drums my_drum_effects # Create an effect profile, call my_drum_effects =back =head4 B (aep) - Apply an effect profile. this will add all the effects in it to the list of tracks stored in the effect profile. Note: You must give the tracks the same names as in the original project, where you created the effect profile. =over 8 apply-effect-profile =back =head4 B - Delete an effect profile. This will delete the effect profile definition from your disk. All projects, which use this effect profile will NOT be affected. =over 8 destroy-effect-profile =back =head4 B (lep) - List all effect profiles. =over 8 list-effect-profiles =back =head4 B (sepr) - List effect profile. =over 8 show-effect-profiles =back =head4 B (fep) - Dump effect profile data structure. =over 8 full-effect-profiles =back =head2 Track commands =head4 B (cache ct bounce freeze) - Cache the current track. Same as freezing or bouncing. This is useful for larger projects or low-power CPUs, since effects do not have to be recomputed for subsequent engine runs. Cache_track stores the effects-processed output of the current track as a new version (WAV file) which becomes the current version. The current effects, inserts and region definition are removed and stored. To go back to the original track state, use the uncache-track command. The show-track display appends a "c" to version numbers created by cache-track (and therefore reversible by uncache) =over 8 cache-track [ ] cache 10 # Cache the curent track and append 10 seconds extra time, =back =head2 Effect commands =head4 B (uncache unc) - Select the uncached track version. This restores effects, but not inserts. =over 8 uncache-track =back =head2 General commands =head4 B (do) - Execute Nama commands from a file in the main project's directory or in the Nama project root directory. A script is a list of Nama commands, just as you would type them on the Nama prompt. =over 8 do-script do prepare_my_drums # Execute the script prepare_my_drums. =back =head4 B - Re-read the project's .wav directory. Mainly useful for troubleshooting. =over 8 scan =back =head2 Effect commands =head4 B (afd fade) - Add a fade-in or fade-out to the current track. =over 8 add-fade ( in | out ) marks/times (see examples) fade in mark1 # Fade in,starting at mark1 and using the # default fade time of 0.5 seconds. fade out mark2 2 # Fade out over 2 seconds, starting at mark2 . fade out 2 mark2 # Fade out over 2 seconds, ending at mark2 . fade in mark1 mark2 # Fade in starting at mark1, ending at mark2 . =back =head4 B (rfd) - Remove a fade from the current track. =over 8 remove-fade [ ] ... list-fade # Print a list of all fades and their tracks. rfd 2 # Remove the fade with the index (n) 2. =back =head4 B (lfd) - List all fades. =over 8 list-fade =back =head2 Track commands =head4 B (comment ac) - Add a comment to the current track (replacing any previous comment). A comment maybe a short discription, notes on instrument settings, etc. =over 8 add-comment ac "Guitar, treble on 50%" =back =head4 B (rc) - Remove a comment from the current track. =over 8 remove-comment =back =head4 B (sc) - Show the comment for the current track. =over 8 show-comment =back =head4 B (sca) - Show all track comments. =over 8 show-comments =back =head4 B (avc) - Add a version comment (replacing any previous user comment). This will add a comment for the current version of the current track. =over 8 add-version-comment avc "The good take with the clear 6/8" =back =head4 B (rvc) - Remove version comment(s) from the current track. =over 8 remove-version-comment =back =head4 B (svc) - Show version comment(s) of the curent track. =over 8 show-version-comment =back =head4 B (svca) - Show all version comments for the current track. =over 8 show-version-comments-all =back =head4 B (asvc) - Set a system version comment. Useful for testing and diagnostics. =over 8 add-system-version-comment =back =head2 Edit commands =head4 B (ned) - Create an edit for the current track and version. =over 8 new-edit =back =head4 B (sep) - Mark play-start, record-start and record-end positions for the current edit. =over 8 set-edit-points =back =head4 B (led) - List all edits for current track and version. =over 8 list-edits =back =head4 B (sed) - Select an edit to modify or delete. After selection it is the current edit. =over 8 select-edit =back =head4 B (eem) - Switch back to normal playback/record mode. The track will play full length again. Edits are managed via a sub- bus. =over 8 end-edit-mode =back =head4 B - Remove an edit and all associated audio files. If no parameter is given, the default is to destroy the current edit. Note: The data will be lost permanently. Use with care! =over 8 destroy-edit [ ] =back =head4 B (pei) - Play the track region without the edit segment. =over 8 preview-edit-in =back =head4 B (peo) - Play the removed edit segment. =over 8 preview-edit-out =back =head4 B (ped) - Play a completed edit. =over 8 play-edit =back =head4 B (red) - Record an audio file for the current edit. =over 8 record-edit =back =head4 B (et) - Set the edit track as the current track. =over 8 edit-track =back =head4 B (hta) - Set the host track alias as the current track. =over 8 host-track-alias =back =head4 B (ht) - Set the host track (edit sub-bus mix track) as the current track. =over 8 host-track =back =head4 B (vmt) - Set the version mix track as the current track. =over 8 version-mix-track =back =head4 B (psm) - Select (and move to) play start mark of the current edit. =over 8 play-start-mark =back =head4 B (rsm) - Select (and move to) rec start mark of the current edit. =over 8 rec-start-mark =back =head4 B (rem) - Select (and move to) rec end mark of the current edit. =over 8 rec-end-mark =back =head4 B (spsm) - Set play-start-mark to the current playback position. =over 8 set-play-start-mark =back =head4 B (srsm) - Set rec-start-mark to the current playback position. =over 8 set-rec-start-mark =back =head4 B (srem) - Set rec-end-mark to current playback position. =over 8 set-rec-end-mark =back =head4 B (ded) - Turn off the edits for the current track and playback the original WAV file. This will remove the edit bus. =over 8 disable-edits =back =head4 B (med) - Mix edits and original into a new host-track. this will write a new audio file to disk and the host track will have a new version for this. =over 8 merge-edits =back =head2 Track commands =head4 B - Make the current track into a sub bus, with one track for each version. =over 8 explode-track =back =head4 B (mtb) - Move the current track to another bus. A new track is always in the Main bus. So to reverse this action use move-to-bus Main . =over 8 move-to-bus asub Drums # Create a new sub bus, called Drums. snare # Make snare the current track. mtb Drums # Move the snare track into the sub bus Drums. =back =head4 B (pvt) - Create a read-only track using the specified version of the current track. =over 8 promote-version-to-track =back =head2 General commands =head4 B (ruc) - Re-read the user customizations file 'custom.pl'. =over 8 read-user-customizations =back =head2 Setup commands =head4 B (lr) - Stop recording after the last audio file finishes playing. Can be turned off with limit-run-time_off. =over 8 limit-run-time [ ] =back =head4 B (lro) - Disable the recording stop timer. =over 8 limit-run-time-off =back =head4 B (ofr) - Record/play from a mark, rather than from the start, i.e. 0.0 seconds. =over 8 offset-run =back =head4 B (ofro) - Turn back to starting from 0. =over 8 offset-run-off =back =head2 General commands =head4 B (wview) - Launch mhwavedit to view/edit waveform of the current track and version. This requires to start Nama on a graphical terminal, like xterm or gterm or from GNOME via alt+F2 . =over 8 view-waveform =back =head4 B (wedit) - Launch audacity to view/edit the waveform of the current track and version. This requires starting Nama on a graphical terminal like xterm or gterm or from GNOME starting Nama using alt+F2 . =over 8 edit-waveform =back =head2 Setup commands =head4 B (rerec) - Record as before. This will set all the tracks to record, which have been recorded just before you listened back. =over 8 rerecord for piano guitar;rec # Set piano and guitar track to record. # do your recording and ilstening. # You want to record another version of both piano and guitar: rerec # Sets piano and guitar to record again. =back =head2 Track commands =head4 B (anl) - Print Ecasound amplitude analysis for current track. This will show highest volume and statistics. =over 8 analyze-level =back =head2 General commands =head4 B - Execute command(s) for several tracks. =over 8 for [ } ... ; for piano guitar; vol - 3; pan 75 # reduce volume and pan right for snare kick toms cymbals; mtb Drums # move tracks to bus Drums =back =head2 Project commands =head4 B - Execute git command in the project directory =over 8 git [arguments] =back =head2 Track commands =head4 B (ersh) - Edit the REC hook script for current track =over 8 edit-rec-setup-hook =back =head4 B (erch) - Edit the REC cleanup hook script for current track =over 8 edit-rec-cleanup-hook =back =head4 B (rffx) - Remove vol pan or fader on current track =over 8 remove-fader-effect vol | pan | fader =back =head4 B - Rename a track and its WAV files =over 8 rename-track =back =head2 Sequence commands =head4 B (nsq) - Define a new sequence =over 8 new-sequence =back =head4 B (slsq) - Select named sequence as current sequence =over 8 select-sequence =back =head4 B (lsq) - List all user sequences =over 8 list-sequences =back =head4 B (ssq) - Display clips making up current sequence =over 8 show-sequence =back =head4 B (asq) - Append items to sequence =over 8 append-to-sequence [,...] asq chorus # append chorus track to current sequence asq # append current track to current sequence =back =head4 B (isq) - Insert items into sequence before index i =over 8 insert-in-sequence [,...] =back =head4 B (rsq) - Remove items from sequence =over 8 remove-from-sequence [,...] =back =head4 B (dsq) - Delete entire sequence =over 8 delete-sequence =back =head4 B (asp) - Add a spacer to the current sequence, in specified position, or appending (if no position is given) =over 8 add-spacer [] =back =head4 B (csq) - Convert the current track to a sequence =over 8 convert-to-sequence =back =head4 B (msq) - Cache the current sequence mix track, and set it to PLAY =over 8 merge-sequence =back =head4 B - Create a sequence from the current track by removing the region(s) defined by mark pair(s). Not supported if the current track is already a sequence. =over 8 snip [...] snip cut1-start cut1-end cut2-start cut2-end This removes cut1 and cut2 regions from the current track by creating a sequence. =back =head4 B (compose-sequence compose-into-sequence) - Compose a new sequence using the region(s) of the named track defined by mark pair(s). If the sequence of that name exists, append the regions to that sequence (compose-into-sequence). =over 8 compose [...] compose speeches conference-audio speaker1-start speaker1-end speaker2-start speaker2-end This creates a "speeches" sequence with two clips for speaker1 and speaker2. =back =head2 General commands =head4 B - Roll back last commit (use "git log" to see specific commands) Note: redo is not supported yet =over 8 undo =back =head4 B - Restore the last undone commit (TODO) =over 8 redo =back =head4 B (show-head last-command last) - Show the last commit, which undo will roll back. A commit may contain multiple commands. The last_* aliases are meaningful when autosave: undo is set. In that case each commit contains only a single command =over 8 show-head-commit =back =head2 Mode commands =head4 B - Set eager mode =over 8 eager on | off =back =head2 Engine commands =head4 B (neg) - Start a named Ecasound engine, or bind to an existing engine =over 8 new-engine =back =head4 B (seg) - Select an ecasound engine (advanced users only!) =over 8 select-engine =back =head2 Track commands =head4 B (steg) - Set the current track's engine affiliation =over 8 set-track-engine-group =back =head2 Bus commands =head4 B (sbeg) - Set the current bus's engine affiliation =over 8 set-bus-engine-group =back =head4 B (ssm) - Set the target for the trim command =over 8 select-submix =back =head4 B (trim tsm) - Control a submix fader =over 8 trim-submix # reduce vol of current track in in_ear_monitor by 3dB select-submix in_ear_monitor trim vol - 3 =back =head2 Effect commands =head4 B (nfx nick) - Add a nickname to the current effect (and create an alias) =over 8 nickname-effect add-track guitar afx Plate nick reverb # current effect gets name "reverb1" mfx reverb1 1 0.05 # modify first reverb effect on current track mfx reverb 1 2 # works, because current track has one effect named "reverb" afx reverb # add another Plate effect, gets name "reverb2" rfx reverb # Error, multiple reverb effects are present on this # track. Please use a numerical suffix. mfx reverb2 1 3 # modify second reverb effect rfx reverb1 # removes reverb1 ifx reverb2 reverb # insert another reverb effect (reverb3) before reverb2 rfx reverb3 # remove reverb3 rfx reverb # removes reverb2, as it is the sole remain reverb effect =back =head4 B (dnd) - Delete a nickname definition. Previously named effects keep their names. =over 8 delete-nickname-definition afx Plate # add Plate effect nick reverb # name it "reverb", and create a nickname for Plate dnd reverb # removes nickname definition afx reverb # error =back =head4 B (rnick) - Remove the "name" attribute of the current effect =over 8 remove-nickname afx Plate nick reverb mfx reverb 1 3 rnick mfx reverb 1 3 # Error: effect named "reverb" not found on current track =back =head4 B (lnd) - List defined nicknames =over 8 list-nickname-definitions =back =head4 B (sen) - Set a nickname only (don't creating an alias) =over 8 set-effect-name =back =head4 B (ses) - Set an effect surname =over 8 set-effect-surname =back =head4 B (ren) - Remove current effect name =over 8 remove-effect-name =back =head4 B (res) - Remove current effect surname =over 8 remove-effect-surname =back =head2 Track commands =head4 B - Set a particular track as the current, or default track against which track-related commands are executed. =over 8 select-track | =back =head2 Midi commands =head4 B (tempo tp) - Set MIDI tempo (bpm) =over 8 set-tempo =back =head2 General commands =head4 B (ssr) - Configure the sample rate for the current project or report sample rate if no parameter =over 8 set-sample-rate =back =head1 REALTIME OPERATION Nama selects realtime or nonrealtime parameters based on the B, B and B fields in F<.namarc>. You can optionally specify the buffersizes as a multiple of the JACK period size. Note that for best realtime operation under JACK you will have to configure jackd appropriately as well. The B and B profiles are useful when using Nama/Ecasound for live fx processing or live monitoring. The B profile sets a small buffersize and other low latency settings whenever a soundcard or JACK client is connected. The B profile uses a bigger buffer, providing extended margins for stable operation. It is suitable for post-processing, or for recording without live monitoring responsibilities. The B profile defaults to nonrealtime settings. It switches to realtime, low-latency settings when a track has a live input. =head1 DIAGNOSTICS On any change in setup, the GUI display updates and C command is executed automatically showing what to expect the next time the engine is started. You can use the C command to verify the Ecasound chain setup. (The Ecasound command C will additionally store all engine data, effects as well as routing.) The C command displays data for the current track. The C command shows all state that would be saved. This is the same output that is written to the F file when you issue the C command. =head1 BUGS AND LIMITATIONS No latency compensation across signal paths is provided at present. This feature is under development. =head1 SECURITY CONCERNS If you are using Nama with the NetECI interface (i.e. if Audio::Ecasound is I installed) you should block TCP port 2868 if your computer is exposed to the Internet. =head1 INSTALLATION The following commands, available on Unixlike systems with Perl installed, will pull in Nama and other Perl libraries required for text mode operation: C -or- C To use the GUI, you will need to install Tk: C You may want to install Audio::Ecasound if you prefer not to run Ecasound in server mode: C You can pull the source code as follows: C Consult the F file for build instructions. =head1 SUPPORT The Nama mailing list is a suitable forum for questions regarding Nama installation, usage, bugs, feature requests, etc. http://www.freelists.org/list/nama For questions and discussion related to Ecasound https://lists.sourceforge.net/lists/listinfo/ecasound-list =head1 PATCHES The modules that make up this application are the preprocessed output from several source files. Patches against these source files are preferred. =head1 AUTHOR Joel Roth, Ejoelz@pobox.comE =head1 CONTRIBUTORS Alex Stone Brett McCoy Dubphil F. Silvain ++ Joy Bausch Julien Claassen ++ Kevin Utter Lars Bjørndal Philippe Schelté Philipp Überbacher Raphaël Mouneyres ++ Rusty Perez S. Massy ++ =head1 COPYRIGHT & LICENSE Copyright (c) 2009-2017 by Joel Roth. This is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, Version 3. Audio-Nama-1.216/lib/0000755000175000017500000000000013544212627013250 5ustar jrothjrothAudio-Nama-1.216/lib/Audio/0000755000175000017500000000000013544212627014311 5ustar jrothjrothAudio-Nama-1.216/lib/Audio/Nama.pm0000644000175000017500000072474413544212613015540 0ustar jrothjrothpackage Audio::Nama; require 5.14.4; use vars qw($VERSION); $VERSION = "1.216"; use Modern::Perl; #use Carp::Always; no warnings qw(uninitialized syntax); ########## External dependencies ########## use Carp qw(carp cluck confess croak); use Cwd; use Data::Section::Simple qw(get_data_section); use File::Find::Rule; use File::Path; use File::Spec; use File::Spec::Link; use File::Temp; use Getopt::Long; use Git::Repository; use Graph; use IO::Socket; use IO::Select; use IPC::Open3; use Log::Log4perl qw(get_logger :levels); use Module::Load::Conditional qw(can_load); use Module::Load; use Parse::RecDescent; use Storable qw(thaw); use Term::ReadLine; use Text::Diff; use Text::Format; use Try::Tiny; # use File::HomeDir;# Assign.pm # use File::Slurp; # several # use List::Util; # Fade.pm # use List::MoreUtils; # Effects.pm # use Time::HiRes; # automatically detected # use Tk; # loaded conditionally # use Event; # loaded conditionally # use AnyEvent; # loaded after Tk or Event # use AnyEvent::Term::TermKey; # --ditto-- # use jacks; # JACK server API # use Protocol::OSC; ########## Nama modules ########### # # Note that :: in the *.p source files is expanded by # SKIP_PREPROC # preprocessing to Audio::Nama in the generated *.pm files. # SKIP_PREPROC # ::Assign becomes Audio::Nama::Assign # SKIP_PREPROC # # These modules import functions and variables # use Audio::Nama::Assign qw(:all); use Audio::Nama::Globals qw(:all); use Audio::Nama::Util qw(:all); # Import the two user-interface classes use Audio::Nama::Text; use Audio::Nama::Graphical; # They are descendents of a base class we define in the root namespace our @ISA; # no ancestors use Audio::Nama::Object qw(); # based on Object::Tiny sub hello {"superclass hello"} sub new { my $class = shift; return bless {@_}, $class } # The singleton $ui belongs to either the Audio::Nama::Text or Audio::Nama::Graphical class # depending on command line flags (-t or -g). # This (along with the availability of Tk) # determines whether the GUI comes up. The Text UI # is *always* available in the terminal that launched # Nama. # How is $ui->init_gui interpreted? If $ui belongs to class # Audio::Nama::Text, Nama finds a no-op init_gui() stub in package Audio::Nama::Text # and does nothing. # If $ui belongs to class Audio::Nama::Graphical, Nama looks for # init_gui() in package Audio::Nama::Graphical, finds nothing, so goes to # look in the base class. All graphical methods (found in # Graphical_subs.pl) are defined in the root namespace so they can # call Nama core methods without a package prefix. ######## Nama classes ######## use Audio::Nama::Track; use Audio::Nama::Bus; use Audio::Nama::Sequence; use Audio::Nama::Mark; use Audio::Nama::IO; use Audio::Nama::Insert; use Audio::Nama::Fade; use Audio::Nama::Edit; use Audio::Nama::EffectChain; use Audio::Nama::Lat; use Audio::Nama::Engine; use Audio::Nama::Waveform; ####### Nama Roles - loaded by another class # use Audio::Nama::Wav; ####### Nama subroutines ###### # # The following modules serve only to define and segregate subroutines. # They occupy the root namespace (except Audio::Nama::ChainSetup) # and do not execute any code when use'd. use Audio::Nama::AnalyseLV2; use Audio::Nama::Initializations (); use Audio::Nama::Options (); use Audio::Nama::Config (); use Audio::Nama::Custom (); use Audio::Nama::Terminal (); use Audio::Nama::Grammar (); use Audio::Nama::Help (); use Audio::Nama::Project (); use Audio::Nama::Persistence (); use Audio::Nama::Git; use Audio::Nama::ChainSetup (); # separate namespace use Audio::Nama::Graph (); use Audio::Nama::Modes (); use Audio::Nama::Mix (); use Audio::Nama::Memoize (); use Audio::Nama::StatusSnapshot (); use Audio::Nama::EngineSetup (); use Audio::Nama::EffectsRegistry (); use Audio::Nama::Effect q(:all); use Audio::Nama::MuteSoloFade (); use Audio::Nama::Jack (); use Audio::Nama::Regions (); use Audio::Nama::CacheTrack (); use Audio::Nama::Bunch (); use Audio::Nama::Wavinfo (); use Audio::Nama::Midi (); use Audio::Nama::Latency (); use Audio::Nama::Log qw(logit loggit logpkg logsub initialize_logger); use Audio::Nama::TrackUtils (); sub main { say eval join(get_data_section('banner'), qw(" ")); bootstrap_environment() ; load_project(); nama_cmd($config->{execute_on_project_load}); reconfigure_engine(); nama_cmd($config->{opts}->{X}); $ui->loop(); } sub bootstrap_environment { definitions(); process_command_line_options(); start_logging(); setup_grammar(); setup_hotkey_grammar(); initialize_interfaces(); } sub kill_and_reap { my @pids = @_; map{ my $pid = $_; map{ my $signal = $_; kill $signal, $pid; sleeper(0.2); } (15,9); waitpid $pid, 1; } @pids; } sub cleanup_exit { logsub("&cleanup_exit"); remove_riff_header_stubs(); trigger_rec_cleanup_hooks(); # for each process: # - SIGINT (1st time) # - allow time to close down # - SIGINT (2nd time) # - allow time to close down # - SIGKILL delete $project->{events}; Audio::Nama::Engine::sync_action('kill_and_reap'); $text->{term}->rl_deprep_terminal() if defined $text->{term}; exit; } END { } 1; __DATA__ @@ commands_yml --- help: type: help what: Display help on Nama commands. short: h parameters: [ | | ] example: | help marks # display the help category marks and all commands containing marks help 6 # display help on the effects category help mfx # display help on modify-effect - shortcut mfx help_effect: type: help what: Display detailed help on LADSPA or LV2 effects. short: hfx he parameters: | example: | help-effect 1970 ! display help on Fons Adriaensen's parametric EQ (LADSPA) help-effect etd ! prints a short message to consult Ecasound manpage, ! where the etd chain operator is documented. hfx lv2-vocProc ! display detailed help on the LV2 VocProc effect find_effect: type: help what: Display one-line help for effects matching the search string(s). short: ffx fe parameters: [ ... ] example: | find-effect compressor ! List all effects containing "compressor" in their name or parameters fe feedback ! List all effects matching "feedback" ! (for example a delay with a feedback parameter) exit: type: general what: Exit Nama, saving settings (the current project). short: quit q parameters: none memoize: type: general what: Cache WAV directory contents (default) parameters: none unmemoize: type: general what: Disable WAV directory caching. parameters: none stop: type: transport what: Stop transport. Stop the engine, when recording or playing back. short: s parameters: none start: type: transport what: Start the transport rolling short: t parameters: none example: | rec # prepare the curent track to be recorded. start # Start the engine/transport rolling (play now!) stop # Stop the engine, cleanup, prepare to review getpos: type: transport what: Get the current playhead position (in seconds). short: gp parameters: none example: | start # Start the engine. gp # Get the current position of the playhead. Where am I? setpos: type: transport what: Set current playhead position (in seconds). short: sp parameters: example: setpos 65.5 # set current position to 65.5 seconds. forward: type: transport what: Move playback position forwards (in seconds). short: fw parameters: example: fw 23.7 # forward 23.7 seconds from the current position. rewind: type: transport what: Move playback position backwards (in seconds). short: rw parameters: example: rewind 6.5 # Move backwards 6.5 seconds from the current position. to_start: type: transport what: Set the playback head to the start. A synonym for setpos 0. short: beg parameters: none to_end: type: transport what: Set the playback head to end minus 10 seconds. short: end parameters: none ecasound_start: type: transport what: | Ecasound-only start. Nama will not monitor the transport. For diagnostic use. parameters: none ecasound_stop: type: transport what: | Ecasound-only stop. Nama will not monitor the transport. For diagnostic use. parameters: none restart_ecasound: type: transport what: | Restart the Ecasound process. May help if Ecasound has crashed or is behaving oddly. parameters: none preview: type: transport what: | Enter the preview mode. Configure Nama for playback and passthru of live inputs without recording (for mic test, rehearsal, etc.) short: song parameters: none example: | rec # Set the current track to record from its source. preview # Enter the preview mode. start # Playback begins. You can play live, adjust effects, ! # forward, rewind, etc. stop # Stop processing audio. arm # Restore to normal recording/playback mode. doodle: type: transport short: live what: | Enter doodle mode. Passthru of live inputs without recording. No playback. Intended for rehearsing and adjusting effects. parameters: none example: | doodle # Switch into doodle mode. start # start the audio engine. (fool around) stop # Stop processing audio. arm # Return to normal mode, allowing playback and record to disk mixdown: type: mix what: | Enter mixdown mode for subsequent engine runs. You will record a new mix each time you use the start command until you leave the mixdown mode using "mixoff". short: mxd parameters: none example: | mixdown # Enter mixdown mode start # Start the transport. The mix will be recorded by the ! # Mixdown track. The engine will run until the ! # longest track ends. (After mixdown Nama places ! # a symlink to the WAV file and possibly ogg/mp3 ! # encoded versions in the project directory.) mixoff # Return to the normal mode. mixplay: type: mix what: | Enter Mixdown play mode, setting user tracks to OFF and only playing the Mixdown track. Use "mixoff" to leave this mode. short: mxp parameters: none example: | mixplay # Enter the Mixdown play mode. start # Play the Mixdown track. stop # Stop playback. mixoff # Return to normal mode. mixoff: type: mix what: | Leave the mixdown or mixplay mode. Sets Mixdown track to OFF, user tracks to MON. short: mxo parameters: none automix: type: mix what: Normalize track volume levels and fix DC-offsets, then mixdown. parameters: none master_on: type: mix what: | Turn on the mastering mode, adding tracks Eq, Low, Mid, High and Boost, if necessary. The mastering setup allows for one EQ and a three-band multiband compression and a final boosting stage. Using "master-off" to leave the mastering mode. short: mr parameters: none example: | mr # Turn on master mode. start # Start the playback. ! # Now you can adjust the Boost or global EQ. stop # Stop the engine. master_off: type: mix what: Leave mastering mode. The mastering network is disabled. short: mro parameters: none add_track: type: track what: Create a new audio track. short: add new parameters: example: | add-track clarinet ! create a mono track called clarinet with input ! from soundcard channel 1. add_midi_track: type: track what: Create a new midi track. short: amt parameters: example: | add-midi-track clarinet add_tracks: type: track what: Create one or more new tracks in one go. parameters: [ ... ] example: add-tracks violin viola contra_bass link_track: type: track what: Create a read-only track, that uses audio files from another track. short: link parameters: [] example: | link my_song_part1 Mixdown part_1 ! Create a read-only track "part_1" in the current project ! using files from track "Mixdown" in project "my_song_part_1". ! link-track compressed_piano piano ! Create a read-only track "compressed_piano" using files from ! track "piano". This is one way to provide wet and dry ! (processed and unprocessed) versions of same source. ! Another way would be to use inserts. import_audio: type: track what: | Import a sound file (wav, ogg, mp3, etc.) to the current track, resampling it if necessary. The imported file is set as current version. short: import parameters: [ ] example: | import /home/samples/bells.flac ! import the file bells.flac to the current track import /home/music/song.mp3 44100 ! import song.mp3, specifying the frequency import_midi: type: midi what: Import a MIDI song file (MIDI tracks only) short: im parameters: route_track: type: track short: route rt what: set source and send for a track (see 'source' and 'send' commands) parameters: set_track: type: track what: Directly set current track parameters (use with care!). parameters: record: type: track what: | Set the current track to record its source. Creates the monitoring route if necessary. Recording to disk will begin on the next engine start. Use the "mon" or "off" commands to disable recording. short: rec parameters: none example: | rec ! Set the current track to record. start ! A new version (take) will be written to disk, ! creating a file such as sax_1.wav. Other tracks ! may be recording or playing back as well. stop ! Stop the recording/playback, automatically enter playback mode play: type: track what: | Set the current track to playback the currently selected version. Creates the monitoring route if necessary. The selected audio file will play the next time the engine starts. parameters: none mon: type: track what: | Create a monitoring route for the current track at the next opportunity. parameters: none off: type: track what: | Remove the monitoring route for the current track and all track I/O at the next opportunity. You can re-include it using "mon", "play" or "rec" commands. parameters: none source: type: track what: | Set the current track's input (source), for example to a soundcard channel, or JACK client name short: src r parameters: | | | | | | | 'jack' | 'null' example: | source 3 ! Take input from soundcard channel 3 (3/4 if track is stereo) ! source null ! Track's input is silence. This is useful for when an effect such ! as a metronome or signal generator provides a source. ! source bus Strings ! set the Strings bus as source ! source track trumpet ! set the track trumpet as source ! source LinuxSampler ! set the JACK client named LinuxSampler as source ! source synth:output_3 ! use the signal from the JACK client synth, using the ! port output_3 (see the jackd and jack_lsp manpages ! for more information). ! source jack ! This leaves the track input exposed as JACK ports ! such as Nama:sax_in_1 for manual connection. ! source kit.ports ! The JACK ports listed in the file kit.ports (if it exists) ! will be connected to the track input. ! ! Ports are listed pairwise in the .ports files for stereo tracks. ! This is convenient for use with the Hydrogen drumkit, ! whose outputs use one JACK port per voice. send: type: track what: Set an aux send for the current track. Remove sends with remove-send . short: aux parameters: | | example: | send 3 # Send the track output to soundcard channel 3. send jconvolver # Send the track output to the jconvolver JACK client. remove_send: type: track what: Remove aux send from the current track. short: nosend noaux parameters: none stereo: type: track what: Configure the current track to record two channels of audio parameters: none mono: type: track what: Configure the current track to record one channel of audio parameters: none set_version: type: track what: | Select a WAV file, by version number, for current track playback (Overrides a bus-level version setting) short: version ver parameters: example: | piano # Select the piano track. version 2 # Select the second recorded version sh # Display information about the current track destroy_current_wav: type: track what: | Remove the currently selected recording version from the current track after confirming user intent. This DESTRUCTIVE command removes the underlying audio file from your disk. Use with caution. parameters: none list_versions: type: track what: | List WAV versions of the current track. This will print the numbers. short: lver parameters: none example: | list-versions # May print something like: 1 2 5 7 9 ! # The other versions might have been deleted earlier by you. vol: type: track what: Change or show the current track's volume. short: v parameters: [ [ + | - | / | * ] ] example: | vol * 1.5 # Multiply the current volume by 1.5 vol 75 # Set the current volume to 75 ! # Depending on your namarc configuration, this means ! # either 75% of full volume (-ea) or 75 dB (-eadb). vol - 5.7 # Decrease current volume by 5.7 (percent or dB) vol # Display the volume setting of the current track. mute: type: track what: | Mute the current track by reducing the volume parameter. Use "unmute" to restore the former volume level. short: c cut parameters: none unmute: type: track what: Restore previous volume level. It can be used after mute or solo. short: nomute C uncut parameters: none unity: type: track what: Set the current track's volume to unity. This will change the volume to the default value (100% or 0 dB). parameters: none example: | vol 55 # Set volume to 55 unity # Set volume to the unity value. vol # Display the current volume (should be 100 or 0, ! # depending on your settings in namarc.) solo: type: track what: | Mute all tracks but the current track or the tracks or bunches specified. You can reverse this with nosolo. short: sl parameters: [ | ] [ ] ... example: | solo # Mute all tracks but the current track. nosolo # Unmute all tracks, restoring prior state. solo piano bass Drums # Mute everything but piano, bass and Drums. nosolo: type: track what: | Unmute all tracks which have been muted by a solo command. Tracks that had been muted before the solo command stay muted. short: nsl parameters: none all: type: track what: | Unmute all tracks that are currently muted parameters: none example: | piano # Select track piano mute # Mute the piano track. sax # Select the track sax. solo # Mute other tracks nosolo # Unmute other tracks (piano is still muted) all # all tracks play pan: type: track what: | Change or display the current panning position of the current track. Panning is moving the audio in the stereo panorama between right and left. Position is given in percent. 0 is hard left and 100 hard right, 50% is dead centre. short: p parameters: [ ] example: | pan 75 # Pan the track to a position between centre and hard right p 50 # Move the current track to the centre. pan # Show the current position of the track in the stereo panorama. pan_right: type: track what: | Pan the current track hard right. this is a synonym for pan 100. Can be reversed with pan-back. short: pr parameters: none pan_left: type: track what: | Pan the current track hard left. This is a synonym for pan 0. Can be reversed with pan-back. short: pl parameters: none pan_center: type: track what: | Pan the current track to the centre. This is a synonym for pan 50. Can be reversed with pan-back. short: pc parameters: none pan_back: type: track what: | Restore the current track's pan position prior to pan-left, pan-right or pan-center commands. short: pb parameters: none show_tracks: type: track what: | Show a list of tracks, including their index number, volume, pan position, recording status and source. short: lt show parameters: none show_tracks_all: type: track what: Like show-tracks, but includes hidden tracks as well. Useful for debugging. short: sha showa parameters: none show_bus: type: bus what: list tracks in current or named bus short: shb parameters: [ ] show_track: type: track what: | Display full information about the current track: index, recording status, effects and controllers, inserts, the selected WAV version, and signal width (channel count). short: sh -fart parameters: none show_mode: type: setup what: | Display the current record/playback mode. this will indicate the mode (doodle, preview, etc.) and possible record/playback settings. short: shm parameters: none show_track_latency: type: track what: display the latency information for the current track. short: shl parameters: none show_latency_all: type: diagnostics what: Dump all latency data. short: shla parameters: none set_region: type: track what: | Specify a playback region for the current track using marks. Can be reversed with remove-region. short: srg parameters: example: | sax # Select "sax" as the current track. setpos 2.5 # Move the playhead to 2.5 seconds. mark sax_start # Create a mark sp 120.5 # Move playhead to 120.5 seconds. mark sax_end # Create another mark set-region sax_start sax_end ! Play only the audio from 2.5 to 120.5 seconds. add_region: type: track what: | Make a copy of the current track using the supplied a region definition. The original track is untouched. parameters: | | [ ] example: | sax # Select "sax" as the current track. add-region sax_start 66.7 trimmed_sax ! Create "trimmed_sax", a copy of "sax" with a region defined ! from mark "sax_start" to 66.7 seconds. remove_region: type: track what: | Remove the region definition from the current track. Remove the current track if it is an auxiliary track. short: rrg parameters: none shift_track: type: track what: | Choose an initial delay before playing a track or region. Can be reversed by unshift-track. short: shift playat pat parameters: | example: | piano # Select "piano" as current track. shift 6.7 # Move the start of track to 6.7 seconds. unshift_track: type: track what: Restore the playback start time of a track or region to 0. short: unshift parameters: none modifiers: type: track what: | Add/show modifiers for the current track (man ecasound for details). This provides direct control over Ecasound track modifiers It is not needed for normal work. short: mods mod parameters: [ Audio file sequencing parameters ] example: | modifiers select 5 15.2 ! Apply Ecasound's select modifier to the current track. ! The usual way to accomplish this is with a region definition. nomodifiers: type: track what: Remove modifiers from the current track. short: nomods nomod parameters: none normalize: type: track what: | Apply ecanormalize to the current track version. This will raise the gain/volume of the current track as far as possible without clipping and store it that way on disk. Note: this will permanently change the file. short: ecanormalize parameters: none fixdc: type: track what: | Fix the DC-offset of the current track using ecafixdc. Note: This will permanently change the file. short: ecafixdc parameters: none autofix_tracks: type: track what: Apply ecafixdc and ecanormalize to all current versions of all tracks, set to playback (MON). short: autofix parameters: none remove_track: type: track what: | Remove the current track with its effects, inserts, etc. Audio files are unchanged. parameters: none bus_version: type: group what: Set the default monitoring version for tracks in the current bus. short: bver gver parameters: none bus_on: type: group what: restore tracks belonging to bus after bus-off bus_off: type: group what: turn off tracks belonging to current bus add_bunch: type: group what: short: abn parameters: [ | ] ... example: | add-bunch strings violin cello bass ! Create a bunch "strings" with tracks violin, cello and bass. for strings; mute # Mute all tracks in the strings bunch. for strings; vol * 0.8 ! Lower the volume of all tracks in bunch "strings" by a ! a factor of 0.8. list_bunches: type: group what: Display a list of all bunches and their tracks. short: lbn parameters: none remove_bunch: type: group what: | Remove the specified bunches. This does not remove the tracks, only the grouping. short: rbn parameters: [ ] ... add_to_bunch: type: group what: Add track(s) to an existing bunch. short: atbn parameters: [ ] ... example: | add-to-bunch woodwind oboe sax flute commit: type: project what: commit Nama's current state short: cm parameters: tag: type: project what: git tag the current branch HEAD commit parameters: [] branch: type: project what: change to named branch short: br parameters: list_branches: type: project what: list branches short: lb lbr new_branch: type: project what: create a new branch short: nbr parameters: [] save_state: type: project what: Save the project settings as file or git snapshot short: keep save parameters: [ [ ] ] get_state: type: project what: Retrieve project settings from file or snapshot short: get recall retrieve parameters: list_projects: type: project what: List all projects. This will list all Nama projects, which are stored in the Nama project root directory. short: lp parameters: none new_project: type: project what: Create or open a new empty Nama project. short: create parameters: example: | create jam # creates empty project call "jam". # Now you can start adding your tracks, editing them and mixing them. load_project: type: project what: Load an existing project. This will load the project from the default project state file. If you wish to load a project state saved to a user specific file, load the project and then use get-state. short: load parameters: example: load my_old_song # Will load my_old_song just as you left it. project_name: type: project what: Display the name of the current project. short: project name parameters: none new_project_template: type: project what: Make a project template based on the current project. This will include tracks and busses. short: npt parameters: [ ] example: | new-project_template my_band_setup "tracks and busses for bass, drums and me" use_project_template: type: project what: Use a template to create tracks in a newly created, empty project. short: upt apt parameters: example: | apt my_band_setup # Will add all the tracks for your basic band setup. list_project_templates: type: project what: List all project templates. short: lpt parameters: none destroy_project_template: type: project what: Remove one or more project templates. parameters: [ ] ... generate: type: setup what: Generate an Ecasound chain setup for audio processing manually. Mainly useful for diagnostics and debugging. short: gen parameters: none arm: type: setup what: Generate and connect a setup to record or playback. If you are in dodle or preview mode, this will bring you back to normal mode. parameters: none arm_start: type: setup what: Generate and connect the setup and then start. This means, that you can directly record or listen to your tracks. short: arms parameters: none connect: type: setup what: Connect the setup, so everything is ready to run. Ifusing JACK, this means, that Nama will connect to all the necessary JACK ports. short: con parameters: none disconnect: type: setup what: Disconnect the setup. If running with JACK, this will disconnect from all JACK ports. short: dcon parameters: none show_chain_setup: type: setup what: Show the underlying Ecasound chain setup for the current working condition. Mainly useful for diagnostics and debugging. short: chains parameters: none loop: type: setup what: Loop the playback between two points. Can be stopped with loop_disable short: l parameters: | | | | example: | loop 1.5 10.0 # Loop between 1.5 and 10.0 seconds. loop 1 5 # Loop between marks with indices 1 and 5, see list-marks. loop sax_start 12.6 # Loop between mark sax_start and 12.6 seconds. noloop: type: setup what: Disable looping. short: nl parameters: none add_controller: type: effect what: Add a controller to an effect (current effect, by default). Controllers can be modified by using mfx and removed using rfx. short: acl parameters: [ ] [ ] ... example: | add-effect etd 100 2 2 50 50 # Add a stero delay of 100ms. ! the delay will get the effect ID E . ! Now we want to slowly change the delay to 200ms. acl E klg 1 100 200 2 0 100 15 200 # Change the delay time linearly (klg) # from 100ms at the start (0 seconds) to 200ms at 15 seconds. See # help for -klg in the Ecasound manpage. add_effect: type: effect what: Add an effect short: afx parameters: | [ (before | first | last ) ] [ ... ] "before", "first" and "last" can be abbreviated "b", "f" and "l", respectively. example: | We want to add the decimator effect (a LADSPA plugin). help-effect decimator # Print help about its paramters/controls. ! # We see two input controls: bitrate and samplerate afx decimator 12 22050 # prints "Added GO (Decimator)" ! We have added the decimator with 12bits and a sample rate of 22050Hz. ! GO is the effect ID, which you will need to modify it. add_effect_last: type: effect what: same as add-effect last short: afxl parameters: [ ... ] add_effect_first: type: effect what: same as add-effect first short: afxf parameters: [ ... ] add_effect_before: type: effect what: same as add-effect before short: afxb insert_effect ifx parameters: [ ... ] modify_effect: type: effect what: Modify effect parameter(s). short: mfx parameters: | [ ] [ + | - | * | / ] fx_alias can be: a position, effect ID, nickname or effect code example: | To change the roomsize of our reverb effect to 62 lfx ! We see that reverb has unique effect ID AF and roomsize is the ! first parameter. mfx AF 1 62 ! mfx AF,BG 1 75 ! Change the first parameter of both AF and BG to 75. ! mfx CE 6,10 -3 ! Change parameters 6 and 10 of effect CE to -3 ! mfx D 4 + 10 ! Increase the fourth parameter of effect D by 10. ! mfx A,B,C 3,6 * 5 ! Adjust parameters 3 and 6 of effect A, B and C by a factor of 5. remove_effect: type: effect what: Remove effects. They don't have to be on the current track. short: rfx parameters: [ ] ... position_effect: type: effect what: Position an effect before another effect (use 'ZZZ' for end). short: pfx parameters: example: | position-effect G F # Move effect with unique ID G immediately before effect F show_effect: type: effect what: Show information about an effect, defaulting to current effect short: sfx parameters: [ ] [ ] ... example: | sfx # Display name, unique ID and parameters/controls of the current effect. sfx H # Display info on effect with unique ID H. H becomes the current effect. dump_effect: type: effect what: Dump all data of current effect object short: dfx list_effects: type: effect what: Print a short list of all effects on the current track, only including unique ID and effect name. short: lfx parameters: none hotkeys: type: general what: | Use this command to set the hotkey mode. (You may also use the hash symbol '#'.) Hit the Escape key to return to command mode. short: hk parameters: none hotkeys_always: type: general what: Activate hotkeys mode after each command. short: hka parameters: none hotkeys_off: type: general what: disable hotkeys always mode short: hko parameters: none hotkeys_list: type: general what: list hotkey bindings short: hkl lhk parameters: none add_insert: type: effect what: Add an external send/return insert to current track. short: ain parameters: | External: ( pre | post ) [ ] Local wet/dry: local example: | add-insert pre jconvolver ! Add a prefader insert. The current track signal is sent ! to jconvolver and returned to the vol/pan controls. add-insert post jconvolver csound ! Send the current track postfader signal (after vol/pan ! controls) to jconvolver, getting the return from csound. guitar # Select the guitar track ain local # Create a local insert guitar-1-wet # Select the wet arm afx G2reverb 50 5.0 0.6 0.5 0 -16 -20 # add a reverb afx etc 6 100 45 2.5 # add a chorus effect on the reverbed signal guitar # Change back to the main guitar track wet 25 # Set the balance between wet/dry track to 25% wet, 75% dry. set_insert_wetness: type: effect what: Set wet/dry balance of the insert for the current track. The balance is given in percent, 0 meaning dry and 100 wet signal only. short: wet parameters: [ pre | post ] example: | wet pre 50 # Set the prefader insert to be balanced 50/50 wet/dry. wet 100 # Simpler if there's only one insert remove_insert: type: effect what: Remove an insert from the current track. short: rin parameters: [ pre | post ] example: | rin # If there is only one insert on the current track, remove it. remove-insert post # Remove the postfader insert from the current track. ctrl_register: type: effect what: List all Ecasound controllers. Controllers include linear controllers and oscillators. short: crg parameters: none preset_register: type: effect what: List all Ecasound effect presets. See the Ecasound manpage for more detail on effect_presets. short: prg parameters: none ladspa_register: type: effect what: List all LADSPA plugins, that Ecasound/Nama can find. short: lrg parameters: none list_marks: type: mark what: List all marks with index, name and their respective positions in time. short: lmk lm parameters: none to_mark: type: mark what: Move the playhead to the named mark or mark index. short: tmk tom parameters: | example: | to-mark sax_start # Jump to the position marked by sax_mark. tmk 2 # Move to the mark with the index 2. add_mark: type: mark what: Drop a new mark at the current playback position. this will fail, if a mark is already placed on that exact position. short: mark amk k parameters: [ ] example: | mark start # Create a mark named "start" at the current position. remove_mark: type: mark what: Remove a mark short: rmk parameters: [ | ] example: | remove-mark start # remove the mark named start rmk 16 # Remove the mark with the index 16. rmk # Remove the current mark next_mark: type: mark what: Move the playhead to the next mark. short: nmk parameters: none previous_mark: type: mark what: Move the playhead to the previous mark. short: pmk parameters: none name_mark: type: mark what: Give a name to the current mark. parameters: modify_mark: type: mark what: Change the position (time) of the current mark. short: move_mark mmk parameters: [ + | - ] example: | move_mark + 2.3 # Move the current mark 2.3 seconds forward from its # current position. mmk 16.8 # Move the current mark to 16.8 seconds, no matter, where it is now. engine_status: type: diagnostics what: Display the Ecasound audio processing engine status. short: egs parameters: none dump_track: type: diagnostics what: Dump current track data. short: dump parameters: none dump_group: type: diagnostics what: Dump group settings for user tracks. short: dumpg parameters: none dump_all: type: diagnostics what: Dump most internal state data. short: dumpa parameters: none dump_io: type: diagnostics what: Show chain inputs and outputs. parameters: none list_history: type: help what: List the command history. Every project stores its own command history. short: lh parameters: none add_submix_cooked: type: bus what: Create a submix using all tracks in bus "Main" short: parameters: example: | add-submix-cooked front_of_house 7 ! send a custom mix named "front_of_house" ! to soundcard channels 7/8 add_submix_raw: type: bus what: Add a submix using tracks in Main bus (unprocessed signals, lower latency) short: asr parameters: example: | asbr Reverb jconv # Add a raw send bus called Reverb, with its output # going to the JACK client jconv . add_bus: type: bus what: Add a sub bus. This is a bus, as known from other DAWs. The default output goes to a mix track and that is routed to the mixer (the Main track). All busses begin with a capital letter! short: abs parameters: [ | | ] example: | abs Brass # Add a bus, "Brass", routed to the Main bus (e.g. mixer) abs special csound # Add a bus, "special" routed to JACK client "csound" update_submix: type: bus what: Include tracks added since the submix was created. short: usm parameters: example: update-submix Reverb # Include new tracks in the Reverb submix remove_bus: type: bus what: Remove a bus or submix parameters: list_buses: type: bus what: List buses and their parameters (TODO). short: lbs parameters: none set_bus: type: bus what: Set bus parameters. This command is intended for advanced users. short: sbs parameters: overwrite_effect_chain: type: effect what: | Create a new effect chain, overwriting an existing one of the same name. short: oec parameters: Same as for new-effect-chain new_effect_chain: type: effect what: | Create an effect chain, a named list of effects with all parameter settings. Useful for storing effect setups for particular instruments. short: nec parameters: [ ... ] example: | new-effect-chain my_piano ! Create a new effect chain, "my_piano", storing all ! effects and their settings from the current track ! except the fader (vol/pan) settings. nec my_guitar A C F G H ! Create a new effect chain, "my_guitar", ! storing the effects with IDs A, C, F, G, H and ! their respective settings. delete_effect_chain: type: effect what: | Delete an effect chain definition. Does not affect the project state. This command is not reversible by undo. short: dec destroy_effect_chain parameters: find_effect_chains: type: effect what: Dump effect chains, filtering on key pairs (if provided) short: fec lec parameters: [ ] ... example: | fec # List all effect chains with their effects. find_user_effect_chains: type: effect what: List all *user* created effect chains, matching key/value pairs, if provided. short: fuec leca parameters: [ ] ... bypass_effects: type: effect what: Bypass effects on the current track. With no parameters default to bypassing the current effect. short: bypass bfx parameters: [ ... | 'all' ] example: | bypass all # Bypass all effects on the current track, except vol and pan. bypass AF # Only bypass the effect with the unique ID AF. bring_back_effects: type: effect what: Restore effects. If no parameter is given, the default is to restore the current effect. short: restore_effects bbfx parameters: [ ... | 'all' ] example: | bbfx # Restore the current effect. restore_effect AF # Restore the effect with the unique ID AF. bring-back-effects all # Restore all effects. new_effect_profile: type: effect what: Create a new effect profile. An effect profile is a named group of effect chains for multiple tracks. Useful for storing a basic template of standard effects for a group of instruments, like a drum kit. short: nep parameters: [ ] example: | add-bunch Drums snare toms kick # Create a buch called Drums. nep Drums my_drum_effects # Create an effect profile, call my_drum_effects # containing all effects from snare toms and kick. apply_effect_profile: type: effect what: Apply an effect profile. this will add all the effects in it to the list of tracks stored in the effect profile. Note: You must give the tracks the same names as in the original project, where you created the effect profile. short: aep parameters: destroy_effect_profile: type: effect what: Delete an effect profile. This will delete the effect profile definition from your disk. All projects, which use this effect profile will NOT be affected. parameters: list_effect_profiles: type: effect what: List all effect profiles. short: lep parameters: none show_effect_profiles: type: effect what: List effect profile. short: sepr parameters: none full_effect_profiles: type: effect what: Dump effect profile data structure. short: fep parameters: none cache_track: type: track what: | Cache the current track. Same as freezing or bouncing. This is useful for larger projects or low-power CPUs, since effects do not have to be recomputed for subsequent engine runs. Cache_track stores the effects-processed output of the current track as a new version (WAV file) which becomes the current version. The current effects, inserts and region definition are removed and stored. To go back to the original track state, use the uncache-track command. The show-track display appends a "c" to version numbers created by cache-track (and therefore reversible by uncache) short: cache ct bounce freeze parameters: [ ] example: | cache 10 # Cache the curent track and append 10 seconds extra time, # to allow a reverb or delay to fade away without being cut. uncache_track: type: effect what: Select the uncached track version. This restores effects, but not inserts. short: uncache unc parameters: none do_script: type: general what: Execute Nama commands from a file in the main project's directory or in the Nama project root directory. A script is a list of Nama commands, just as you would type them on the Nama prompt. short: do parameters: example: | do prepare_my_drums # Execute the script prepare_my_drums. scan: type: general what: Re-read the project's .wav directory. Mainly useful for troubleshooting. parameters: none add_fade: type: effect what: Add a fade-in or fade-out to the current track. short: afd fade parameters: ( in | out ) marks/times (see examples) example: | fade in mark1 # Fade in,starting at mark1 and using the ! # default fade time of 0.5 seconds. fade out mark2 2 # Fade out over 2 seconds, starting at mark2 . fade out 2 mark2 # Fade out over 2 seconds, ending at mark2 . fade in mark1 mark2 # Fade in starting at mark1, ending at mark2 . remove_fade: type: effect what: Remove a fade from the current track. short: rfd parameters: [ ] ... example: | list-fade # Print a list of all fades and their tracks. rfd 2 # Remove the fade with the index (n) 2. list_fade: type: effect what: List all fades. short: lfd parameters: none add_comment: type: track what: Add a comment to the current track (replacing any previous comment). A comment maybe a short discription, notes on instrument settings, etc. short: comment ac parameters: example: ac "Guitar, treble on 50%" remove_comment: type: track what: Remove a comment from the current track. short: rc parameters: none show_comment: type: track what: Show the comment for the current track. short: sc parameters: none show_comments: type: track what: Show all track comments. short: sca parameters: none add_version_comment: type: track what: Add a version comment (replacing any previous user comment). This will add a comment for the current version of the current track. short: avc parameters: example: avc "The good take with the clear 6/8" remove_version_comment: type: track what: Remove version comment(s) from the current track. short: rvc parameters: none show_version_comment: type: track what: Show version comment(s) of the curent track. short: svc parameters: none show_version_comments_all: type: track what: Show all version comments for the current track. short: svca parameters: none add_system_version_comment: type: track what: Set a system version comment. Useful for testing and diagnostics. short: asvc parameters: new_edit: type: edit what: Create an edit for the current track and version. short: ned parameters: none set_edit_points: type: edit what: Mark play-start, record-start and record-end positions for the current edit. short: sep parameters: none list_edits: type: edit what: List all edits for current track and version. short: led parameters: none select_edit: type: edit what: Select an edit to modify or delete. After selection it is the current edit. short: sed parameters: end_edit_mode: type: edit what: Switch back to normal playback/record mode. The track will play full length again. Edits are managed via a sub- bus. short: eem parameters: none destroy_edit: type: edit what: Remove an edit and all associated audio files. If no parameter is given, the default is to destroy the current edit. Note: The data will be lost permanently. Use with care! parameters: [ ] preview_edit_in: type: edit what: Play the track region without the edit segment. short: pei parameters: none preview_edit_out: type: edit what: Play the removed edit segment. short: peo parameters: none play_edit: type: edit what: Play a completed edit. short: ped parameters: none record_edit: type: edit what: Record an audio file for the current edit. short: red parameters: none edit_track: type: edit what: set the edit track as the current track. short: et parameters: none host_track_alias: type: edit what: Set the host track alias as the current track. short: hta parameters: none host_track: type: edit what: Set the host track (edit sub-bus mix track) as the current track. short: ht parameters: none version_mix_track: type: edit what: Set the version mix track as the current track. short: vmt parameters: none play_start_mark: type: edit what: Select (and move to) play start mark of the current edit. short: psm parameters: none rec_start_mark: type: edit what: Select (and move to) rec start mark of the current edit. short: rsm parameters: none rec_end_mark: type: edit what: Select (and move to) rec end mark of the current edit. short: rem parameters: none set_play_start_mark: type: edit what: Set play-start-mark to the current playback position. short: spsm parameters: none set_rec_start_mark: type: edit what: Set rec-start-mark to the current playback position. short: srsm parameters: none set_rec_end_mark: type: edit what: Set rec-end-mark to current playback position. short: srem parameters: none disable_edits: type: edit what: | Turn off the edits for the current track and playback the original WAV file. This will remove the edit bus. short: ded parameters: none merge_edits: type: edit what: Mix edits and original into a new host-track. this will write a new audio file to disk and the host track will have a new version for this. short: med parameters: none explode_track: type: track what: Make the current track into a sub bus, with one track for each version. parameters: none move_to_bus: type: track what: Move the current track to another bus. A new track is always in the Main bus. So to reverse this action use move-to-bus Main . short: mtb parameters: example: | asub Drums # Create a new sub bus, called Drums. snare # Make snare the current track. mtb Drums # Move the snare track into the sub bus Drums. promote_version_to_track: type: track what: Create a read-only track using the specified version of the current track. short: pvt parameters: read_user_customizations: type: general what: Re-read the user customizations file 'custom.pl'. short: ruc parameters: none limit_run_time: type: setup what: Stop recording after the last audio file finishes playing. Can be turned off with limit-run-time_off. short: lr parameters: [ ] limit_run_time_off: type: setup what: Disable the recording stop timer. short: lro parameters: none offset_run: type: setup what: Record/play from a mark, rather than from the start, i.e. 0.0 seconds. short: ofr parameters: offset_run_off: type: setup what: Turn back to starting from 0. short: ofro parameters: none view_waveform: type: general what: Launch mhwavedit to view/edit waveform of the current track and version. This requires to start Nama on a graphical terminal, like xterm or gterm or from GNOME via alt+F2 . short: wview parameters: none edit_waveform: type: general what: Launch audacity to view/edit the waveform of the current track and version. This requires starting Nama on a graphical terminal like xterm or gterm or from GNOME starting Nama using alt+F2 . short: wedit parameters: none rerecord: type: setup what: Record as before. This will set all the tracks to record, which have been recorded just before you listened back. short: rerec parameters: none example: | for piano guitar;rec # Set piano and guitar track to record. ! do your recording and ilstening. ! You want to record another version of both piano and guitar: rerec # Sets piano and guitar to record again. analyze_level: type: track what: Print Ecasound amplitude analysis for current track. This will show highest volume and statistics. short: anl parameters: none for: type: general what: Execute command(s) for several tracks. parameters: [ } ... ; example: | for piano guitar; vol - 3; pan 75 # reduce volume and pan right for snare kick toms cymbals; mtb Drums # move tracks to bus Drums git: type: project what: execute git command in the project directory parameters: [arguments] edit_rec_setup_hook: type: track what: edit the REC hook script for current track short: ersh parameters: none edit_rec_cleanup_hook: type: track what: edit the REC cleanup hook script for current track short: erch parameters: none remove_fader_effect: type: track short: rffx what: remove vol pan or fader on current track parameters: vol | pan | fader rename_track: type: track what: rename a track and its WAV files parameters: new_sequence: type: sequence short: nsq what: define a new sequence parameters: select_sequence: type: sequence short: slsq what: select named sequence as current sequence parameter: list_sequences: type: sequence short: lsq what: list all user sequences show_sequence: type: sequence short: ssq what: display clips making up current sequence parameters: none append_to_sequence: type: sequence short: asq what: append items to sequence parameters: [,...] example: | asq chorus # append chorus track to current sequence asq # append current track to current sequence insert_in_sequence: type: sequence short: isq what: insert items into sequence before index i parameters: [,...] remove_from_sequence: type: sequence short: rsq what: remove items from sequence parameters: [,...] delete_sequence: type: sequence short: dsq what: delete entire sequence parameters: add_spacer: type: sequence short: asp what: add a spacer to the current sequence, in specified position, or appending (if no position is given) parameters: [] convert_to_sequence: type: sequence short: csq what: convert the current track to a sequence parameters: none merge_sequence: type: sequence short: msq what: cache the current sequence mix track, and set it to PLAY parameters: none snip: type: sequence what: | create a sequence from the current track by removing the region(s) defined by mark pair(s). Not supported if the current track is already a sequence. example: | snip cut1-start cut1-end cut2-start cut2-end This removes cut1 and cut2 regions from the current track by creating a sequence. parameters: [...] compose: type: sequence short: compose_sequence compose_into_sequence what: | compose a new sequence using the region(s) of the named track defined by mark pair(s). If the sequence of that name exists, append the regions to that sequence (compose_into_sequence). parameters: [...] example: | compose speeches conference-audio speaker1-start speaker1-end speaker2-start speaker2-end This creates a "speeches" sequence with two clips for speaker1 and speaker2. undo: type: general what: | roll back last commit (use "git log" to see specific commands) Note: redo is not supported yet redo: type: general what: restore the last undone commit (TODO) show_head_commit: type: general short: show_head last_command last what: | show the last commit, which undo will roll back. A commit may contain multiple commands. The last_* aliases are meaningful when autosave: undo is set. In that case each commit contains only a single command eager: type: mode what: set eager mode parameters: on | off new_engine: type: engine what: Start a named Ecasound engine, or bind to an existing engine short: neg parameters: select_engine: type: engine what: Select an ecasound engine (advanced users only!) short: seg parameters: set_track_engine_group: type: track what: set the current track's engine affiliation short: steg parameters: set_bus_engine_group: type: bus what: set the current bus's engine affiliation short: sbeg parameters: select_submix: type: bus short: ssm what: Set the target for the trim command parameters: trim_submix: type: bus what: control a submix fader short: trim tsm example: | ! reduce vol of current track in in_ear_monitor by 3dB select-submix in_ear_monitor trim vol - 3 nickname_effect: type: effect short: nfx nick what: Add a nickname to the current effect (and create an alias) parameters: example: | add-track guitar afx Plate nick reverb # current effect gets name "reverb1" # "reverb" becomes an alias for "Plate" mfx reverb1 1 0.05 # modify first reverb effect on current track mfx reverb 1 2 # works, because current track has one effect named "reverb" afx reverb # add another Plate effect, gets name "reverb2" rfx reverb # Error, multiple reverb effects are present on this ! # track. Please use a numerical suffix. mfx reverb2 1 3 # modify second reverb effect rfx reverb1 # removes reverb1 ifx reverb2 reverb # insert another reverb effect (reverb3) before reverb2 rfx reverb3 # remove reverb3 rfx reverb # removes reverb2, as it is the sole remain reverb effect delete_nickname_definition: type: effect short: dnd what: delete a nickname definition. Previously named effects keep their names. example: | afx Plate # add Plate effect nick reverb # name it "reverb", and create a nickname for Plate dnd reverb # removes nickname definition afx reverb # error remove_nickname: type: effect what: remove the "name" attribute of the current effect short: rnick example: | afx Plate nick reverb mfx reverb 1 3 rnick mfx reverb 1 3 # Error: effect named "reverb" not found on current track list_nickname_definitions: type: effect what: list defined nicknames short: lnd parameters: none set_effect_name: type: effect what: set a nickname only (don't creating an alias) short: sen parameters: set_effect_surname: type: effect what: set an effect surname short: ses parameters: remove_effect_name: type: effect what: remove current effect name short: ren remove_effect_surname: type: effect what: remove current effect surname short: res select_track: type: track what: | set a particular track as the current, or default track against which track-related commands are executed. parameters: | set_tempo: short: tempo tp type: midi what: set MIDI tempo (bpm) parameters: set_sample_rate: type: general short: ssr what: configure the sample rate for the current project or report sample rate if no parameter parameters: ... @@ grammar command: _a_test { print "aaa-test" } _a_test: /something_else\b/ | /a-test\b/ meta: midi_cmd midi_cmd: /[a-z]+/ predicate { return unless $Audio::Nama::this_track->engine_group =~ /midi/ and $Audio::Nama::text->{midi_cmd}->{$item[1]}; my $line = "$item[1] $item{predicate}"; $line =~ s/^m//; Audio::Nama::pager(Audio::Nama::midish_cmd($line)); 1; } meta: bang shellcode stopper { Audio::Nama::logit(__LINE__,'Audio::Nama::Grammar','debug',"Evaluating shell commands!"); my $shellcode = $item{shellcode}; $shellcode =~ s/\$thiswav/$Audio::Nama::this_track->full_path/e; my $olddir = Audio::Nama::getcwd(); my $prefix = "chdir ". Audio::Nama::project_dir().";"; $shellcode = "$prefix $shellcode" if $shellcode =~ /^\s*git /; Audio::Nama::pager( "executing this shell code: $shellcode" ) if $shellcode ne $item{shellcode}; my $output = qx( $shellcode ); chdir $olddir; Audio::Nama::pager($output) if $output; 1; } meta: eval perlcode stopper { Audio::Nama::logit(__LINE__,'Audio::Nama::Grammar','debug',"Evaluating perl code"); Audio::Nama::eval_perl($item{perlcode}); 1 } meta: for bunch_spec ';' namacode stopper { Audio::Nama::logit(__LINE__,'Grammar','debug',"namacode: $item{namacode}"); my @tracks = Audio::Nama::bunch_tracks($item{bunch_spec}); for my $t(@tracks) { Audio::Nama::set_current_track($t); $Audio::Nama::text->{parser}->meta($item{namacode}); } 1; } bunch_spec: text meta: nosemi(s /\s*;\s*/) semicolon(?) nosemi: text { $Audio::Nama::text->{parser}->do_part($item{text}) } text: /[^;]+/ semicolon: ';' do_part: command end do_part: track_spec command end do_part: track_spec end predicate: nonsemi end { $item{nonsemi}} predicate: /$/ iam_cmd: ident { $item{ident} if $Audio::Nama::text->{iam}->{$item{ident}} } track_spec: ident { Audio::Nama::set_current_track($item{ident}) } bang: '!' eval: 'eval' for: 'for' stopper: ';;' | /$/ shellcode: somecode perlcode: somecode namacode: somecode somecode: /.+?(?=;;|$)/ nonsemi: /[^;]+/ semistop: /;|$/ command: iam_cmd predicate { my $user_input = "$item{iam_cmd} $item{predicate}"; Audio::Nama::logit(__LINE__,'Audio::Nama::Grammar','debug',"Found Ecasound IAM command: $user_input"); my $result = Audio::Nama::ecasound_iam($user_input); Audio::Nama::pager( "$result\n" ); 1 } command: user_command predicate { Audio::Nama::do_user_command(split " ",$item{predicate}); 1; } command: user_alias predicate { $Audio::Nama::text->{parser}->do_part("$item{user_alias} $item{predicate}"); 1 } user_alias: ident { $Audio::Nama::config->{alias}->{command}->{$item{ident}} } user_command: ident { return $item{ident} if $Audio::Nama::text->{user_command}->{$item{ident}} } key: /\w+/ someval: /[\w.+-]+/ sign: '+' | '-' | '*' | '/' parameter_value: '*' | value value: /[+-]?([\d_]+(\.\d*)?|\.\d+)([eE][+-]?\d+)?/ float: /\d+\.\d+/ op_id: /[A-Z]+/ existing_op_id: op_id { my $FX; $FX = Audio::Nama::fxn($item[-1]) and $FX->id } parameter: /\d+/ dd: /\d+/ shellish: /"(.+)"/ { $1 } shellish: /'(.+)'/ { $1 } shellish: anytag | jack_port: shellish effect: /\w[^, ]+/ project_id: ident slash(?) { $item{ident} } slash: '/' anytag: /\S+/ ident: /[-\w]+/ save_target: /[-:\w.]+/ decimal_seconds: /\d+(\.\d+)?/ marktime: /\d+\.\d+/ markname: alphafirst { Audio::Nama::throw("$item[1]: non-existent mark name. Skipping"), return undef unless $Audio::Nama::Mark::by_name{$item[1]}; $item[1]; } alphafirst: /[A-Za-z][\w_-]*/ path: shellish modifier: 'audioloop' | 'select' | 'reverse' | 'playat' | value end: /[;\s]*$/ help_effect: _help_effect effect { Audio::Nama::help_effect($item{effect}) ; 1} find_effect: _find_effect anytag(s) { Audio::Nama::find_effect(@{$item{"anytag(s)"}}); 1} help: _help anytag { Audio::Nama::help($item{anytag}) ; 1} help: _help { print( $Audio::Nama::help->{screen} ); 1} project_name: _project_name { Audio::Nama::pager( "project name: ", $Audio::Nama::project->{name}); 1} new_project: _new_project project_id { Audio::Nama::t_create_project $item{project_id} ; 1} list_projects: _list_projects { Audio::Nama::list_projects() ; 1} load_project: _load_project project_id { Audio::Nama::t_load_project $item{project_id} ; 1} new_project_template: _new_project_template key text(?) { Audio::Nama::new_project_template($item{key}, $item{text}); 1; } use_project_template: _use_project_template key { Audio::Nama::use_project_template($item{key}); 1; } list_project_templates: _list_project_templates { Audio::Nama::list_project_templates(); 1; } destroy_project_template: _destroy_project_template key(s) { Audio::Nama::remove_project_template(@{$item{'key(s)'}}); 1; } tag: _tag tagname message(?) { Audio::Nama::git_snapshot(); my @args = ('tag', $item{tagname}); push @args, '-m', "@{$item{'message(?)'}}" if @{$item{'message(?)'}}; Audio::Nama::git(@args); 1; } commit: _commit message(?) { Audio::Nama::git_snapshot(@{$item{'message(?)'}}); 1; } branch: _branch branchname { Audio::Nama::throw("$item{branchname}: branch does not exist. Skipping."), return 1 if ! Audio::Nama::git_branch_exists($item{branchname}); if(Audio::Nama::git_checkout($item{branchname})){ Audio::Nama::load_project(name => $Audio::Nama::project->{name}) } else { } 1; } branch: _branch { Audio::Nama::list_branches(); 1} list_branches: _list_branches end { Audio::Nama::list_branches(); 1} new_branch: _new_branch branchname branchfrom(?) { my $name = $item{branchname}; my $from = "@{$item{'branchfrom(?)'}}"; Audio::Nama::throw("$name: branch already exists. Doing nothing."), return 1 if Audio::Nama::git_branch_exists($name); Audio::Nama::git_create_branch($name, $from); 1 } tagname: ident branchname: ident branchfrom: ident message: /.+/ save_state: _save_state save_target message(?) { my $name = $item{save_target}; my $default_msg = "user save - $name"; my $message = "@{$item{'message(?)'}}" || $default_msg; Audio::Nama::pager("save target name: $name\n"); Audio::Nama::pager("commit message: $message\n") if $message; if( ! $Audio::Nama::config->{use_git} or $name =~ /\.json$/ ) { Audio::Nama::pager("saving as file\n"), Audio::Nama::save_state( $name) } else { Audio::Nama::git_snapshot(); my @args = ('tag', $name); push @args, '-m', $message if $message; Audio::Nama::git(@args); Audio::Nama::pager_newline(qq/tagged HEAD commit as "$name"/, qq/type "get $name" to return to this commit./) } 1 } save_state: _save_state { Audio::Nama::git_snapshot('user save'); 1} get_state: _get_state save_target { Audio::Nama::load_project( name => $Audio::Nama::project->{name}, settings => $item{save_target} ); 1} getpos: _getpos { Audio::Nama::pager( Audio::Nama::d1( Audio::Nama::ecasound_iam('getpos'))); 1} setpos: _setpos timevalue { Audio::Nama::set_position($item{timevalue}); 1} forward: _forward timevalue { Audio::Nama::forward( $item{timevalue} ); 1} rewind: _rewind timevalue { Audio::Nama::rewind( $item{timevalue} ); 1} timevalue: min_sec | decimal_seconds seconds: samples seconds: /\d+/ samples: /\d+sa/ { my ($samples) = $item[1] =~ /(\d+)/; $return = $samples/$Audio::Nama::project->{sample_rate} } min_sec: /\d+/ ':' /\d+/ { $item[1] * 60 + $item[3] } to_start: _to_start { Audio::Nama::to_start(); 1 } to_end: _to_end { Audio::Nama::to_end(); 1 } add_track: _add_track new_track_name { Audio::Nama::add_track($item{new_track_name}); 1 } add_midi_track: _add_midi_track new_track_name { Audio::Nama::add_midi_track($item{new_track_name}); Audio::Nama::pager_newline(qq(creating MIDI track "$item{new_track_name}")); 1 } arg: anytag add_tracks: _add_tracks track_name(s) { map{ Audio::Nama::add_track($_) } @{$item{'track_name(s)'}}; 1} new_track_name: anytag { my $proposed = $item{anytag}; Audio::Nama::throw( qq(Track name "$proposed" needs to start with a letter)), return undef if $proposed !~ /^[A-Za-z]/; Audio::Nama::throw( qq(Track name "$proposed" cannot contain a colon.)), return undef if $proposed =~ /:/; Audio::Nama::throw( qq(A track named "$proposed" already exists.)), return undef if $Audio::Nama::Track::by_name{$proposed}; Audio::Nama::throw( qq(Track name "$proposed" conflicts with Ecasound command keyword.)), return undef if $Audio::Nama::text->{iam}->{$proposed}; Audio::Nama::throw( qq(Track name "$proposed" conflicts with user command.)), return undef if $Audio::Nama::text->{user_command}->{$proposed}; Audio::Nama::throw( qq(Track name "$proposed" conflicts with Nama command or shortcut.)), return undef if $Audio::Nama::text->{commands}->{$proposed} or $Audio::Nama::text->{command_shortcuts}->{$proposed}; ; $proposed } track_name: alphafirst bus_name: alphafirst existing_track_name: track_name { my $track_name = $item{track_name}; if ($Audio::Nama::tn{$track_name}){ $track_name; } else { Audio::Nama::throw("$track_name: track does not exist.\n"); undef } } existing_bus_name: bus_name { my $bus_name = $item{bus_name}; if ($Audio::Nama::bn{$bus_name}){ $bus_name; } else { Audio::Nama::throw("$bus_name: bus does not exist.\n"); undef } } move_to_bus: _move_to_bus existing_bus_name { $Audio::Nama::this_track->set( group => $item{existing_bus_name}); 1 } set_track: _set_track key someval { $Audio::Nama::this_track->set( $item{key}, $item{someval} ); 1} dump_track: _dump_track { Audio::Nama::pager($Audio::Nama::this_track->dump); 1} dump_group: _dump_group { Audio::Nama::pager($Audio::Nama::bn{Main}->dump); 1} dump_all: _dump_all { Audio::Nama::dump_all(); 1} remove_track: _remove_track quiet end { local $Audio::Nama::quiet = 1; Audio::Nama::remove_track_cmd($Audio::Nama::this_track); 1 } remove_track: _remove_track existing_track_name { Audio::Nama::remove_track_cmd($Audio::Nama::tn{$item{existing_track_name}}); 1 } remove_track: _remove_track end { Audio::Nama::remove_track_cmd($Audio::Nama::this_track) ; 1 } quiet: 'quiet' link_track: _link_track existing_project_name track_name new_track_name end { Audio::Nama::add_track_alias_project( $item{new_track_name}, $item{track_name}, $item{existing_project_name} ); 1 } link_track: _link_track target track_name end { Audio::Nama::add_track_alias($item{track_name}, $item{target}); 1 } target: existing_track_name existing_project_name: ident { $item{ident} if -d Audio::Nama::join_path(Audio::Nama::project_root(),$item{ident}) } project: ident set_region: _set_region beginning ending { Audio::Nama::set_region( @item{ qw( beginning ending ) } ); 1; } set_region: _set_region beginning { Audio::Nama::set_region( $item{beginning}, 'END' ); 1; } remove_region: _remove_region { Audio::Nama::remove_region(); 1; } add_region: _add_region beginning ending track_name(?) { my $name = $item{'track_name(?)'}->[0]; Audio::Nama::new_region(@item{qw(beginning ending)}, $name); 1 } shift_track: _shift_track start_position { my $pos = $item{start_position}; if ( $pos =~ /\d+\.\d+/ ){ Audio::Nama::pager($Audio::Nama::this_track->name, ": Shifting start time to $pos seconds"); $Audio::Nama::this_track->set(playat => $pos); 1; } elsif ( $Audio::Nama::Mark::by_name{$pos} ){ my $time = Audio::Nama::Mark::mark_time( $pos ); Audio::Nama::pager($Audio::Nama::this_track->name, qq(: Shifting start time to mark "$pos", $time seconds)); $Audio::Nama::this_track->set(playat => $pos); 1; } else { Audio::Nama::throw( "Shift value is neither decimal nor mark name. Skipping."); 0; } } start_position: float | samples | markname unshift_track: _unshift_track { $Audio::Nama::this_track->set(playat => undef) } beginning: marktime | markname ending: 'END' | marktime | markname generate: _generate { Audio::Nama::generate_setup(); 1} arm: _arm { Audio::Nama::arm(); 1} arm_start: _arm_start { Audio::Nama::arm(); Audio::Nama::start_transport(); 1 } connect: _connect { Audio::Nama::connect_transport(); 1} disconnect: _disconnect { Audio::Nama::disconnect_transport(); 1} engine_status: _engine_status { Audio::Nama::pager(Audio::Nama::ecasound_iam('engine-status')); 1} start: _start { Audio::Nama::start_transport(); 1} stop: _stop { Audio::Nama::stop_transport(); 1} ecasound_start: _ecasound_start { Audio::Nama::ecasound_iam('start'); 1} ecasound_stop: _ecasound_stop { Audio::Nama::ecasound_iam('stop'); 1} restart_ecasound: _restart_ecasound { Audio::Nama::restart_ecasound(); 1 } show_tracks: _show_tracks { Audio::Nama::pager( Audio::Nama::show_tracks(Audio::Nama::showlist())); 1; } show_tracks_all: _show_tracks_all { my $list = [undef, undef, sort{$a->n <=> $b->n} Audio::Nama::all_tracks()]; Audio::Nama::pager(Audio::Nama::show_tracks($list)); 1; } show_bus: _show_bus existing_bus_name { my $bus = $Audio::Nama::bn{$item{existing_bus_name}}; my $list = $bus->trackslist; Audio::Nama::pager(Audio::Nama::show_tracks($list)); 1; } show_bus: _show_bus { my $bus = $Audio::Nama::bn{$Audio::Nama::this_bus}; my $list = $bus->trackslist; Audio::Nama::pager(Audio::Nama::show_tracks($list)); 1; } modifiers: _modifiers modifier(s) { $Audio::Nama::this_track->set(modifiers => (join q(,), @{$item{"modifier(s)"}}, q() )); 1;} modifiers: _modifiers { Audio::Nama::pager( $Audio::Nama::this_track->modifiers); 1} nomodifiers: _nomodifiers { $Audio::Nama::this_track->set(modifiers => ""); 1} show_chain_setup: _show_chain_setup { Audio::Nama::pager(Audio::Nama::ChainSetup::ecasound_chain_setup); 1} dump_io: _dump_io { Audio::Nama::ChainSetup::show_io(); 1} show_track: _show_track { my $output = $Audio::Nama::text->{format_top}; $output .= Audio::Nama::show_tracks_section($Audio::Nama::this_track); $output .= Audio::Nama::show_track_comment($Audio::Nama::this_track); $output .= Audio::Nama::show_region(); $output .= Audio::Nama::show_versions(); $output .= Audio::Nama::show_version_comment($Audio::Nama::this_track, $Audio::Nama::this_track->version); $output .= Audio::Nama::show_send(); $output .= Audio::Nama::show_bus(); $output .= Audio::Nama::show_modifiers(); $output .= join "", "Signal width: ", Audio::Nama::width($Audio::Nama::this_track->width), "\n"; $output .= Audio::Nama::show_inserts(); $output .= Audio::Nama::show_effects(); Audio::Nama::pager( $output ); 1;} show_track: _show_track track_name { Audio::Nama::pager( Audio::Nama::show_tracks( $Audio::Nama::tn{$item{track_name}} )) if $Audio::Nama::tn{$item{track_name}}; 1;} show_track: _show_track dd { Audio::Nama::pager( Audio::Nama::show_tracks( $Audio::Nama::ti{$item{dd}} )) if $Audio::Nama::ti{$item{dd}}; 1;} show_mode: _show_mode { Audio::Nama::pager( Audio::Nama::show_status()); 1} bus_version: _bus_version dd { my $n = $item{dd}; Audio::Nama::nama_cmd("for $Audio::Nama::this_bus; version $n"); } mixdown: _mixdown additional_time { $Audio::Nama::setup->{extra_run_time} = $item{additional_time}; Audio::Nama::mixdown(); 1} mixdown: _mixdown { Audio::Nama::mixdown(); 1} mixplay: _mixplay { Audio::Nama::mixplay(); 1} mixoff: _mixoff { Audio::Nama::mixoff(); 1} automix: _automix { Audio::Nama::automix(); 1 } autofix_tracks: _autofix_tracks { Audio::Nama::nama_cmd("for mon; fixdc; normalize"); 1 } master_on: _master_on { Audio::Nama::master_on(); 1 } master_off: _master_off { Audio::Nama::master_off(); 1 } exit: _exit { Audio::Nama::save_state(); Audio::Nama::cleanup_exit(); CORE::exit; } source: _source ('track'|'t') trackname { $Audio::Nama::this_track->set_source($item{trackname}, 'track'); 1 } trackname: existing_track_name source: _source 'bus' existing_bus_name { $Audio::Nama::this_track->set(source_id => $item{existing_bus_name}, source_type => 'bus') } source: _source source_id { $Audio::Nama::this_track->set_source($item{source_id}); 1 } source_id: shellish source: _source { my $status = $Audio::Nama::this_track->rec_status; my $source = join ": input set to ",$Audio::Nama::this_track->name, $Audio::Nama::this_track->input_object_text; $source .= " however track is $status" if $status ne Audio::Nama::REC and $status ne Audio::Nama::MON; Audio::Nama::pager_newline($source); } send: _send ('track'|'t') trackname { $Audio::Nama::this_track->set_send($item{trackname}, 'track'); 1 } send: _send send_id { $Audio::Nama::this_track->set_send($item{send_id}); 1} send: _send { $Audio::Nama::this_track->set_send(); 1} send_id: shellish remove_send: _remove_send { $Audio::Nama::this_track->set(send_type => undef); $Audio::Nama::this_track->set(send_id => undef); 1 } stereo: _stereo { $Audio::Nama::this_track->set(width => 2); Audio::Nama::pager($Audio::Nama::this_track->name, ": setting to stereo\n"); 1; } mono: _mono { $Audio::Nama::this_track->set(width => 1); Audio::Nama::pager($Audio::Nama::this_track->name, ": setting to mono\n"); 1; } off: 'dummy' record: 'dummy' mon: 'dummy' play: 'dummy' command: mono command: rw rw_setting: 'REC ' | 'rec' | 'PLAY' | 'play' | 'MON' | 'mon' | 'OFF' | 'off' { $return = $item[1] } rw: rw_setting { $Audio::Nama::this_track->set(rw => uc $item{rw_setting}) ; 1 } set_version: _set_version dd { $Audio::Nama::this_track->set_version($item{dd}); 1} vol: _vol value { $Audio::Nama::this_track->vol or Audio::Nama::throw(( $Audio::Nama::this_track->name . ": no volume control available")), return; Audio::Nama::modify_effect( $Audio::Nama::this_track->vol, 1, undef, $item{value}); 1; } vol: _vol sign(?) value { $Audio::Nama::this_track->vol or Audio::Nama::throw( $Audio::Nama::this_track->name . ": no volume control available"), return; Audio::Nama::modify_effect( $Audio::Nama::this_track->vol, 1, $item{'sign(?)'}->[0], $item{value}); 1; } vol: _vol { Audio::Nama::pager( $Audio::Nama::this_track->vol_level); 1} mute: _mute { $Audio::Nama::this_track->mute; 1} unmute: _unmute { $Audio::Nama::this_track->unmute; 1} solo: _solo ident(s) { Audio::Nama::solo(@{$item{'ident(s)'}}); 1 } solo: _solo { Audio::Nama::solo($Audio::Nama::this_track->name); 1} all: _all { Audio::Nama::all() ; 1} nosolo: _nosolo { Audio::Nama::nosolo() ; 1} unity: _unity { Audio::Nama::unity($Audio::Nama::this_track); 1} pan: _pan panval { Audio::Nama::update_effect( $Audio::Nama::this_track->pan, 0, $item{panval}); 1;} pan: _pan sign panval { Audio::Nama::modify_effect( $Audio::Nama::this_track->pan, 1, $item{sign}, $item{panval} ); 1;} panval: float | dd pan: _pan { Audio::Nama::pager( $Audio::Nama::this_track->pan_level); 1} pan_right: _pan_right { Audio::Nama::pan_check($Audio::Nama::this_track, 100 ); 1} pan_left: _pan_left { Audio::Nama::pan_check($Audio::Nama::this_track, 0 ); 1} pan_center: _pan_center { Audio::Nama::pan_check($Audio::Nama::this_track, 50 ); 1} pan_back: _pan_back { Audio::Nama::pan_back($Audio::Nama::this_track); 1;} remove_mark: _remove_mark dd { my @marks = Audio::Nama::Mark::all(); $marks[$item{dd}]->remove if defined $marks[$item{dd}]; 1;} remove_mark: _remove_mark ident { my $mark = $Audio::Nama::Mark::by_name{$item{ident}}; $mark->remove if defined $mark; 1;} remove_mark: _remove_mark { return unless (ref $Audio::Nama::this_mark) =~ /Mark/; $Audio::Nama::this_mark->remove; 1;} add_mark: _add_mark ident { Audio::Nama::drop_mark $item{ident}; 1} add_mark: _add_mark { Audio::Nama::drop_mark(); 1} next_mark: _next_mark { Audio::Nama::next_mark(); 1} previous_mark: _previous_mark { Audio::Nama::previous_mark(); 1} loop: _loop someval(s) { my @new_endpoints = @{ $item{"someval(s)"}}; $Audio::Nama::mode->{loop_enable} = 1; @{$Audio::Nama::setup->{loop_endpoints}} = (@new_endpoints, @{$Audio::Nama::setup->{loop_endpoints}}); @{$Audio::Nama::setup->{loop_endpoints}} = @{$Audio::Nama::setup->{loop_endpoints}}[0,1]; 1;} noloop: _noloop { $Audio::Nama::mode->{loop_enable} = 0; 1} name_mark: _name_mark ident {$Audio::Nama::this_mark->set_name( $item{ident}); 1} list_marks: _list_marks { my $i = 0; my @lines = map{ ( $_->{time} == $Audio::Nama::this_mark->{time} ? q(*) : q() ,join " ", $i++, sprintf("%.1f", $_->{time}), $_->name, "\n") } @Audio::Nama::Mark::all; my $start = my $end = "undefined"; push @lines, "now at ". sprintf("%.1f\n", Audio::Nama::ecasound_iam("getpos")); Audio::Nama::pager(@lines); 1;} to_mark: _to_mark dd { my @marks = Audio::Nama::Mark::all(); $marks[$item{dd}]->jump_here; 1;} to_mark: _to_mark ident { my $mark = $Audio::Nama::Mark::by_name{$item{ident}}; $mark->jump_here if defined $mark; 1;} modify_mark: _modify_mark sign value { my $newtime = eval($Audio::Nama::this_mark->{time} . $item{sign} . $item{value}); $Audio::Nama::this_mark->set( time => $newtime ); Audio::Nama::pager($Audio::Nama::this_mark->name, ": set to ", Audio::Nama::d2( $newtime), "\n"); Audio::Nama::pager("adjusted to ",$Audio::Nama::this_mark->time, "\n") if $Audio::Nama::this_mark->time != $newtime; Audio::Nama::set_position($Audio::Nama::this_mark->time); Audio::Nama::request_setup(); 1; } modify_mark: _modify_mark value { $Audio::Nama::this_mark->set( time => $item{value} ); my $newtime = $item{value}; Audio::Nama::pager($Audio::Nama::this_mark->name, ": set to ", Audio::Nama::d2($newtime),"\n"); Audio::Nama::pager("adjusted to ",$Audio::Nama::this_mark->time, "\n") if $Audio::Nama::this_mark->time != $newtime; Audio::Nama::set_position($Audio::Nama::this_mark->time); Audio::Nama::request_setup(); 1; } remove_effect: _remove_effect remove_target(s) { Audio::Nama::mute(); map{ my $id = $_; my ($use) = grep{ $id eq $Audio::Nama::this_track->$_ } qw(vol pan fader); if($use){ Audio::Nama::throw("Effect $id is used as $use by track",$Audio::Nama::this_track->name, ".\nSee 'remove_fader_effect to remove it'\n") } else { my $FX = Audio::Nama::fxn($id); Audio::Nama::pager_newline("removing effect ".$FX->nameline); $FX->_remove_effect(); } } grep { $_ } map{ split ' ', $_} @{ $item{"remove_target(s)"}} ; Audio::Nama::sleeper(0.5); Audio::Nama::unmute(); 1;} add_controller: _add_controller parent effect value(s?) { my $code = $item{effect}; my $parent = $item{parent}; my $parent_o = Audio::Nama::fxn($parent); print "parent: ", $parent_o, " chain: ", $parent_o->chain; my $values = $item{"value(s?)"}; my $id = Audio::Nama::add_effect({ parent => $parent, chain => $parent_o->chain, type => $code, params => $values, }); if($id) { my $iname = Audio::Nama::fxn($id)->fxname; my $pname = Audio::Nama::fxn($parent)->fxname; Audio::Nama::pager("\nAdded $id, $iname to $parent, $pname\n\n"); } 1; } add_controller: _add_controller effect value(s?) { Audio::Nama::throw("current effect is undefined, skipping\n"), return 1 if ! Audio::Nama::this_op(); my $code = $item{effect}; my $parent = Audio::Nama::this_op(); my $values = $item{"value(s?)"}; my $cmd = "add_controller $parent $code @$values"; print "command: $cmd\n"; Audio::Nama::nama_cmd($cmd); 1 } existing_effect_chain: ident { $item{ident} if Audio::Nama::is_effect_chain($item{ident}) } add_target: fx_nick | existing_effect_chain | known_effect_type nickname_effect: _nickname_effect ident { my $ident = $item{ident}; Audio::Nama::this_op_o()->set_name($ident); Audio::Nama::throw("$ident: no such nickname. Skipping."), return unless defined Audio::Nama::this_op_o(); my $type = Audio::Nama::this_op_o()->type; my $fxname = Audio::Nama::this_op_o()->fxname; $Audio::Nama::fx->{alias}->{$ident} = $type; Audio::Nama::pager_newline("$ident: nickname created for $type ($fxname)"); 1 } remove_nickname: _remove_nickname { Audio::Nama::this_op_o()->remove_name() } delete_nickname_definition: _delete_nickname_definition ident { my $was = delete $Audio::Nama::fx->{alias}->{$item{ident}}; $was or Audio::Nama::throw("$item{ident}: no such nickname"), return 0; Audio::Nama::pager_newline("$item{ident}: effect nickname deleted"); } list_nickname_definitions: _list_nickname_definitions { my @lines; while( my($nick,$code) = each %{ $Audio::Nama::fx->{alias} } ) { push @lines, join " ", "$nick:", $Audio::Nama::fx_cache->{registry}->[Audio::Nama::effect_index($code)]->{name}, "($code)\n"; } Audio::Nama::pager(@lines); 1 } known_effect_type: effect { Audio::Nama::full_effect_code($item{effect}) } before: fx_alias fx_name: ident { $Audio::Nama::this_track->effect_id_by_name($item{ident}) } fx_surname: ident { $Audio::Nama::this_track->with_surname($item{ident}) } add_effect: _add_effect add_target parameter_value(s?) before(?) { my ($code, $effect_chain); my $values = $item{'parameter_value(s?)'}; my $args = { track => $Audio::Nama::this_track, params => $values }; if( my $fxc = Audio::Nama::is_effect_chain($item{add_target}) ) { $args->{effect_chain} = $fxc } else{ $args->{type} = $item{add_target} } my $fader = Audio::Nama::fxn($Audio::Nama::this_track->pan) && $Audio::Nama::this_track->pan || Audio::Nama::fxn($Audio::Nama::this_track->vol) && $Audio::Nama::this_track->vol; { no warnings 'uninitialized'; Audio::Nama::logpkg(__FILE__,__LINE__,'debug',$Audio::Nama::this_track->name,": effect insert point is $fader", Audio::Nama::Dumper($args)); } my $predecessor = $item{'before(?)'}->[0] || $fader; $args->{before} = $predecessor if $predecessor; my $added = Audio::Nama::_add_effect($args); for my $FX(@$added) { my $iname = $FX->fxname; my $id = $FX->id; Audio::Nama::pager_newline("Added $id, $iname"); Audio::Nama::set_current_op($id); } } add_effect: _add_effect ('first' | 'f') add_target value(s?) { my $command = join " ", qw(add_effect), $item{add_target}, @{$item{'value(s?)'}}, $Audio::Nama::this_track->{ops}->[0]; Audio::Nama::nama_cmd($command) } add_effect: _add_effect ('last' | 'l') add_target value(s?) { my $command = join " ", qw(add_effect), $item{add_target}, @{$item{'value(s?)'}}, qw(ZZZ); Audio::Nama::nama_cmd($command) } add_effect: _add_effect ('before' | 'b') before add_target value(s?) { my $command = join " ", qw(add_effect), $item{add_target}, @{$item{'value(s?)'}}, $item{before}; Audio::Nama::nama_cmd($command) } add_effect_first: _add_effect_first add_target value(s?) { my $command = join " ", qw(add_effect), "last", $item{add_target}, @{$item{'value(s?)'}}; Audio::Nama::nama_cmd($command) } add_effect_last: _add_effect_last add_target value(s?) { my $command = join " ", qw(add_effect), "last", $item{add_target}, @{$item{'value(s?)'}}; Audio::Nama::nama_cmd($command) } add_effect_before: _add_effect_before before add_target value(s?) { my $command = join " ", qw(add_effect), "before", $item{before}, $item{add_target}, @{$item{'value(s?)'}}; Audio::Nama::nama_cmd($command) } parent: op_id modify_effect: _modify_effect fx_alias(s /,/) parameter(s /,/) value { Audio::Nama::modify_multiple_effects( @item{qw(fx_alias(s) parameter(s) sign value)}); Audio::Nama::pager(Audio::Nama::show_effect(@{ $item{'fx_alias(s)'} })); 1 } modify_effect: _modify_effect fx_alias(s /,/) parameter(s /,/) sign value { Audio::Nama::modify_multiple_effects( @item{qw(fx_alias(s) parameter(s) sign value)}); Audio::Nama::pager(Audio::Nama::show_effect(@{ $item{'fx_alias(s)'} })); 1 } modify_effect: _modify_effect parameter(s /,/) value { Audio::Nama::throw("current effect is undefined, skipping"), return 1 if ! Audio::Nama::this_op(); Audio::Nama::modify_multiple_effects( [Audio::Nama::this_op()], $item{'parameter(s)'}, undef, $item{value}); Audio::Nama::pager( Audio::Nama::show_effect(Audio::Nama::this_op(), "with track affiliation")); 1 } modify_effect: _modify_effect parameter(s /,/) sign value { Audio::Nama::throw("current effect is undefined, skipping"), return 1 if ! Audio::Nama::this_op(); Audio::Nama::modify_multiple_effects( [Audio::Nama::this_op()], @item{qw(parameter(s) sign value)}); Audio::Nama::pager( Audio::Nama::show_effect(Audio::Nama::this_op())); 1 } fx_alias3: ident { join " ", map{ $_->id } grep { $_->surname eq $item{ident} } $Audio::Nama::this_track->user_ops_o; } remove_target: existing_op_id | fx_pos | fx_surname | fx_name { $item[-1] or print("no effect object found\n"), return 0} fx_alias: fx_alias2 | fx_alias1 fx_nick: ident { $Audio::Nama::fx->{alias}->{$item{ident}} } fx_alias1: op_id fx_alias1: fx_pos fx_alias1: fx_name fx_alias2: fx_type fx_pos: dd { $Audio::Nama::this_track->{ops}->[$item{dd} - 1] } fx_type: effect { my $FX = $Audio::Nama::this_track->first_effect_of_type($item{effect}); $FX ? $FX->id : undef } position_effect: _position_effect op_to_move new_following_op { my $op = $item{op_to_move}; my $pos = $item{new_following_op}; my $FX = Audio::Nama::fxn($op); $FX->position_effect($pos); Audio::Nama::set_current_op($op); 1; } op_to_move: op_id new_following_op: op_id show_effect: _show_effect fx_alias(s) { my @fx = @{ $item{'fx_alias(s)'}}; @fx = Audio::Nama::Effect::expanded_ops_list(@fx); my @lines = map{ Audio::Nama::show_effect($_, "with track affiliation") } grep{ Audio::Nama::fxn($_) } @fx; Audio::Nama::set_current_op($item{'fx_alias(s)'}->[-1]); Audio::Nama::pager(@lines); 1 } show_effect: _show_effect { Audio::Nama::throw("current effect is undefined, skipping"), return 1 if ! Audio::Nama::this_op(); Audio::Nama::pager( Audio::Nama::show_effect(Audio::Nama::this_op(), "with track affiliation")); 1; } dump_effect: _dump_effect fx_alias { Audio::Nama::pager( Audio::Nama::json_out(Audio::Nama::fxn($item{fx_alias})->as_hash) ); 1} dump_effect: _dump_effect { Audio::Nama::pager( Audio::Nama::json_out(Audio::Nama::this_op_o()->as_hash) ); 1} list_effects: _list_effects { Audio::Nama::pager(Audio::Nama::list_effects()); 1} add_bunch: _add_bunch ident(s) { Audio::Nama::bunch( @{$item{'ident(s)'}}); 1} list_bunches: _list_bunches { Audio::Nama::bunch(); 1} remove_bunch: _remove_bunch ident(s) { map{ delete $Audio::Nama::project->{bunch}->{$_} } @{$item{'ident(s)'}}; 1} add_to_bunch: _add_to_bunch ident(s) { Audio::Nama::add_to_bunch( @{$item{'ident(s)'}});1 } list_versions: _list_versions { Audio::Nama::pagers( join " ", @{$Audio::Nama::this_track->versions}); 1} ladspa_register: _ladspa_register { Audio::Nama::pager( Audio::Nama::ecasound_iam("ladspa-register")); 1} preset_register: _preset_register { Audio::Nama::pagers( Audio::Nama::ecasound_iam("preset-register")); 1} ctrl_register: _ctrl_register { Audio::Nama::pager( Audio::Nama::ecasound_iam("ctrl-register")); 1} preview: _preview { Audio::Nama::set_preview_mode(); 1} doodle: _doodle { Audio::Nama::set_doodle_mode(); 1 } normalize: _normalize { $Audio::Nama::this_track->normalize; 1} fixdc: _fixdc { $Audio::Nama::this_track->fixdc; 1} destroy_current_wav: _destroy_current_wav { Audio::Nama::destroy_current_wav(); 1 } memoize: _memoize { package Audio::Nama::Wav; $Audio::Nama::config->{memoize} = 1; memoize('candidates'); 1 } unmemoize: _unmemoize { package Audio::Nama::Wav; $Audio::Nama::config->{memoize} = 0; unmemoize('candidates'); 1 } import_audio: _import_audio path frequency { Audio::Nama::import_audio($Audio::Nama::this_track, $item{path}, $item{frequency}); 1; } import_midi: _import_midi path { my $fname = $item{path}; $fname = qq("$fname") unless $fname =~ /"/; Audio::Nama::midish_cmd("import $fname"); 1 } import_audio: _import_audio path { Audio::Nama::import_audio($Audio::Nama::this_track, $item{path}); 1; } frequency: value list_history: _list_history { my @history = $Audio::Nama::text->{term}->GetHistory; my %seen; Audio::Nama::pager( grep{ ! $seen{$_} and $seen{$_}++ } @history ); } add_submix_cooked: _add_submix_cooked bus_name destination { Audio::Nama::add_submix( $item{bus_name}, $item{destination}, 'cooked' ); 1; } add_submix_raw: _add_submix_raw bus_name destination { Audio::Nama::add_submix( $item{bus_name}, $item{destination}, 'raw' ); 1; } add_bus: _add_bus bus_name { Audio::Nama::add_bus( $item{bus_name}); 1 } existing_bus_name: bus_name { if ( $Audio::Nama::bn{$item{bus_name}} ){ $item{bus_name} } else { Audio::Nama::throw("$item{bus_name}: no such bus"); undef } } bus_name: ident user_bus_name: ident { if($item[1] =~ /^[A-Z]/){ $item[1] } else { Audio::Nama::throw("Bus name must begin with capital letter."); undef} } destination: jack_port remove_bus: _remove_bus existing_bus_name { $Audio::Nama::bn{$item{existing_bus_name}}->remove; 1; } update_submix: _update_submix existing_bus_name { Audio::Nama::update_submix( $item{existing_bus_name} ); 1; } set_bus: _set_bus key someval { $Audio::Nama::bn{$Audio::Nama::this_bus}->set($item{key} => $item{someval}); 1 } list_buses: _list_buses { Audio::Nama::list_buses() ; 1} add_insert: _add_insert 'local' { Audio::Nama::Insert::add_insert( $Audio::Nama::this_track,'postfader_insert'); 1; } add_insert: _add_insert prepost send_id return_id(?) { my $return_id = $item{'return_id(?)'}->[0]; my $send_id = $item{send_id}; Audio::Nama::Insert::add_insert($Audio::Nama::this_track, "$item{prepost}fader_insert",$send_id, $return_id); 1; } prepost: 'pre' | 'post' send_id: jack_port return_id: jack_port set_insert_wetness: _set_insert_wetness prepost(?) parameter { my $prepost = $item{'prepost(?)'}->[0]; my $p = $item{parameter}; my $id = Audio::Nama::Insert::get_id($Audio::Nama::this_track,$prepost); Audio::Nama::throw($Audio::Nama::this_track->name. ": Missing or ambiguous insert. Skipping"), return 1 unless $id; Audio::Nama::throw("wetness parameter must be an integer between 0 and 100"), return 1 unless ($p <= 100 and $p >= 0); my $i = $Audio::Nama::Insert::by_index{$id}; Audio::Nama::throw("track '",$Audio::Nama::this_track->n, "' has no insert. Skipping."), return 1 unless $i; $i->set_wetness($p); Audio::Nama::pager( "The insert is ", $i->wetness, "% wet, ", (100 - $i->wetness), "% dry."); } set_insert_wetness: _set_insert_wetness prepost(?) { my $prepost = $item{'prepost(?)'}->[0]; my $id = Audio::Nama::Insert::get_id($Audio::Nama::this_track,$prepost); $id or Audio::Nama::throw($Audio::Nama::this_track->name. ": Missing or ambiguous insert. Skipping"), return 1 ; my $i = $Audio::Nama::Insert::by_index{$id}; Audio::Nama::pager( "The insert is ", $i->wetness, "% wet, ", (100 - $i->wetness), "% dry."); } remove_insert: _remove_insert prepost(?) { my $prepost = $item{'prepost(?)'}->[0]; my $id = Audio::Nama::Insert::get_id($Audio::Nama::this_track,$prepost); $id or Audio::Nama::throw($Audio::Nama::this_track->name. ": Missing or ambiguous insert. Skipping"), return 1 ; Audio::Nama::pager( $Audio::Nama::this_track->name.": removing ". $prepost ? "$prepost fader insert" : "insert"); $Audio::Nama::Insert::by_index{$id}->remove; 1; } cache_track: _cache_track additional_time(?) { my $time = $item{'additional_time(?)'}->[0]; Audio::Nama::cache_track($Audio::Nama::this_track, $time); 1 } additional_time: float | dd uncache_track: _uncache_track { Audio::Nama::uncache_track($Audio::Nama::this_track); 1 } overwrite_effect_chain: 'dummy' new_effect_chain: (_new_effect_chain | _overwrite_effect_chain ) ident op_id(s?) end { my $name = $item{ident}; my @existing = Audio::Nama::EffectChain::find(user => 1, name => $name); if ( scalar @existing ){ $item[1] eq 'overwrite_effect_chain' ? Audio::Nama::nama_cmd("delete_effect_chain $name") : Audio::Nama::throw(qq/$name: effect chain with this name is already defined. Use a different name, or use "overwrite_effect_chain"/) && return; } my $ops = scalar @{$item{'op_id(s?)'}} ? $item{'op_id(s?)'} : [ $Audio::Nama::this_track->user_ops ]; my @options; Audio::Nama::EffectChain->new( user => 1, global => 1, name => $item{ident}, ops_list => $ops, inserts_data => $Audio::Nama::this_track->inserts, @options, ); 1; } delete_effect_chain: _delete_effect_chain ident(s) { map { map{$_->destroy()} Audio::Nama::EffectChain::find( user => 1, name => $_); } @{ $item{'ident(s)'} }; 1; } find_effect_chains: _find_effect_chains ident(s?) { my @args; push @args, @{ $item{'ident(s?)'} } if $item{'ident(s?)'}; Audio::Nama::pager(map{$_->dump} Audio::Nama::EffectChain::find(@args)); 1 } find_user_effect_chains: _find_user_effect_chains ident(s?) { my @args = ('user' , 1); push @args, @{ $item{'ident(s)'} } if $item{'ident(s)'}; (scalar @args) % 2 == 0 or Audio::Nama::throw("odd number of arguments\n@args\n"), return 0; Audio::Nama::pager( map{ $_->summary} Audio::Nama::EffectChain::find(@args) ); 1; } bypass_effects: _bypass_effects op_id(s) { my $arr_ref = $item{'op_id(s)'}; return unless (ref $arr_ref) =~ /ARRAY/ and scalar @{$arr_ref}; my @illegal = grep { ! Audio::Nama::fxn($_) } @$arr_ref; Audio::Nama::throw("@illegal: non-existing effect(s), skipping."), return 0 if @illegal; Audio::Nama::pager( "track ",$Audio::Nama::this_track->name,", bypassing effects:"); Audio::Nama::pager( Audio::Nama::named_effects_list(@$arr_ref)); Audio::Nama::bypass_effects($Audio::Nama::this_track,@$arr_ref); Audio::Nama::set_current_op($arr_ref->[0]) if scalar @$arr_ref == 1; } bypass_effects: _bypass_effects 'all' { Audio::Nama::pager( "track ",$Audio::Nama::this_track->name,", bypassing all effects (except vol/pan)"); Audio::Nama::bypass_effects($Audio::Nama::this_track, $Audio::Nama::this_track->user_ops) if $Audio::Nama::this_track->user_ops; 1; } bypass_effects: _bypass_effects { Audio::Nama::throw("current effect is undefined, skipping"), return 1 if ! Audio::Nama::this_op(); Audio::Nama::pager( "track ",$Audio::Nama::this_track->name,", bypassing effects:"); Audio::Nama::pager( Audio::Nama::named_effects_list(Audio::Nama::this_op())); Audio::Nama::bypass_effects($Audio::Nama::this_track, Audio::Nama::this_op()); 1; } bring_back_effects: _bring_back_effects end { Audio::Nama::pager("current effect is undefined, skipping"), return 1 if ! Audio::Nama::this_op(); Audio::Nama::pager( "restoring effects:"); Audio::Nama::pager( Audio::Nama::named_effects_list(Audio::Nama::this_op())); Audio::Nama::restore_effects( $Audio::Nama::this_track, Audio::Nama::this_op()); } bring_back_effects: _bring_back_effects op_id(s) { my $arr_ref = $item{'op_id(s)'}; return unless (ref $arr_ref) =~ /ARRAY/ and scalar @{$arr_ref}; my @illegal = grep { ! Audio::Nama::fxn($_) } @$arr_ref; Audio::Nama::throw("@illegal: non-existing effect(s), aborting."), return 0 if @illegal; Audio::Nama::pager( "restoring effects:"); Audio::Nama::pager( Audio::Nama::named_effects_list(@$arr_ref)); Audio::Nama::restore_effects($Audio::Nama::this_track,@$arr_ref); Audio::Nama::set_current_op($arr_ref->[0]) if scalar @$arr_ref == 1; } bring_back_effects: _bring_back_effects 'all' { Audio::Nama::pager( "restoring all effects"); Audio::Nama::restore_effects( $Audio::Nama::this_track, $Audio::Nama::this_track->user_ops); } fxc_val: shellish this_track_op_id: op_id(s) { my %ops = map{ $_ => 1 } @{$Audio::Nama::this_track->ops}; my @ids = @{$item{'op_id(s)'}}; my @belonging = grep { $ops{$_} } @ids; my @alien = grep { ! $ops{$_} } @ids; @alien and Audio::Nama::pager("@alien: don't belong to track ",$Audio::Nama::this_track->name, "skipping."); @belonging } bunch_name: ident { Audio::Nama::is_bunch($item{ident}) or Audio::Nama::bunch_tracks($item{ident}) or Audio::Nama::throw("$item{ident}: no such bunch name."), return; $item{ident}; } effect_profile_name: ident existing_effect_profile_name: ident { Audio::Nama::pager("$item{ident}: no such effect profile"), return unless Audio::Nama::EffectChain::find(profile => $item{ident}); $item{ident} } new_effect_profile: _new_effect_profile bunch_name effect_profile_name { Audio::Nama::new_effect_profile($item{bunch_name}, $item{effect_profile_name}); 1 } destroy_effect_profile: _destroy_effect_profile existing_effect_profile_name { Audio::Nama::delete_effect_profile($item{existing_effect_profile_name}); 1 } apply_effect_profile: _apply_effect_profile existing_effect_profile_name { Audio::Nama::apply_effect_profile($item{effect_profile_name}); 1 } list_effect_profiles: _list_effect_profiles { my %profiles; map{ $profiles{$_->profile}++ } Audio::Nama::EffectChain::find(profile => 1); my @output = keys %profiles; if( @output ) { Audio::Nama::pager( join " ","Effect Profiles available:", @output) } else { Audio::Nama::throw("no match") } 1; } show_effect_profiles: _show_effect_profiles ident(?) { my $name; $name = $item{'ident(?)'}->[-1] if $item{'ident(?)'}; $name ||= 1; my %profiles; map{ $profiles{$_->profile}++ } Audio::Nama::EffectChain::find(profile => $name); my @names = keys %profiles; my @output; for $name (@names) { push @output, "\nprofile name: $name\n"; map { push @output, $_->summary } Audio::Nama::EffectChain::find(profile => $name) } if( @output ) { Audio::Nama::pager( @output); } else { Audio::Nama::throw("no match") } 1; } full_effect_profiles: _full_effect_profiles ident(?) { my $name; $name = $item{'ident(?)'}->[-1] if $item{'ident(?)'}; $name ||= 1; my @output = map{ $_->dump } Audio::Nama::EffectChain::find(profile => $name ) ; if( @output ) { Audio::Nama::pager( @output); } else { Audio::Nama::throw("no match") } 1; } do_script: _do_script shellish { Audio::Nama::do_script($item{shellish});1} scan: _scan { Audio::Nama::pager( "scanning ", Audio::Nama::this_wav_dir()); Audio::Nama::restart_wav_memoize() } add_fade: _add_fade in_or_out mark1 duration(?) { Audio::Nama::Fade->new( type => $item{in_or_out}, mark1 => $item{mark1}, duration => $item{'duration(?)'}->[0] || $Audio::Nama::config->{engine_fade_default_length}, relation => 'fade_from_mark', track => $Audio::Nama::this_track->name, ); Audio::Nama::request_setup(); } add_fade: _add_fade in_or_out duration(?) mark1 { Audio::Nama::Fade->new( type => $item{in_or_out}, mark1 => $item{mark1}, duration => $item{'duration(?)'}->[0] || $Audio::Nama::config->{engine_fade_default_length}, track => $Audio::Nama::this_track->name, relation => 'fade_to_mark', ); Audio::Nama::request_setup(); } add_fade: _add_fade in_or_out mark1 mark2 { Audio::Nama::Fade->new( type => $item{in_or_out}, mark1 => $item{mark1}, mark2 => $item{mark2}, track => $Audio::Nama::this_track->name, ); Audio::Nama::request_setup(); } add_fade: _add_fade in_or_out time1 time2 { my $mark1 = Audio::Nama::Mark->new( name => join('_',$Audio::Nama::this_track->name, 'fade', Audio::Nama::Mark::next_id()), time => $item{time1} ); my $mark2 = Audio::Nama::Mark->new( name => join('_',$Audio::Nama::this_track->name, 'fade', Audio::Nama::Mark::next_id()), time => $item{time2} ); Audio::Nama::Fade->new( type => $item{in_or_out}, mark1 => $mark1->name, mark2 => $mark2->name, track => $Audio::Nama::this_track->name, ); Audio::Nama::request_setup(); } time1: value time2: value in_or_out: 'in' | 'out' duration: value mark1: markname mark2: markname remove_fade: _remove_fade fade_index(s) { my @i = @{ $item{'fade_index(s)'} }; Audio::Nama::remove_fade($_) for (@i); Audio::Nama::request_setup(); 1 } fade_index: dd list_fade: _list_fade { Audio::Nama::pager(join "\n", map{ s/^---//; s/...\s$//; $_} map{$_->dump} sort{$a->n <=> $b->n} values %Audio::Nama::Fade::by_index); 1 } add_comment: _add_comment text { Audio::Nama::pagers( $Audio::Nama::this_track->name. ": comment: $item{text}"); $Audio::Nama::project->{track_comments}->{$Audio::Nama::this_track->name} = $item{text}; 1; } remove_comment: _remove_comment { Audio::Nama::pager( $Audio::Nama::this_track->name, ": comment removed"); delete $Audio::Nama::project->{track_comments}->{$Audio::Nama::this_track->name}; 1; } show_comment: _show_comment { Audio::Nama::pager( map{ $_->name. ": ". $_->comment } $Audio::Nama::this_track ); 1; } show_comments: _show_comments { Audio::Nama::pager( map{ $_->name. ": ". $_->comment } grep{ $_->comment } Audio::Nama::all_tracks() ); 1; } add_version_comment: _add_version_comment dd(?) text { my $t = $Audio::Nama::this_track; my $v = $item{'dd(?)'}->[0] // $t->playback_version // return 1; Audio::Nama::pager( $t->add_version_comment($v,$item{text})); } remove_version_comment: _remove_version_comment dd { my $t = $Audio::Nama::this_track; Audio::Nama::pager( $t->remove_version_comment($item{dd})); 1 } remove_version_comment: _remove_version_comment { my $t = $Audio::Nama::this_track; Audio::Nama::pager( $t->remove_version_comment($t->version)); 1 } show_version_comment: _show_version_comment dd(s) { my $t = $Audio::Nama::this_track; my @v = @{$item{'dd(s)'}}; if(!@v){ @v = $t->playback_version} @v or return 1; $t->show_version_comments(@v); 1; } show_version_comment: _show_version_comment { my $t = $Audio::Nama::this_track; my @v = @{$t->versions}; $t->show_version_comments(@v); 1; } show_version_comments_all: _show_version_comments_all { map { my $t = $_; my @v = @{$t->versions}; $t->show_version_comments(@v); } Audio::Nama::all_tracks(); 1; } add_system_version_comment: _add_system_version_comment dd text { Audio::Nama::pagers( $Audio::Nama::this_track->add_system_version_comment(@item{qw(dd text)}));1; } new_edit: _new_edit { Audio::Nama::new_edit(); 1; } set_edit_points: _set_edit_points { Audio::Nama::set_edit_points(); 1 } list_edits: _list_edits { Audio::Nama::list_edits(); 1} destroy_edit: _destroy_edit { Audio::Nama::destroy_edit(); 1} select_edit: _select_edit dd { Audio::Nama::select_edit($item{dd}); 1} preview_edit_in: _preview_edit_in { Audio::Nama::edit_action($item[0]); 1} preview_edit_out: _preview_edit_out { Audio::Nama::edit_action($item[0]); 1} play_edit: _play_edit { Audio::Nama::edit_action($item[0]); 1} record_edit: _record_edit { Audio::Nama::edit_action($item[0]); 1} edit_track: _edit_track { Audio::Nama::select_edit_track('edit_track'); 1} host_track_alias: _host_track_alias { Audio::Nama::select_edit_track('host_alias_track'); 1} host_track: _host_track { Audio::Nama::select_edit_track('host'); 1} version_mix_track: _version_mix_track { Audio::Nama::select_edit_track('version_mix'); 1} play_start_mark: _play_start_mark { my $mark = $Audio::Nama::this_edit->play_start_mark; $mark->jump_here; 1; } rec_start_mark: _rec_start_mark { $Audio::Nama::this_edit->rec_start_mark->jump_here; 1; } rec_end_mark: _rec_end_mark { $Audio::Nama::this_edit->rec_end_mark->jump_here; 1; } set_play_start_mark: _set_play_start_mark { $Audio::Nama::setup->{edit_points}->[0] = Audio::Nama::ecasound_iam('getpos'); 1} set_rec_start_mark: _set_rec_start_mark { $Audio::Nama::setup->{edit_points}->[1] = Audio::Nama::ecasound_iam('getpos'); 1} set_rec_end_mark: _set_rec_end_mark { $Audio::Nama::setup->{edit_points}->[2] = Audio::Nama::ecasound_iam('getpos'); 1} end_edit_mode: _end_edit_mode { Audio::Nama::end_edit_mode(); 1;} disable_edits: _disable_edits { Audio::Nama::disable_edits();1 } merge_edits: _merge_edits { Audio::Nama::merge_edits(); 1; } explode_track: _explode_track { Audio::Nama::explode_track($Audio::Nama::this_track) } promote_version_to_track: _promote_version_to_track version { my $v = $item{version}; my $t = $Audio::Nama::this_track; $t->versions->[$v] or Audio::Nama::pager($t->name,": version $v does not exist."), return; Audio::Nama::VersionTrack->new( name => $t->name.":$v", version => $v, target => $t->name, rw => Audio::Nama::PLAY, group => $t->group, ); } version: dd read_user_customizations: _read_user_customizations { Audio::Nama::setup_user_customization(); 1 } limit_run_time: _limit_run_time sign(?) dd { my $sign = $item{'sign(?)'}->[-1]; $Audio::Nama::setup->{runtime_limit} = $sign ? eval "$Audio::Nama::setup->{audio_length} $sign $item{dd}" : $item{dd}; Audio::Nama::pager( "Run time limit: ", Audio::Nama::heuristic_time($Audio::Nama::setup->{runtime_limit})); 1; } limit_run_time_off: _limit_run_time_off { Audio::Nama::pager( "Run timer disabled"); Audio::Nama::disable_length_timer(); 1; } offset_run: _offset_run markname { Audio::Nama::set_offset_run_mark( $item{markname} ); 1 } offset_run_off: _offset_run_off { Audio::Nama::pager( "no run offset."); Audio::Nama::disable_offset_run_mode(); } view_waveform: _view_waveform { my $viewer = 'mhwaveedit'; if( `which $viewer` =~ m/\S/){ my $cmd = join " ", $viewer, "--driver", $Audio::Nama::jack->{jackd_running} ? "jack" : "alsa", $Audio::Nama::this_track->full_path, "&"; system($cmd) } else { Audio::Nama::throw("Mhwaveedit not found. No waveform viewer is available.") } } edit_waveform: _edit_waveform { if ( `which audacity` =~ m/\S/ ){ my $cmd = join " ", 'audacity', $Audio::Nama::this_track->full_path, "&"; my $old_pwd = Audio::Nama::getcwd(); chdir Audio::Nama::this_wav_dir(); system($cmd); chdir $old_pwd; } else { Audio::Nama::throw("Audacity not found. No waveform editor available.") } 1; } rerecord: _rerecord { Audio::Nama::pager( scalar @{$Audio::Nama::setup->{_last_rec_tracks}} ? "Toggling previous recording tracks to REC" : "No tracks in REC list. Skipping." ); map{ $_->set(rw => Audio::Nama::REC) } @{$Audio::Nama::setup->{_last_rec_tracks}}; Audio::Nama::restore_preview_mode(); 1; } show_track_latency: _show_track_latency { my $node = $Audio::Nama::setup->{latency}->{track}->{$Audio::Nama::this_track->name}; Audio::Nama::pager( Audio::Nama::json_out($node)) if $node; 1; } show_latency_all: _show_latency_all { Audio::Nama::pager( Audio::Nama::json_out($Audio::Nama::setup->{latency})) if $Audio::Nama::setup->{latency}; 1; } analyze_level: _analyze_level { Audio::Nama::check_level($Audio::Nama::this_track);1 } git: _git shellcode stopper { Audio::Nama::pager(map {$_.="\n"} $Audio::Nama::project->{repo}->run( split " ", $item{shellcode})) } edit_rec_setup_hook: _edit_rec_setup_hook { system("$ENV{EDITOR} ".$Audio::Nama::this_track->rec_setup_script() ); chmod 0755, $Audio::Nama::this_track->rec_setup_script(); 1 } edit_rec_cleanup_hook: _edit_rec_cleanup_hook { system("$ENV{EDITOR} ".$Audio::Nama::this_track->rec_cleanup_script() ); chmod 0755, $Audio::Nama::this_track->rec_cleanup_script(); 1 } remove_fader_effect: _remove_fader_effect fader_role { Audio::Nama::remove_fader_effect($Audio::Nama::this_track, $item{fader_role}); 1 } fader_role: 'vol'|'pan'|'fader' hotkeys: _hotkeys { Audio::Nama::setup_hotkeys()} hotkeys_always: _hotkeys_always { $Audio::Nama::config->{hotkeys_always}++; Audio::Nama::setup_hotkeys(); } hotkeys_off: _hotkeys_off { undef $Audio::Nama::config->{hotkeys_always}; 1 } hotkeys_list: _hotkeys_list { Audio::Nama::list_hotkeys() ; 1 } select_sequence: _select_sequence existing_sequence_name { $Audio::Nama::this_sequence = $Audio::Nama::bn{$item{existing_sequence_name}} } existing_sequence_name: ident { my $buslike = $Audio::Nama::bn{$item{ident}}; $return = $item{ident} if (ref $buslike) =~ /Sequence/ } convert_to_sequence: _convert_to_sequence { my $sequence_name = $Audio::Nama::this_track->name; Audio::Nama::nama_cmd("nsq $sequence_name"); $Audio::Nama::this_sequence->new_clip($Audio::Nama::this_track); 1 } merge_sequence: _merge_sequence { cache_track($Audio::Nama::tn{$Audio::Nama::this_sequence->name}); 1 } new_sequence: _new_sequence new_sequence_name track_identifier(s?) { Audio::Nama::new_sequence( name => $item{new_sequence_name}, tracks => $item{'track_identifier(s?)'} || [] ); 1 } new_sequence_name: ident { $return = $Audio::Nama::bn{$item{ident}} ? do { Audio::Nama::pager("$item{ident}: name already in use\n"), undef} : $item{ident} } track_identifier: tid { my $tid = $Audio::Nama::tn{$item{tid}} || $Audio::Nama::ti{$item{tid}} ; if ($tid) { $tid } else { Audio::Nama::throw("$item{tid}: track name or index not found.\n"); undef } } tid: ident list_sequences: _list_sequences { Audio::Nama::pager( map {Audio::Nama::json_out($_->as_hash)} grep {$_->{class} =~ /Sequence/} Audio::Nama::Bus::all() ); 1 } show_sequence: _show_sequence { Audio::Nama::pager($Audio::Nama::this_sequence->list_output) } append_to_sequence: _append_to_sequence track_identifier(s?) { my $seq = $Audio::Nama::this_sequence; my $items = $item{'track_identifier(s?)'} || [$Audio::Nama::this_track]; map { my $clip = $seq->new_clip($_); $seq->append_item($clip) } @$items; 1; } insert_in_sequence: _insert_in_sequence position track_identifier(s) { my $seq = $Audio::Nama::this_sequence; my $items = $item{'track_identifier(s)'}; my $position = $item{position}; for ( reverse map{ $seq->new_clip($_) } @$items ){ $seq->insert_item($_,$position) } } remove_from_sequence: _remove_from_sequence position(s) { my $seq = $Audio::Nama::this_sequence; my @positions = sort { $a <=> $b } @{ $item{'position(s)'}}; $seq->verify_item($_) ? $seq->delete_item($_) : Audio::Nama::throw("skipping index $_: out of bounds") for reverse @positions } delete_sequence: _delete_sequence existing_sequence_name { $Audio::Nama::bn{$item{existing_sequence_name}}->remove } position: dd { $Audio::Nama::this_sequence->verify_item($item{dd}) and $item{dd} } add_spacer: _add_spacer value position { $Audio::Nama::this_sequence->new_spacer( duration => $item{value}, position => $item{position}, hidden => 1, ); Audio::Nama::request_setup(); 1 } add_spacer: _add_spacer value { $Audio::Nama::this_sequence->new_spacer( duration => $item{value}, hidden => 1, ); Audio::Nama::request_setup(); 1 } snip: _snip track_identifier mark_pair(s) { my $track = $item{track_identifier}; my @pairs = $item{'mark_pair(s)'}; my @list = map{ @$_ } @pairs; @list = (0, @list, $track->length); @pairs = (); while ( scalar @list ){ push @pairs, [splice( @list, 0, 2)] } Audio::Nama::compose_sequence($track->name, $track, \@pairs); } compose: _compose ident track_identifier mark_pair(s) { Audio::Nama::compose_sequence(@item{qw/ident track_identifier mark_pair(s)/}); } mark_pair: mark1 mark2 { my @marks = map{ $Audio::Nama::mn{$_}} @item{qw(mark1 mark2)}; Audio::Nama::throw(join" ",(map{$_->name} @marks), ": pair must be ascending in time"), return undef if not( $marks[0]->time < $marks[1]->time ); \@marks } mark1: ident { $Audio::Nama::mn{$item{ident}} } mark2: mark1 snip: _snip new_sequence_name mark_pair(s) {} rename_track: _rename_track existing_track_name new_track_name { Audio::Nama::rename_track( @item{qw(existing_track_name new_track_name)}, $Audio::Nama::file->git_state_store, Audio::Nama::this_wav_dir() ); } undo: _undo { Audio::Nama::undo() } redo: _redo { Audio::Nama::redo() } show_head_commit: _show_head_commit { Audio::Nama::show_head_commit() } eager: _eager on_or_off { $Audio::Nama::mode->{eager} = $item{on_or_off} =~ /[1n]/ ? 1 : 0 } on_or_off: 'on' | '1' | 'off' | '0' new_engine: _new_engine ident port { Audio::Nama::Engine->new(name => $item{ident}, port => $item{port}) } port: dd select_engine: _select_engine ident { my $new_choice = $Audio::Nama::Engine::by_name{$item{ident}}; $Audio::Nama::this_engine = $new_choice if defined $new_choice; Audio::Nama::pager("Current engine is ".$Audio::Nama::this_engine->name) } set_track_engine_group: _set_track_engine_group ident { $Audio::Nama::this_track->set(engine_group => $item{ident}); Audio::Nama::pager($Audio::Nama::this_track->name. ": engine group set to $item{ident}"); } set_bus_engine_group: _set_bus_engine_group ident { $Audio::Nama::bn{$Audio::Nama::this_bus}->set(engine_group => $item{ident}); Audio::Nama::pager("$Audio::Nama::this_bus: bus engine group set to $item{ident}"); } select_submix: _select_submix existing_bus_name { $Audio::Nama::this_user = $Audio::Nama::bn{$item{existing_bus_name}} } trim_submix: _trim_submix effect parameter sign(?) value { my $real_track = join '_', $Audio::Nama::this_user->name, $Audio::Nama::this_track->name; Audio::Nama::pager("real track: $real_track\n"); my $FX = $Audio::Nama::tn{$real_track}->first_effect_of_type(Audio::Nama::full_effect_code($item{effect})); Audio::Nama::modify_effect($FX->id, $item{parameter}, @{$item{'sign(?)'}}, $item{value}); } set_effect_name: _set_effect_name ident { Audio::Nama::this_op_o->set_name($item{ident}); 1} remove_effect_name: _remove_effect_name { Audio::Nama::this_op_o->set_name(); 1 } set_effect_surname: _set_effect_surname ident { Audio::Nama::this_op_o->set_surname($item{ident}); 1} remove_effect_surname: _remove_effect_surname { Audio::Nama::this_op_o()->set_surname(); 1} select_track: _select_track track_spec set_tempo: _set_tempo dd {Audio::Nama::midish_cmd("t $item{dd}")} route_track: _route_track source_id send_id { Audio::Nama::nama_cmd("source $item{source_id}"); Audio::Nama::nama_cmd("send $item{send_id}"); 1 } set_sample_rate: _set_sample_rate dd {Audio::Nama::set_sample_rate($item{dd})} set_sample_rate: _set_sample_rate {Audio::Nama::get_sample_rate()} bus_on: _bus_on { Audio::Nama::pagers('turning bus on'); my $bus_name = $Audio::Nama::this_track->source_type eq 'bus' ? $Audio::Nama::this_track->source_id : $Audio::Nama::this_bus; print "bus_name: $bus_name\n"; $Audio::Nama::bn{$bus_name}->tracks_on } bus_off: _bus_off { Audio::Nama::pagers('turning bus off'); my $bus_name = $Audio::Nama::this_track->source_type eq 'bus' ? $Audio::Nama::this_track->source_id : $Audio::Nama::this_bus; print "bus_name: $bus_name\n"; $Audio::Nama::bn{$bus_name}->tracks_off } command: help command: help_effect command: find_effect command: exit command: memoize command: unmemoize command: stop command: start command: getpos command: setpos command: forward command: rewind command: to_start command: to_end command: ecasound_start command: ecasound_stop command: restart_ecasound command: preview command: doodle command: mixdown command: mixplay command: mixoff command: automix command: master_on command: master_off command: add_track command: add_midi_track command: add_tracks command: link_track command: import_audio command: import_midi command: route_track command: set_track command: record command: play command: mon command: off command: source command: send command: remove_send command: stereo command: mono command: set_version command: destroy_current_wav command: list_versions command: vol command: mute command: unmute command: unity command: solo command: nosolo command: all command: pan command: pan_right command: pan_left command: pan_center command: pan_back command: show_tracks command: show_tracks_all command: show_bus command: show_track command: show_mode command: show_track_latency command: show_latency_all command: set_region command: add_region command: remove_region command: shift_track command: unshift_track command: modifiers command: nomodifiers command: normalize command: fixdc command: autofix_tracks command: remove_track command: bus_version command: bus_on command: bus_off command: add_bunch command: list_bunches command: remove_bunch command: add_to_bunch command: commit command: tag command: branch command: list_branches command: new_branch command: save_state command: get_state command: list_projects command: new_project command: load_project command: project_name command: new_project_template command: use_project_template command: list_project_templates command: destroy_project_template command: generate command: arm command: arm_start command: connect command: disconnect command: show_chain_setup command: loop command: noloop command: add_controller command: add_effect command: add_effect_last command: add_effect_first command: add_effect_before command: modify_effect command: remove_effect command: position_effect command: show_effect command: dump_effect command: list_effects command: hotkeys command: hotkeys_always command: hotkeys_off command: hotkeys_list command: add_insert command: set_insert_wetness command: remove_insert command: ctrl_register command: preset_register command: ladspa_register command: list_marks command: to_mark command: add_mark command: remove_mark command: next_mark command: previous_mark command: name_mark command: modify_mark command: engine_status command: dump_track command: dump_group command: dump_all command: dump_io command: list_history command: add_submix_cooked command: add_submix_raw command: add_bus command: update_submix command: remove_bus command: list_buses command: set_bus command: overwrite_effect_chain command: new_effect_chain command: delete_effect_chain command: find_effect_chains command: find_user_effect_chains command: bypass_effects command: bring_back_effects command: new_effect_profile command: apply_effect_profile command: destroy_effect_profile command: list_effect_profiles command: show_effect_profiles command: full_effect_profiles command: cache_track command: uncache_track command: do_script command: scan command: add_fade command: remove_fade command: list_fade command: add_comment command: remove_comment command: show_comment command: show_comments command: add_version_comment command: remove_version_comment command: show_version_comment command: show_version_comments_all command: add_system_version_comment command: new_edit command: set_edit_points command: list_edits command: select_edit command: end_edit_mode command: destroy_edit command: preview_edit_in command: preview_edit_out command: play_edit command: record_edit command: edit_track command: host_track_alias command: host_track command: version_mix_track command: play_start_mark command: rec_start_mark command: rec_end_mark command: set_play_start_mark command: set_rec_start_mark command: set_rec_end_mark command: disable_edits command: merge_edits command: explode_track command: move_to_bus command: promote_version_to_track command: read_user_customizations command: limit_run_time command: limit_run_time_off command: offset_run command: offset_run_off command: view_waveform command: edit_waveform command: rerecord command: analyze_level command: for command: git command: edit_rec_setup_hook command: edit_rec_cleanup_hook command: remove_fader_effect command: rename_track command: new_sequence command: select_sequence command: list_sequences command: show_sequence command: append_to_sequence command: insert_in_sequence command: remove_from_sequence command: delete_sequence command: add_spacer command: convert_to_sequence command: merge_sequence command: snip command: compose command: undo command: redo command: show_head_commit command: eager command: new_engine command: select_engine command: set_track_engine_group command: set_bus_engine_group command: select_submix command: trim_submix command: nickname_effect command: delete_nickname_definition command: remove_nickname command: list_nickname_definitions command: set_effect_name command: set_effect_surname command: remove_effect_name command: remove_effect_surname command: select_track command: set_tempo command: set_sample_rate _help: /help\b/ | /h\b/ { "help" } _help_effect: /help_effect\b/ | /hfx\b/ | /he\b/ { "help_effect" } _find_effect: /find_effect\b/ | /ffx\b/ | /fe\b/ { "find_effect" } _exit: /exit\b/ | /quit\b/ | /q\b/ { "exit" } _memoize: /memoize\b/ { "memoize" } _unmemoize: /unmemoize\b/ { "unmemoize" } _stop: /stop\b/ | /s\b/ { "stop" } _start: /start\b/ | /t\b/ { "start" } _getpos: /getpos\b/ | /gp\b/ { "getpos" } _setpos: /setpos\b/ | /sp\b/ { "setpos" } _forward: /forward\b/ | /fw\b/ { "forward" } _rewind: /rewind\b/ | /rw\b/ { "rewind" } _to_start: /to_start\b/ | /beg\b/ { "to_start" } _to_end: /to_end\b/ | /end\b/ { "to_end" } _ecasound_start: /ecasound_start\b/ { "ecasound_start" } _ecasound_stop: /ecasound_stop\b/ { "ecasound_stop" } _restart_ecasound: /restart_ecasound\b/ { "restart_ecasound" } _preview: /preview\b/ | /song\b/ { "preview" } _doodle: /doodle\b/ | /live\b/ { "doodle" } _mixdown: /mixdown\b/ | /mxd\b/ { "mixdown" } _mixplay: /mixplay\b/ | /mxp\b/ { "mixplay" } _mixoff: /mixoff\b/ | /mxo\b/ { "mixoff" } _automix: /automix\b/ { "automix" } _master_on: /master_on\b/ | /mr\b/ { "master_on" } _master_off: /master_off\b/ | /mro\b/ { "master_off" } _add_track: /add_track\b/ | /add\b/ | /new\b/ { "add_track" } _add_midi_track: /add_midi_track\b/ | /amt\b/ { "add_midi_track" } _add_tracks: /add_tracks\b/ { "add_tracks" } _link_track: /link_track\b/ | /link\b/ { "link_track" } _import_audio: /import_audio\b/ | /import\b/ { "import_audio" } _import_midi: /import_midi\b/ | /im\b/ { "import_midi" } _route_track: /route_track\b/ | /route\b/ | /rt\b/ { "route_track" } _set_track: /set_track\b/ { "set_track" } _record: /record\b/ | /rec\b/ { "record" } _play: /play\b/ { "play" } _mon: /mon\b/ { "mon" } _off: /off\b/ { "off" } _source: /source\b/ | /src\b/ | /r\b/ { "source" } _send: /send\b/ | /aux\b/ { "send" } _remove_send: /remove_send\b/ | /nosend\b/ | /noaux\b/ { "remove_send" } _stereo: /stereo\b/ { "stereo" } _mono: /mono\b/ { "mono" } _set_version: /set_version\b/ | /version\b/ | /ver\b/ { "set_version" } _destroy_current_wav: /destroy_current_wav\b/ { "destroy_current_wav" } _list_versions: /list_versions\b/ | /lver\b/ { "list_versions" } _vol: /vol\b/ | /v\b/ { "vol" } _mute: /mute\b/ | /c\b/ | /cut\b/ { "mute" } _unmute: /unmute\b/ | /nomute\b/ | /C\b/ | /uncut\b/ { "unmute" } _unity: /unity\b/ { "unity" } _solo: /solo\b/ | /sl\b/ { "solo" } _nosolo: /nosolo\b/ | /nsl\b/ { "nosolo" } _all: /all\b/ { "all" } _pan: /pan\b/ | /p\b/ { "pan" } _pan_right: /pan_right\b/ | /pr\b/ { "pan_right" } _pan_left: /pan_left\b/ | /pl\b/ { "pan_left" } _pan_center: /pan_center\b/ | /pc\b/ { "pan_center" } _pan_back: /pan_back\b/ | /pb\b/ { "pan_back" } _show_tracks: /show_tracks\b/ | /lt\b/ | /show\b/ { "show_tracks" } _show_tracks_all: /show_tracks_all\b/ | /sha\b/ | /showa\b/ { "show_tracks_all" } _show_bus: /show_bus\b/ | /shb\b/ { "show_bus" } _show_track: /show_track\b/ | /sh\b/ | /-fart\b/ { "show_track" } _show_mode: /show_mode\b/ | /shm\b/ { "show_mode" } _show_track_latency: /show_track_latency\b/ | /shl\b/ { "show_track_latency" } _show_latency_all: /show_latency_all\b/ | /shla\b/ { "show_latency_all" } _set_region: /set_region\b/ | /srg\b/ { "set_region" } _add_region: /add_region\b/ { "add_region" } _remove_region: /remove_region\b/ | /rrg\b/ { "remove_region" } _shift_track: /shift_track\b/ | /shift\b/ | /playat\b/ | /pat\b/ { "shift_track" } _unshift_track: /unshift_track\b/ | /unshift\b/ { "unshift_track" } _modifiers: /modifiers\b/ | /mods\b/ | /mod\b/ { "modifiers" } _nomodifiers: /nomodifiers\b/ | /nomods\b/ | /nomod\b/ { "nomodifiers" } _normalize: /normalize\b/ | /ecanormalize\b/ { "normalize" } _fixdc: /fixdc\b/ | /ecafixdc\b/ { "fixdc" } _autofix_tracks: /autofix_tracks\b/ | /autofix\b/ { "autofix_tracks" } _remove_track: /remove_track\b/ { "remove_track" } _bus_version: /bus_version\b/ | /bver\b/ | /gver\b/ { "bus_version" } _bus_on: /bus_on\b/ { "bus_on" } _bus_off: /bus_off\b/ { "bus_off" } _add_bunch: /add_bunch\b/ | /abn\b/ { "add_bunch" } _list_bunches: /list_bunches\b/ | /lbn\b/ { "list_bunches" } _remove_bunch: /remove_bunch\b/ | /rbn\b/ { "remove_bunch" } _add_to_bunch: /add_to_bunch\b/ | /atbn\b/ { "add_to_bunch" } _commit: /commit\b/ | /cm\b/ { "commit" } _tag: /tag\b/ { "tag" } _branch: /branch\b/ | /br\b/ { "branch" } _list_branches: /list_branches\b/ | /lb\b/ | /lbr\b/ { "list_branches" } _new_branch: /new_branch\b/ | /nbr\b/ { "new_branch" } _save_state: /save_state\b/ | /keep\b/ | /save\b/ { "save_state" } _get_state: /get_state\b/ | /get\b/ | /recall\b/ | /retrieve\b/ { "get_state" } _list_projects: /list_projects\b/ | /lp\b/ { "list_projects" } _new_project: /new_project\b/ | /create\b/ { "new_project" } _load_project: /load_project\b/ | /load\b/ { "load_project" } _project_name: /project_name\b/ | /project\b/ | /name\b/ { "project_name" } _new_project_template: /new_project_template\b/ | /npt\b/ { "new_project_template" } _use_project_template: /use_project_template\b/ | /upt\b/ | /apt\b/ { "use_project_template" } _list_project_templates: /list_project_templates\b/ | /lpt\b/ { "list_project_templates" } _destroy_project_template: /destroy_project_template\b/ { "destroy_project_template" } _generate: /generate\b/ | /gen\b/ { "generate" } _arm: /arm\b/ { "arm" } _arm_start: /arm_start\b/ | /arms\b/ { "arm_start" } _connect: /connect\b/ | /con\b/ { "connect" } _disconnect: /disconnect\b/ | /dcon\b/ { "disconnect" } _show_chain_setup: /show_chain_setup\b/ | /chains\b/ { "show_chain_setup" } _loop: /loop\b/ | /l\b/ { "loop" } _noloop: /noloop\b/ | /nl\b/ { "noloop" } _add_controller: /add_controller\b/ | /acl\b/ { "add_controller" } _add_effect: /add_effect\b/ | /afx\b/ { "add_effect" } _add_effect_last: /add_effect_last\b/ | /afxl\b/ { "add_effect_last" } _add_effect_first: /add_effect_first\b/ | /afxf\b/ { "add_effect_first" } _add_effect_before: /add_effect_before\b/ | /afxb\b/ | /insert_effect\b/ | /ifx\b/ { "add_effect_before" } _modify_effect: /modify_effect\b/ | /mfx\b/ { "modify_effect" } _remove_effect: /remove_effect\b/ | /rfx\b/ { "remove_effect" } _position_effect: /position_effect\b/ | /pfx\b/ { "position_effect" } _show_effect: /show_effect\b/ | /sfx\b/ { "show_effect" } _dump_effect: /dump_effect\b/ | /dfx\b/ { "dump_effect" } _list_effects: /list_effects\b/ | /lfx\b/ { "list_effects" } _hotkeys: /hotkeys\b/ | /hk\b/ { "hotkeys" } _hotkeys_always: /hotkeys_always\b/ | /hka\b/ { "hotkeys_always" } _hotkeys_off: /hotkeys_off\b/ | /hko\b/ { "hotkeys_off" } _hotkeys_list: /hotkeys_list\b/ | /hkl\b/ | /lhk\b/ { "hotkeys_list" } _add_insert: /add_insert\b/ | /ain\b/ { "add_insert" } _set_insert_wetness: /set_insert_wetness\b/ | /wet\b/ { "set_insert_wetness" } _remove_insert: /remove_insert\b/ | /rin\b/ { "remove_insert" } _ctrl_register: /ctrl_register\b/ | /crg\b/ { "ctrl_register" } _preset_register: /preset_register\b/ | /prg\b/ { "preset_register" } _ladspa_register: /ladspa_register\b/ | /lrg\b/ { "ladspa_register" } _list_marks: /list_marks\b/ | /lmk\b/ | /lm\b/ { "list_marks" } _to_mark: /to_mark\b/ | /tmk\b/ | /tom\b/ { "to_mark" } _add_mark: /add_mark\b/ | /mark\b/ | /amk\b/ | /k\b/ { "add_mark" } _remove_mark: /remove_mark\b/ | /rmk\b/ { "remove_mark" } _next_mark: /next_mark\b/ | /nmk\b/ { "next_mark" } _previous_mark: /previous_mark\b/ | /pmk\b/ { "previous_mark" } _name_mark: /name_mark\b/ { "name_mark" } _modify_mark: /modify_mark\b/ | /move_mark\b/ | /mmk\b/ { "modify_mark" } _engine_status: /engine_status\b/ | /egs\b/ { "engine_status" } _dump_track: /dump_track\b/ | /dump\b/ { "dump_track" } _dump_group: /dump_group\b/ | /dumpg\b/ { "dump_group" } _dump_all: /dump_all\b/ | /dumpa\b/ { "dump_all" } _dump_io: /dump_io\b/ { "dump_io" } _list_history: /list_history\b/ | /lh\b/ { "list_history" } _add_submix_cooked: /add_submix_cooked\b/ { "add_submix_cooked" } _add_submix_raw: /add_submix_raw\b/ | /asr\b/ { "add_submix_raw" } _add_bus: /add_bus\b/ | /abs\b/ { "add_bus" } _update_submix: /update_submix\b/ | /usm\b/ { "update_submix" } _remove_bus: /remove_bus\b/ { "remove_bus" } _list_buses: /list_buses\b/ | /lbs\b/ { "list_buses" } _set_bus: /set_bus\b/ | /sbs\b/ { "set_bus" } _overwrite_effect_chain: /overwrite_effect_chain\b/ | /oec\b/ { "overwrite_effect_chain" } _new_effect_chain: /new_effect_chain\b/ | /nec\b/ { "new_effect_chain" } _delete_effect_chain: /delete_effect_chain\b/ | /dec\b/ | /destroy_effect_chain\b/ { "delete_effect_chain" } _find_effect_chains: /find_effect_chains\b/ | /fec\b/ | /lec\b/ { "find_effect_chains" } _find_user_effect_chains: /find_user_effect_chains\b/ | /fuec\b/ | /leca\b/ { "find_user_effect_chains" } _bypass_effects: /bypass_effects\b/ | /bypass\b/ | /bfx\b/ { "bypass_effects" } _bring_back_effects: /bring_back_effects\b/ | /restore_effects\b/ | /bbfx\b/ { "bring_back_effects" } _new_effect_profile: /new_effect_profile\b/ | /nep\b/ { "new_effect_profile" } _apply_effect_profile: /apply_effect_profile\b/ | /aep\b/ { "apply_effect_profile" } _destroy_effect_profile: /destroy_effect_profile\b/ { "destroy_effect_profile" } _list_effect_profiles: /list_effect_profiles\b/ | /lep\b/ { "list_effect_profiles" } _show_effect_profiles: /show_effect_profiles\b/ | /sepr\b/ { "show_effect_profiles" } _full_effect_profiles: /full_effect_profiles\b/ | /fep\b/ { "full_effect_profiles" } _cache_track: /cache_track\b/ | /cache\b/ | /ct\b/ | /bounce\b/ | /freeze\b/ { "cache_track" } _uncache_track: /uncache_track\b/ | /uncache\b/ | /unc\b/ { "uncache_track" } _do_script: /do_script\b/ | /do\b/ { "do_script" } _scan: /scan\b/ { "scan" } _add_fade: /add_fade\b/ | /afd\b/ | /fade\b/ { "add_fade" } _remove_fade: /remove_fade\b/ | /rfd\b/ { "remove_fade" } _list_fade: /list_fade\b/ | /lfd\b/ { "list_fade" } _add_comment: /add_comment\b/ | /comment\b/ | /ac\b/ { "add_comment" } _remove_comment: /remove_comment\b/ | /rc\b/ { "remove_comment" } _show_comment: /show_comment\b/ | /sc\b/ { "show_comment" } _show_comments: /show_comments\b/ | /sca\b/ { "show_comments" } _add_version_comment: /add_version_comment\b/ | /avc\b/ { "add_version_comment" } _remove_version_comment: /remove_version_comment\b/ | /rvc\b/ { "remove_version_comment" } _show_version_comment: /show_version_comment\b/ | /svc\b/ { "show_version_comment" } _show_version_comments_all: /show_version_comments_all\b/ | /svca\b/ { "show_version_comments_all" } _add_system_version_comment: /add_system_version_comment\b/ | /asvc\b/ { "add_system_version_comment" } _new_edit: /new_edit\b/ | /ned\b/ { "new_edit" } _set_edit_points: /set_edit_points\b/ | /sep\b/ { "set_edit_points" } _list_edits: /list_edits\b/ | /led\b/ { "list_edits" } _select_edit: /select_edit\b/ | /sed\b/ { "select_edit" } _end_edit_mode: /end_edit_mode\b/ | /eem\b/ { "end_edit_mode" } _destroy_edit: /destroy_edit\b/ { "destroy_edit" } _preview_edit_in: /preview_edit_in\b/ | /pei\b/ { "preview_edit_in" } _preview_edit_out: /preview_edit_out\b/ | /peo\b/ { "preview_edit_out" } _play_edit: /play_edit\b/ | /ped\b/ { "play_edit" } _record_edit: /record_edit\b/ | /red\b/ { "record_edit" } _edit_track: /edit_track\b/ | /et\b/ { "edit_track" } _host_track_alias: /host_track_alias\b/ | /hta\b/ { "host_track_alias" } _host_track: /host_track\b/ | /ht\b/ { "host_track" } _version_mix_track: /version_mix_track\b/ | /vmt\b/ { "version_mix_track" } _play_start_mark: /play_start_mark\b/ | /psm\b/ { "play_start_mark" } _rec_start_mark: /rec_start_mark\b/ | /rsm\b/ { "rec_start_mark" } _rec_end_mark: /rec_end_mark\b/ | /rem\b/ { "rec_end_mark" } _set_play_start_mark: /set_play_start_mark\b/ | /spsm\b/ { "set_play_start_mark" } _set_rec_start_mark: /set_rec_start_mark\b/ | /srsm\b/ { "set_rec_start_mark" } _set_rec_end_mark: /set_rec_end_mark\b/ | /srem\b/ { "set_rec_end_mark" } _disable_edits: /disable_edits\b/ | /ded\b/ { "disable_edits" } _merge_edits: /merge_edits\b/ | /med\b/ { "merge_edits" } _explode_track: /explode_track\b/ { "explode_track" } _move_to_bus: /move_to_bus\b/ | /mtb\b/ { "move_to_bus" } _promote_version_to_track: /promote_version_to_track\b/ | /pvt\b/ { "promote_version_to_track" } _read_user_customizations: /read_user_customizations\b/ | /ruc\b/ { "read_user_customizations" } _limit_run_time: /limit_run_time\b/ | /lr\b/ { "limit_run_time" } _limit_run_time_off: /limit_run_time_off\b/ | /lro\b/ { "limit_run_time_off" } _offset_run: /offset_run\b/ | /ofr\b/ { "offset_run" } _offset_run_off: /offset_run_off\b/ | /ofro\b/ { "offset_run_off" } _view_waveform: /view_waveform\b/ | /wview\b/ { "view_waveform" } _edit_waveform: /edit_waveform\b/ | /wedit\b/ { "edit_waveform" } _rerecord: /rerecord\b/ | /rerec\b/ { "rerecord" } _analyze_level: /analyze_level\b/ | /anl\b/ { "analyze_level" } _for: /for\b/ { "for" } _git: /git\b/ { "git" } _edit_rec_setup_hook: /edit_rec_setup_hook\b/ | /ersh\b/ { "edit_rec_setup_hook" } _edit_rec_cleanup_hook: /edit_rec_cleanup_hook\b/ | /erch\b/ { "edit_rec_cleanup_hook" } _remove_fader_effect: /remove_fader_effect\b/ | /rffx\b/ { "remove_fader_effect" } _rename_track: /rename_track\b/ { "rename_track" } _new_sequence: /new_sequence\b/ | /nsq\b/ { "new_sequence" } _select_sequence: /select_sequence\b/ | /slsq\b/ { "select_sequence" } _list_sequences: /list_sequences\b/ | /lsq\b/ { "list_sequences" } _show_sequence: /show_sequence\b/ | /ssq\b/ { "show_sequence" } _append_to_sequence: /append_to_sequence\b/ | /asq\b/ { "append_to_sequence" } _insert_in_sequence: /insert_in_sequence\b/ | /isq\b/ { "insert_in_sequence" } _remove_from_sequence: /remove_from_sequence\b/ | /rsq\b/ { "remove_from_sequence" } _delete_sequence: /delete_sequence\b/ | /dsq\b/ { "delete_sequence" } _add_spacer: /add_spacer\b/ | /asp\b/ { "add_spacer" } _convert_to_sequence: /convert_to_sequence\b/ | /csq\b/ { "convert_to_sequence" } _merge_sequence: /merge_sequence\b/ | /msq\b/ { "merge_sequence" } _snip: /snip\b/ { "snip" } _compose: /compose\b/ | /compose_sequence\b/ | /compose_into_sequence\b/ { "compose" } _undo: /undo\b/ { "undo" } _redo: /redo\b/ { "redo" } _show_head_commit: /show_head_commit\b/ | /show_head\b/ | /last_command\b/ | /last\b/ { "show_head_commit" } _eager: /eager\b/ { "eager" } _new_engine: /new_engine\b/ | /neg\b/ { "new_engine" } _select_engine: /select_engine\b/ | /seg\b/ { "select_engine" } _set_track_engine_group: /set_track_engine_group\b/ | /steg\b/ { "set_track_engine_group" } _set_bus_engine_group: /set_bus_engine_group\b/ | /sbeg\b/ { "set_bus_engine_group" } _select_submix: /select_submix\b/ | /ssm\b/ { "select_submix" } _trim_submix: /trim_submix\b/ | /trim\b/ | /tsm\b/ { "trim_submix" } _nickname_effect: /nickname_effect\b/ | /nfx\b/ | /nick\b/ { "nickname_effect" } _delete_nickname_definition: /delete_nickname_definition\b/ | /dnd\b/ { "delete_nickname_definition" } _remove_nickname: /remove_nickname\b/ | /rnick\b/ { "remove_nickname" } _list_nickname_definitions: /list_nickname_definitions\b/ | /lnd\b/ { "list_nickname_definitions" } _set_effect_name: /set_effect_name\b/ | /sen\b/ { "set_effect_name" } _set_effect_surname: /set_effect_surname\b/ | /ses\b/ { "set_effect_surname" } _remove_effect_name: /remove_effect_name\b/ | /ren\b/ { "remove_effect_name" } _remove_effect_surname: /remove_effect_surname\b/ | /res\b/ { "remove_effect_surname" } _select_track: /select_track\b/ { "select_track" } _set_tempo: /set_tempo\b/ | /tempo\b/ | /tp\b/ { "set_tempo" } _set_sample_rate: /set_sample_rate\b/ | /ssr\b/ { "set_sample_rate" } @@ hotkey_grammar command: set_current_effect command: set_stepsize command: set_jumpsize command: set_parameter command: cancel cancel: /.+Escape/ foo: /./ set_current_effect: 'f' effect_id 'Enter' {Audio::Nama::set_current_op($item{effect_id}) } effect_id: lower_case_effect_id { $return = uc $item{lower_case_effect_id} } lower_case_effect_id: /[a-z]+/ set_stepsize: 'm' digit { Audio::Nama::set_current_stepsize(10**$item{digit})} set_stepsize: 'm-' digit { Audio::Nama::set_current_stepsize(10**-$item{digit})} set_parameter: '=' value 'Enter' {Audio::Nama::set_parameter_value($item{value})} set_jumpsize: 'j' value 'Enter' {$Audio::Nama::text->{hotkey_playback_jumpsize} = $item{value}} digit: /\d/ value: /[+-]?([\d_]+(\.\d*)?|\.\d+)([eE][+-]?\d+)?/ @@ ecasound_chain_operator_hints_yml --- - code: ea count: 1 display: scale name: Volume params: - begin: 0 default: 100 end: 600 name: "Level %" resolution: 0 - code: eadb count: 1 display: scale name: Volume params: - begin: -40 default: 0 end: 60 name: "Level db" resolution: 0.5 - code: epp count: 1 display: scale name: Pan params: - begin: 0 default: 50 end: 100 name: "Level %" resolution: 0 - code: eal count: 1 display: scale name: Limiter params: - begin: 0 default: 100 end: 100 name: "Limit %" resolution: 0 - code: ec count: 2 display: scale name: Compressor params: - begin: 0 default: 1 end: 1 name: "Compression Rate (Db)" resolution: 0 - begin: 0 default: 50 end: 100 name: "Threshold %" resolution: 0 - code: eca count: 4 display: scale name: "Advanced Compressor" params: - begin: 0 default: 69 end: 100 name: "Peak Level %" resolution: 0 - begin: 0 default: 2 end: 5 name: "Release Time (Seconds)" resolution: 0 - begin: 0 default: 0.5 end: 1 name: "Fast Compressor Rate" resolution: 0 - begin: 0 default: 1 end: 1 name: "Compressor Rate (Db)" resolution: 0 - code: enm count: 5 display: scale name: "Noise Gate" params: - begin: 0 default: 100 end: 100 name: "Threshold Level %" resolution: 0 - begin: 0 default: 200 end: 2000 name: "Pre Hold Time (ms)" resolution: 0 - begin: 0 default: 200 end: 2000 name: "Attack Time (ms)" resolution: 0 - begin: 0 default: 200 end: 2000 name: "Post Hold Time (ms)" resolution: 0 - begin: 0 default: 200 end: 2000 name: "Release Time (ms)" resolution: 0 - code: ef1 count: 2 display: scale name: "Resonant Bandpass Filter" params: - begin: 0 default: 0 end: 20000 name: "Center Frequency (Hz)" resolution: 0 - begin: 0 default: 0 end: 2000 name: "Width (Hz)" resolution: 0 - code: ef3 count: 3 display: scale name: "Resonant Lowpass Filter" params: - begin: 0 default: 0 end: 5000 name: "Cutoff Frequency (Hz)" resolution: 0 - begin: 0 default: 0 end: 2 name: Resonance resolution: 0 - begin: 0 default: 0 end: 1 name: Gain resolution: 0 - code: efa count: 2 display: scale name: "Allpass Filter" params: - begin: 0 default: 0 end: 10000 name: "Delay Samples" resolution: 0 - begin: 0 default: 50 end: 100 name: "Feedback %" resolution: 0 - code: efb count: 2 display: scale name: "Bandpass Filter" params: - begin: 0 default: 11000 end: 11000 name: "Center Frequency (Hz)" resolution: 0 - begin: 0 default: 22000 end: 22000 name: "Width (Hz)" resolution: 0 - code: efh count: 1 display: scale name: "Highpass Filter" params: - begin: 10000 default: 10000 end: 22000 name: "Cutoff Frequency (Hz)" resolution: 0 - code: efl count: 1 display: scale name: "Lowpass Filter" params: - begin: 0 default: 0 end: 10000 name: "Cutoff Frequency (Hz)" resolution: 0 - code: efr count: 2 display: scale name: "Bandreject Filter" params: - begin: 0 default: 11000 end: 11000 name: "Center Frequency (Hz)" resolution: 0 - begin: 0 default: 22000 end: 22000 name: "Width (Hz)" resolution: 0 - code: efs count: 2 display: scale name: "Resonator Filter" params: - begin: 0 default: 11000 end: 11000 name: "Center Frequency (Hz)" resolution: 0 - begin: 0 default: 22000 end: 22000 name: "Width (Hz)" resolution: 0 - code: etd count: 5 display: scale name: Delay params: - begin: 0 default: 200 end: 2000 name: "Delay Time (ms)" resolution: 0 - begin: 0 default: 0 end: 2 name: "Surround Mode (Normal, Surround St., Spread)" resolution: 1 - begin: 0 default: 50 end: 100 name: "Number of Delays" resolution: 0 - begin: 0 default: 50 end: 100 name: "Mix %" resolution: 0 - begin: 0 default: 0 end: 100 name: "Feedback %" resolution: 0 - code: etc count: 4 display: scale name: Chorus params: - begin: 0 default: 200 end: 2000 name: "Delay Time (ms)" resolution: 0 - begin: 0 default: 500 end: 10000 name: "Variance Time Samples" resolution: 0 - begin: 0 default: 50 end: 100 name: "Feedback %" resolution: 0 - begin: 0 default: 50 end: 100 name: "LFO Frequency (Hz)" resolution: 0 - code: etr count: 3 display: scale name: Reverb params: - begin: 0 default: 200 end: 2000 name: "Delay Time (ms)" resolution: 0 - begin: 0 default: 0 end: 1 name: "Surround Mode (0=Normal, 1=Surround)" resolution: 1 - begin: 0 default: 50 end: 100 name: "Feedback %" resolution: 0 - code: ete count: 3 display: scale name: "Advanced Reverb" params: - begin: 0 default: 10 end: 100 name: "Room Size (Meters)" resolution: 0 - begin: 0 default: 50 end: 100 name: "Feedback %" resolution: 0 - begin: 0 default: 50 end: 100 name: "Wet %" resolution: 0 - code: etf count: 1 display: scale name: "Fake Stereo" params: - begin: 0 default: 40 end: 500 name: "Delay Time (ms)" resolution: 0 - code: etl count: 4 display: scale name: Flanger params: - begin: 0 default: 200 end: 1000 name: "Delay Time (ms)" resolution: 0 - begin: 0 default: 200 end: 10000 name: "Variance Time Samples" resolution: 0 - begin: 0 default: 50 end: 100 name: "Feedback %" resolution: 0 - begin: 0 default: 50 end: 100 name: "LFO Frequency (Hz)" resolution: 0 - code: etm count: 3 display: scale name: "Multitap Delay" params: - begin: 0 default: 200 end: 2000 name: "Delay Time (ms)" resolution: 0 - begin: 0 default: 20 end: 100 name: "Number of Delays" resolution: 0 - begin: 0 default: 50 end: 100 name: "Mix %" resolution: 0 - code: etp count: 4 display: scale name: Phaser params: - begin: 0 default: 200 end: 2000 name: "Delay Time (ms)" resolution: 0 - begin: 0 default: 100 end: 10000 name: "Variance Time Samples" resolution: 0 - begin: 0 default: 50 end: 100 name: "Feedback %" resolution: 0 - begin: 0 default: 50 end: 100 name: "LFO Frequency (Hz)" resolution: 0 - code: pn:metronome count: 1 display: scale name: Metronome params: - begin: 30 default: 120 end: 300 name: BPM resolution: 1 ... ; @@ default_namarc # # Nama Configuration file # # This file has been auto-generated by Nama # It will not be overwritten, so edit it as you like. # # Notes # # - The format of this file is YAML, preprocessed to allow # comments. # # - A value _must_ be supplied for each 'leaf' field. # For example "mixer_out_format: cd-stereo" # # - A value must _not_ be supplied for nodes, i.e. # 'device:'. The value for 'device' is the entire indented # data structure that follows in subsequent lines. # # - white space *is* significant. Two spaces indent is # required for each sublevel. # # - You may use the tilde symbol '~' to represent a null (undef) value # For example "execute_on_project_load: ~" # # - This file is distinct from .ecasoundrc (which in # general you will not need to run Nama.) # project root directory # all project directories (or their symlinks) will live here project_root: ~ # replaced during first run # define abbreviations abbreviations: 24-mono: s24_le,1,frequency 24-stereo: s24_le,2,frequency,i cd-mono: s16_le,1,44100 cd-stereo: s16_le,2,44100,i frequency: 44100 # define audio devices devices: jack: signal_format: f32_le,N,frequency # do not change this consumer: ecasound_id: alsa,default input_format: cd-stereo output_format: cd-stereo hardware_latency: 0 multi: ecasound_id: alsa,ice1712 input_format: s32_le,12,frequency output_format: s32_le,10,frequency hardware_latency: 0 null: ecasound_id: null output_format: ~ # ALSA soundcard device assignments and formats alsa_capture_device: consumer # for ALSA/OSS alsa_playback_device: consumer # for ALSA/OSS mixer_out_format: cd-stereo # for ALSA/OSS # soundcard_channels: 10 # GUI input/output channel selection range # audio file format templates mix_to_disk_format: s16_le,N,frequency,i raw_to_disk_format: s16_le,N,frequency,i cache_to_disk_format: s16_le,N,frequency,i mixdown_encodings: mp3 ogg sample_rate: frequency realtime_profile: nonrealtime # other choices: realtime or auto # The buffer size settings below apply only when JACK is *not* used ecasound_buffersize: realtime: default: 256 nonrealtime: default: 1024 ecasound_globals: common: -z:mixmode,sum realtime: -z:db,100000 -z:nointbuf nonrealtime: -z:nodb -z:intbuf waveform_height: 200 # ecasound_tcp_port: 2868 # default without midi, till it is sorted out use_midi: 0 # cheap version tracking for our projects, makes possible # undo/redo and branching use_git: 1 # WAVs recorded at the same time get the same numeric suffix use_group_numbering: 1 # Enable pressing SPACE to start/stop transport (in terminal, cursor in column 1) press_space_to_start_transport: 1 # commands to execute each time a project is loaded execute_on_project_load: ~ volume_control_operator: eadb # must be 'ea' or 'eadb' # beep PC speaker on command error # beep_command: beep -f 350 -l 700 # hotkey_beep: beep -f 250 -l 200 # effects for use in mastering mode eq: Parametric1 1 0 0 40 1 0 0 200 1 0 0 600 1 0 0 3300 1 0 low_pass: lowpass_iir 106 2 mid_pass: bandpass_iir 520 800 2 high_pass: highpass_iir 1030 2 compressor: sc4 0 3 16 0 1 3.25 0 spatialiser: matrixSpatialiser 0 limiter: tap_limiter 0 0 hotkeys: Escape: exit_hotkey_mode Insert: previous_track Delete: next_track Home: previous_effect End: next_effect PageUp: previous_param PageDown: next_param Left: previous_param Right: next_param Up: increment_param Down: decrement_param j: decrement_param k: increment_param h: previous_param l: next_param a: previous_track s: previous_effect d: next_effect f: next_track i: previous_track o: next_track I: previous_effect O: next_effect Space: toggle_transport alias: command: mbs: move_to_bus pcv: promote_current_version djp: disable_jack_polling effect: reverb: gverb # end @@ custom_pl ### customize.pl - user code # test this by typing: # # perl customize.pl # # or, if you are running from your build directory, e.g. # # perl -I ~/build/nama/lib customize.pl use Modern::Perl; use Audio::Nama::Globals qw(:all); my @user_customization = ( prompt => sub { no warnings 'uninitialized'; join ' ', 'nama', git_branch_display(), bus_track_display(), '> ' }, ## user defined commands commands => { # usage: greet greet => sub { my ($name,$adjective) = @_; pager("Hello $name! You look $adjective today!!"); }, disable_jack_polling => sub{ $project->{events}->{poll_jack} = undef }, promote_current_version => sub { my $v = $this_track->playback_version; promote_version_to_track($this_track, $v); }, }, ); @@ fake_jack_lsp system:capture_1 alsa_pcm:capture_1 properties: output,can-monitor,physical,terminal, system:capture_2 alsa_pcm:capture_2 properties: output,can-monitor,physical,terminal, system:capture_3 alsa_pcm:capture_3 properties: output,can-monitor,physical,terminal, system:capture_4 alsa_pcm:capture_4 properties: output,can-monitor,physical,terminal, system:capture_5 alsa_pcm:capture_5 properties: output,can-monitor,physical,terminal, system:capture_6 alsa_pcm:capture_6 properties: output,can-monitor,physical,terminal, system:capture_7 alsa_pcm:capture_7 properties: output,can-monitor,physical,terminal, system:capture_8 alsa_pcm:capture_8 properties: output,can-monitor,physical,terminal, system:capture_9 alsa_pcm:capture_9 properties: output,can-monitor,physical,terminal, system:capture_10 alsa_pcm:capture_10 properties: output,can-monitor,physical,terminal, system:capture_11 alsa_pcm:capture_11 properties: output,can-monitor,physical,terminal, system:capture_12 alsa_pcm:capture_12 properties: output,can-monitor,physical,terminal, system:playback_1 alsa_pcm:playback_1 properties: input,physical,terminal, system:playback_2 alsa_pcm:playback_2 properties: input,physical,terminal, system:playback_3 alsa_pcm:playback_3 properties: input,physical,terminal, system:playback_4 alsa_pcm:playback_4 properties: input,physical,terminal, system:playback_5 alsa_pcm:playback_5 properties: input,physical,terminal, system:playback_6 alsa_pcm:playback_6 properties: input,physical,terminal, system:playback_7 alsa_pcm:playback_7 properties: input,physical,terminal, system:playback_8 alsa_pcm:playback_8 properties: input,physical,terminal, system:playback_9 alsa_pcm:playback_9 properties: input,physical,terminal, system:playback_10 alsa_pcm:playback_10 properties: input,physical,terminal, Horgand:out_1 properties: output,terminal, Horgand:out_2 properties: output,terminal, fluidsynth:left properties: output, fluidsynth:right properties: output, NamaEcasound:out_1 properties: output, NamaEcasound:out_2 properties: output, jconvolver:out_1 properties: output, jconvolver:out_2 properties: output, jconvolver:in_1 properties: input, jconvolver:in_2 properties: input, LinuxSampler:0 properties: output, LinuxSampler:1 properties: output, beatrix-0:output-0 properties: output, beatrix-0:output-1 properties: output, @@ fake_lv2_register 1. Calf Compressor -elv2:http://calf.sourceforge.net/plugins/Compressor,'Threshold','Ratio',' ... Attack','Release','Makeup Gain','Knee','Detection','Stereo ... Link','A-weighting','Compression','Peak Output','0dB','Bypass' 2. Calf Filter -elv2:http://calf.sourceforge.net/plugins/Filter,'Frequency','Resonance',' ... Mode','Inertia' 3. Calf Filterclavier -elv2:http://calf.sourceforge.net/plugins/Filterclavier,'Transpose','Detun ... e','Max. Resonance','Mode','Portamento time' 4. Calf Flanger -elv2:http://calf.sourceforge.net/plugins/Flanger,'Minimum ... delay','Modulation depth','Modulation rate','Feedback','Stereo ... phase','Reset','Amount','Dry Amount' 5. Calf Monosynth -elv2:http://calf.sourceforge.net/plugins/Monosynth,'Osc1 Wave','Osc2 ... Wave','O1<>2 Detune','Osc 2 transpose','Phase mode','O1<>2 ... Mix','Filter','Cutoff','Resonance','Separation','Env->Cutoff','Env->Res' ... ,'Env->Amp','Attack','Decay','Sustain','Release','Key Follow','Legato ... Mode','Portamento','Vel->Filter','Vel->Amp','Volume','PBend Range' 6. Calf MultiChorus -elv2:http://calf.sourceforge.net/plugins/MultiChorus,'Minimum ... delay','Modulation depth','Modulation rate','Stereo ... phase','Voices','Inter-voice phase','Amount','Dry Amount','Center Frq ... 1','Center Frq 2','Q' 7. Calf Phaser -elv2:http://calf.sourceforge.net/plugins/Phaser,'Center ... Freq','Modulation depth','Modulation rate','Feedback','# ... Stages','Stereo phase','Reset','Amount','Dry Amount' 8. Calf Reverb -elv2:http://calf.sourceforge.net/plugins/Reverb,'Decay time','High Frq ... Damp','Room size','Diffusion','Wet Amount','Dry Amount','Pre ... Delay','Bass Cut','Treble Cut' 9. Calf Rotary Speaker -elv2:http://calf.sourceforge.net/plugins/RotarySpeaker,'Speed Mode','Tap ... Spacing','Tap Offset','Mod Depth','Treble Motor','Bass Motor','Mic ... Distance','Reflection' 10. Calf Vintage Delay -elv2:http://calf.sourceforge.net/plugins/VintageDelay,'Tempo','Subdivide' ... ,'Time L','Time R','Feedback','Amount','Mix mode','Medium','Dry Amount' 11. IR -elv2:http://factorial.hu/plugins/lv2/ir,'Reverse ... IR','Predelay','Attack','Attack ... time','Envelope','Length','Stretch','Stereo width in','Stereo width ... IR','Autogain','Dry','Dry gain','Wet','Wet ... gain','FileHash0','FileHash1','FileHash2','Dry L meter','Dry R ... meter','Wet L meter','Wet R meter','Latency' 12. Aliasing -elv2:http://plugin.org.uk/swh-plugins/alias,'Aliasing level' 13. Allpass delay line, cubic spline interpolation -elv2:http://plugin.org.uk/swh-plugins/allpass_c,'Max Delay (s)','Delay ... Time (s)','Decay Time (s)' 14. Allpass delay line, linear interpolation -elv2:http://plugin.org.uk/swh-plugins/allpass_l,'Max Delay (s)','Delay ... Time (s)','Decay Time (s)' 15. Allpass delay line, noninterpolating -elv2:http://plugin.org.uk/swh-plugins/allpass_n,'Max Delay (s)','Delay ... Time (s)','Decay Time (s)' 16. AM pitchshifter -elv2:http://plugin.org.uk/swh-plugins/amPitchshift,'Pitch shift','Buffer ... size','latency' 17. Simple amplifier -elv2:http://plugin.org.uk/swh-plugins/amp,'Amps gain (dB)' 18. Analogue Oscillator -elv2:http://plugin.org.uk/swh-plugins/analogueOsc,'Waveform (1=sin, ... 2=tri, 3=squ, 4=saw)','Frequency (Hz)','Warmth','Instability' 19. Artificial latency -elv2:http://plugin.org.uk/swh-plugins/artificialLatency,'Delay ... (ms)','latency' 20. Auto phaser -elv2:http://plugin.org.uk/swh-plugins/autoPhaser,'Attack time ... (s)','Decay time (s)','Modulation depth','Feedback','Spread (octaves)' 21. Glame Bandpass Analog Filter -elv2:http://plugin.org.uk/swh-plugins/bandpass_a_iir,'Center Frequency ... (Hz)','Bandwidth (Hz)' 22. Glame Bandpass Filter -elv2:http://plugin.org.uk/swh-plugins/bandpass_iir,'Center Frequency ... (Hz)','Bandwidth (Hz)','Stages(2 poles per stage)' 23. Bode frequency shifter -elv2:http://plugin.org.uk/swh-plugins/bodeShifter,'Frequency ... shift','latency' 24. Bode frequency shifter (CV) -elv2:http://plugin.org.uk/swh-plugins/bodeShifterCV,'Base shift','Mix ... (-1=down, +1=up)','CV Attenuation','latency' 25. GLAME Butterworth Highpass -elv2:http://plugin.org.uk/swh-plugins/butthigh_iir,'Cutoff Frequency ... (Hz)','Resonance' 26. GLAME Butterworth Lowpass -elv2:http://plugin.org.uk/swh-plugins/buttlow_iir,'Cutoff Frequency ... (Hz)','Resonance' 27. Glame Butterworth X-over Filter -elv2:http://plugin.org.uk/swh-plugins/bwxover_iir,'Cutoff Frequency ... (Hz)','Resonance' 28. Chebyshev distortion -elv2:http://plugin.org.uk/swh-plugins/chebstortion,'Distortion' 29. Comb Filter -elv2:http://plugin.org.uk/swh-plugins/comb,'Band separation ... (Hz)','Feedback' 30. Comb Splitter -elv2:http://plugin.org.uk/swh-plugins/combSplitter,'Band separation (Hz)' 31. Comb delay line, cubic spline interpolation -elv2:http://plugin.org.uk/swh-plugins/comb_c,'Max Delay (s)','Delay Time ... (s)','Decay Time (s)' 32. Comb delay line, linear interpolation -elv2:http://plugin.org.uk/swh-plugins/comb_l,'Max Delay (s)','Delay Time ... (s)','Decay Time (s)' 33. Comb delay line, noninterpolating -elv2:http://plugin.org.uk/swh-plugins/comb_n,'Max Delay (s)','Delay Time ... (s)','Decay Time (s)' 34. Constant Signal Generator -elv2:http://plugin.org.uk/swh-plugins/const,'Signal amplitude' 35. Crossover distortion -elv2:http://plugin.org.uk/swh-plugins/crossoverDist,'Crossover ... amplitude','Smoothing' 36. DC Offset Remover -elv2:http://plugin.org.uk/swh-plugins/dcRemove, 37. Exponential signal decay -elv2:http://plugin.org.uk/swh-plugins/decay,'Decay Time (s)' 38. Decimator -elv2:http://plugin.org.uk/swh-plugins/decimator,'Bit depth','Sample rate ... (Hz)' 39. Declipper -elv2:http://plugin.org.uk/swh-plugins/declip, 40. Simple delay line, cubic spline interpolation -elv2:http://plugin.org.uk/swh-plugins/delay_c,'Max Delay (s)','Delay ... Time (s)' 41. Simple delay line, linear interpolation -elv2:http://plugin.org.uk/swh-plugins/delay_l,'Max Delay (s)','Delay ... Time (s)' 42. Simple delay line, noninterpolating -elv2:http://plugin.org.uk/swh-plugins/delay_n,'Max Delay (s)','Delay ... Time (s)' 43. Delayorama -elv2:http://plugin.org.uk/swh-plugins/delayorama,'Random seed','Input ... gain (dB)','Feedback (%)','Number of taps','First delay (s)','Delay ... range (s)','Delay change','Delay random (%)','Amplitude ... change','Amplitude random (%)','Dry/wet mix' 44. Diode Processor -elv2:http://plugin.org.uk/swh-plugins/diode,'Mode (0 for none, 1 for ... half wave, 2 for full wave)' 45. Audio Divider (Suboctave Generator) -elv2:http://plugin.org.uk/swh-plugins/divider,'Denominator' 46. DJ flanger -elv2:http://plugin.org.uk/swh-plugins/djFlanger,'LFO sync','LFO period ... (s)','LFO depth (ms)','Feedback (%)' 47. DJ EQ -elv2:http://plugin.org.uk/swh-plugins/dj_eq,'Lo gain (dB)','Mid gain ... (dB)','Hi gain (dB)','latency' 48. DJ EQ (mono) -elv2:http://plugin.org.uk/swh-plugins/dj_eq_mono,'Lo gain (dB)','Mid ... gain (dB)','Hi gain (dB)','latency' 49. Dyson compressor -elv2:http://plugin.org.uk/swh-plugins/dysonCompress,'Peak limit ... (dB)','Release time (s)','Fast compression ratio','Compression ratio' 50. Fractionally Addressed Delay Line -elv2:http://plugin.org.uk/swh-plugins/fadDelay,'Delay ... (seconds)','Feedback (dB)' 51. Fast Lookahead limiter -elv2:http://plugin.org.uk/swh-plugins/fastLookaheadLimiter,'Input gain ... (dB)','Limit (dB)','Release time (s)','Attenuation (dB)','latency' 52. Flanger -elv2:http://plugin.org.uk/swh-plugins/flanger,'Delay base (ms)','Max ... slowdown (ms)','LFO frequency (Hz)','Feedback' 53. FM Oscillator -elv2:http://plugin.org.uk/swh-plugins/fmOsc,'Waveform (1=sin, 2=tri, ... 3=squ, 4=saw)' 54. Foldover distortion -elv2:http://plugin.org.uk/swh-plugins/foldover,'Drive','Skew' 55. 4 x 4 pole allpass -elv2:http://plugin.org.uk/swh-plugins/fourByFourPole,'Frequency ... 1','Feedback 1','Frequency 2','Feedback 2','Frequency 3','Feedback ... 3','Frequency 4','Feedback 4' 56. Fast overdrive -elv2:http://plugin.org.uk/swh-plugins/foverdrive,'Drive level' 57. Frequency tracker -elv2:http://plugin.org.uk/swh-plugins/freqTracker,'Tracking speed' 58. Gate -elv2:http://plugin.org.uk/swh-plugins/gate,'LF key filter (Hz)','HF key ... filter (Hz)','Threshold (dB)','Attack (ms)','Hold (ms)','Decay ... (ms)','Range (dB)','Output select (-1 = key listen, 0 = gate, 1 = ... bypass)','Key level (dB)','Gate state' 59. Giant flange -elv2:http://plugin.org.uk/swh-plugins/giantFlange,'Double delay','LFO ... frequency 1 (Hz)','Delay 1 range (s)','LFO frequency 2 (Hz)','Delay 2 ... range (s)','Feedback','Dry/Wet level' 60. Gong model -elv2:http://plugin.org.uk/swh-plugins/gong,'Inner damping','Outer ... damping','Mic position','Inner size 1','Inner stiffness 1 +','Inner ... stiffness 1 -','Inner size 2','Inner stiffness 2 +','Inner stiffness 2 ... -','Inner size 3','Inner stiffness 3 +','Inner stiffness 3 -','Inner ... size 4','Inner stiffness 4 +','Inner stiffness 4 -','Outer size ... 1','Outer stiffness 1 +','Outer stiffness 1 -','Outer size 2','Outer ... stiffness 2 +','Outer stiffness 2 -','Outer size 3','Outer stiffness 3 ... +','Outer stiffness 3 -','Outer size 4','Outer stiffness 4 +','Outer ... stiffness 4 -' 61. Gong beater -elv2:http://plugin.org.uk/swh-plugins/gongBeater,'Impulse gain ... (dB)','Strike gain (dB)','Strike duration (s)' 62. GVerb -elv2:http://plugin.org.uk/swh-plugins/gverb,'Roomsize (m)','Reverb time ... (s)','Damping','Input bandwidth','Dry signal level (dB)','Early ... reflection level (dB)','Tail level (dB)' 63. Hard Limiter -elv2:http://plugin.org.uk/swh-plugins/hardLimiter,'dB limit','Wet ... level','Residue level' 64. Harmonic generator -elv2:http://plugin.org.uk/swh-plugins/harmonicGen,'Fundamental ... magnitude','2nd harmonic magnitude','3rd harmonic magnitude','4th ... harmonic magnitude','5th harmonic magnitude','6th harmonic ... magnitude','7th harmonic magnitude','8th harmonic magnitude','9th ... harmonic magnitude','10th harmonic magnitude' 65. Hermes Filter -elv2:http://plugin.org.uk/swh-plugins/hermesFilter,'LFO1 freq ... (Hz)','LFO1 wave (0 = sin, 1 = tri, 2 = saw, 3 = squ, 4 = s&h)','LFO2 ... freq (Hz)','LFO2 wave (0 = sin, 1 = tri, 2 = saw, 3 = squ, 4 = ... s&h)','Osc1 freq (Hz)','Osc1 wave (0 = sin, 1 = tri, 2 = saw, 3 = squ, ... 4 = noise)','Osc2 freq (Hz)','Osc2 wave (0 = sin, 1 = tri, 2 = saw, 3 = ... squ, 4 = noise)','Ringmod 1 depth (0=none, 1=AM, 2=RM)','Ringmod 2 ... depth (0=none, 1=AM, 2=RM)','Ringmod 3 depth (0=none, 1=AM, ... 2=RM)','Osc1 gain (dB)','RM1 gain (dB)','Osc2 gain (dB)','RM2 gain ... (dB)','Input gain (dB)','RM3 gain (dB)','Xover lower freq','Xover upper ... freq','Dist1 drive','Dist2 drive','Dist3 drive','Filt1 type (0=none, ... 1=LP, 2=HP, 3=BP, 4=BR, 5=AP)','Filt1 freq','Filt1 q','Filt1 ... resonance','Filt1 LFO1 level','Filt1 LFO2 level','Filt2 type (0=none, ... 1=LP, 2=HP, 3=BP, 4=BR, 5=AP)','Filt2 freq','Filt2 q','Filt2 ... resonance','Filt2 LFO1 level','Filt2 LFO2 level','Filt3 type (0=none, ... 1=LP, 2=HP, 3=BP, 4=BR, 5=AP)','Filt3 freq','Filt3 q','Filt3 ... resonance','Filt3 LFO1 level','Filt3 LFO2 level','Delay1 length ... (s)','Delay1 feedback','Delay1 wetness','Delay2 length (s)','Delay2 ... feedback','Delay2 wetness','Delay3 length (s)','Delay3 ... feedback','Delay3 wetness','Band 1 gain (dB)','Band 2 gain (dB)','Band ... 3 gain (dB)' 66. Glame Highpass Filter -elv2:http://plugin.org.uk/swh-plugins/highpass_iir,'Cutoff ... Frequency','Stages(2 poles per stage)' 67. Hilbert transformer -elv2:http://plugin.org.uk/swh-plugins/hilbert,'latency' 68. Non-bandlimited single-sample impulses -elv2:http://plugin.org.uk/swh-plugins/impulse_fc,'Frequency (Hz)' 69. Inverter -elv2:http://plugin.org.uk/swh-plugins/inv, 70. Karaoke -elv2:http://plugin.org.uk/swh-plugins/karaoke,'Vocal volume (dB)' 71. L/C/R Delay -elv2:http://plugin.org.uk/swh-plugins/lcrDelay,'L delay (ms)','L ... level','C delay (ms)','C level','R delay (ms)','R ... level','Feedback','High damp (%)','Low damp (%)','Spread','Dry/Wet ... level' 72. LFO Phaser -elv2:http://plugin.org.uk/swh-plugins/lfoPhaser,'LFO rate (Hz)','LFO ... depth','Feedback','Spread (octaves)' 73. Lookahead limiter -elv2:http://plugin.org.uk/swh-plugins/lookaheadLimiter,'Limit ... (dB)','Lookahead delay','Attenuation (dB)','latency' 74. Lookahead limiter (fixed latency) -elv2:http://plugin.org.uk/swh-plugins/lookaheadLimiterConst,'Limit ... (dB)','Lookahead time (s)','Attenuation (dB)','latency' 75. Glame Lowpass Filter -elv2:http://plugin.org.uk/swh-plugins/lowpass_iir,'Cutoff ... Frequency','Stages(2 poles per stage)' 76. LS Filter -elv2:http://plugin.org.uk/swh-plugins/lsFilter,'Filter type (0=LP, 1=BP, ... 2=HP)','Cutoff frequency (Hz)','Resonance' 77. Matrix: MS to Stereo -elv2:http://plugin.org.uk/swh-plugins/matrixMSSt,'Width' 78. Matrix Spatialiser -elv2:http://plugin.org.uk/swh-plugins/matrixSpatialiser,'Width' 79. Matrix: Stereo to MS -elv2:http://plugin.org.uk/swh-plugins/matrixStMS, 80. Multiband EQ -elv2:http://plugin.org.uk/swh-plugins/mbeq,'50Hz gain (low ... shelving)','100Hz gain','156Hz gain','220Hz gain','311Hz gain','440Hz ... gain','622Hz gain','880Hz gain','1250Hz gain','1750Hz gain','2500Hz ... gain','3500Hz gain','5000Hz gain','10000Hz gain','20000Hz ... gain','latency' 81. Modulatable delay -elv2:http://plugin.org.uk/swh-plugins/modDelay,'Base delay (s)' 82. Multivoice Chorus -elv2:http://plugin.org.uk/swh-plugins/multivoiceChorus,'Number of ... voices','Delay base (ms)','Voice separation (ms)','Detune (%)','LFO ... frequency (Hz)','Output attenuation (dB)' 83. Higher Quality Pitch Scaler -elv2:http://plugin.org.uk/swh-plugins/pitchScaleHQ,'Pitch ... co-efficient','latency' 84. Plate reverb -elv2:http://plugin.org.uk/swh-plugins/plate,'Reverb ... time','Damping','Dry/wet mix' 85. Pointer cast distortion -elv2:http://plugin.org.uk/swh-plugins/pointerCastDistortion,'Effect ... cutoff freq (Hz)','Dry/wet mix' 86. Rate shifter -elv2:http://plugin.org.uk/swh-plugins/rateShifter,'Rate' 87. Retro Flanger -elv2:http://plugin.org.uk/swh-plugins/retroFlange,'Average stall ... (ms)','Flange frequency (Hz)' 88. Reverse Delay (5s max) -elv2:http://plugin.org.uk/swh-plugins/revdelay,'Delay Time (s)','Dry ... Level (dB)','Wet Level (dB)','Feedback','Crossfade samples' 89. Ringmod with LFO -elv2:http://plugin.org.uk/swh-plugins/ringmod_1i1o1l,'Modulation depth ... (0=none, 1=AM, 2=RM)','Frequency (Hz)','Sine level','Triangle ... level','Sawtooth level','Square level' 90. Ringmod with two inputs -elv2:http://plugin.org.uk/swh-plugins/ringmod_2i1o,'Modulation depth ... (0=none, 1=AM, 2=RM)' 91. Barry's Satan Maximiser -elv2:http://plugin.org.uk/swh-plugins/satanMaximiser,'Decay time ... (samples)','Knee point (dB)' 92. SC1 -elv2:http://plugin.org.uk/swh-plugins/sc1,'Attack time (ms)','Release ... time (ms)','Threshold level (dB)','Ratio (1:n)','Knee radius ... (dB)','Makeup gain (dB)' 93. SC2 -elv2:http://plugin.org.uk/swh-plugins/sc2,'Attack time (ms)','Release ... time (ms)','Threshold level (dB)','Ratio (1:n)','Knee radius ... (dB)','Makeup gain (dB)' 94. SC3 -elv2:http://plugin.org.uk/swh-plugins/sc3,'Attack time (ms)','Release ... time (ms)','Threshold level (dB)','Ratio (1:n)','Knee radius ... (dB)','Makeup gain (dB)','Chain balance' 95. SC4 -elv2:http://plugin.org.uk/swh-plugins/sc4,'RMS/peak','Attack time ... (ms)','Release time (ms)','Threshold level (dB)','Ratio (1:n)','Knee ... radius (dB)','Makeup gain (dB)','Amplitude (dB)','Gain reduction (dB)' 96. SE4 -elv2:http://plugin.org.uk/swh-plugins/se4,'RMS/peak','Attack time ... (ms)','Release time (ms)','Threshold level (dB)','Ratio (1:n)','Knee ... radius (dB)','Attenuation (dB)','Amplitude (dB)','Gain expansion (dB)' 97. Wave shaper -elv2:http://plugin.org.uk/swh-plugins/shaper,'Waveshape' 98. Signal sifter -elv2:http://plugin.org.uk/swh-plugins/sifter,'Sift size' 99. Sine + cosine oscillator -elv2:http://plugin.org.uk/swh-plugins/sinCos,'Base frequency ... (Hz)','Pitch offset' 100. Single band parametric -elv2:http://plugin.org.uk/swh-plugins/singlePara,'Gain (dB)','Frequency ... (Hz)','Bandwidth (octaves)' 101. Sinus wavewrapper -elv2:http://plugin.org.uk/swh-plugins/sinusWavewrapper,'Wrap degree' 102. Smooth Decimator -elv2:http://plugin.org.uk/swh-plugins/smoothDecimate,'Resample ... rate','Smoothing' 103. Mono to Stereo splitter -elv2:http://plugin.org.uk/swh-plugins/split, 104. Surround matrix encoder -elv2:http://plugin.org.uk/swh-plugins/surroundEncoder, 105. State Variable Filter -elv2:http://plugin.org.uk/swh-plugins/svf,'Filter type (0=none, 1=LP, ... 2=HP, 3=BP, 4=BR, 5=AP)','Filter freq','Filter Q','Filter resonance' 106. Tape Delay Simulation -elv2:http://plugin.org.uk/swh-plugins/tapeDelay,'Tape speed (inches/sec, ... 1=normal)','Dry level (dB)','Tap 1 distance (inches)','Tap 1 level ... (dB)','Tap 2 distance (inches)','Tap 2 level (dB)','Tap 3 distance ... (inches)','Tap 3 level (dB)','Tap 4 distance (inches)','Tap 4 level ... (dB)' 107. Transient mangler -elv2:http://plugin.org.uk/swh-plugins/transient,'Attack speed','Sustain ... time' 108. Triple band parametric with shelves -elv2:http://plugin.org.uk/swh-plugins/triplePara,'Low-shelving gain ... (dB)','Low-shelving frequency (Hz)','Low-shelving slope','Band 1 gain ... (dB)','Band 1 frequency (Hz)','Band 1 bandwidth (octaves)','Band 2 gain ... (dB)','Band 2 frequency (Hz)','Band 2 bandwidth (octaves)','Band 3 gain ... (dB)','Band 3 frequency (Hz)','Band 3 bandwidth ... (octaves)','High-shelving gain (dB)','High-shelving frequency ... (Hz)','High-shelving slope' 109. Valve saturation -elv2:http://plugin.org.uk/swh-plugins/valve,'Distortion ... level','Distortion character' 110. Valve rectifier -elv2:http://plugin.org.uk/swh-plugins/valveRect,'Sag level','Distortion' 111. VyNil (Vinyl Effect) -elv2:http://plugin.org.uk/swh-plugins/vynil,'Year','RPM','Surface ... warping','Crackle','Wear' 112. Wave Terrain Oscillator -elv2:http://plugin.org.uk/swh-plugins/waveTerrain, 113. Crossfade -elv2:http://plugin.org.uk/swh-plugins/xfade,'Crossfade' 114. Crossfade (4 outs) -elv2:http://plugin.org.uk/swh-plugins/xfade4,'Crossfade' 115. z-1 -elv2:http://plugin.org.uk/swh-plugins/zm1, 116. TalentedHack -elv2:urn:jeremy.salwen:plugins:talentedhack,'Mix','Pull To In ... Tune','Smooth Pitch','Formant Correction','Formant Warp','Jump To Midi ... Input','Pitch Correct Midi Out','Quantize LFO','LFO Amplitude','LFO ... Rate (Hz)','LFO Shape','LFO Symmetry','Concert A (Hz)','Detect ... A','Detect A#','Detect B','Detect C','Detect C#','Detect D','Detect ... D#','Detect E','Detect F','Detect F#','Detect G','Detect G#','Output ... A','Output A#','Output B','Output C','Output C#','Output D','Output ... D#','Output E','Output F','Output F#','Output G','Output G#','Latency' @@ fake_jack_latency system:capture_1 port latency = 1024 frames port playback latency = [ 0 0 ] frames port capture latency = [ 1024 1024 ] frames system:capture_2 port latency = 1024 frames port playback latency = [ 0 0 ] frames port capture latency = [ 1024 1024 ] frames system:playback_1 port latency = 2048 frames port playback latency = [ 2048 2048 ] frames port capture latency = [ 0 0 ] frames system:playback_2 port latency = 2048 frames port playback latency = [ 2048 2048 ] frames port capture latency = [ 0 0 ] frames LinuxSampler:capture_1 port latency = 1024 frames port playback latency = [ 256 256 ] frames port capture latency = [ 512 1024 ] frames LinuxSampler:capture_2 port latency = 1024 frames port playback latency = [ 256 256 ] frames port capture latency = [ 256 1024 ] frames LinuxSampler:playback_1 port latency = 2048 frames port playback latency = [ 2048 2048 ] frames port capture latency = [ 512 512 ] frames LinuxSampler:playback_2 port latency = 2048 frames port playback latency = [ 2048 2048 ] frames port capture latency = [ 512 512 ] frames @@ midi_commands print err h exec debug panic info getunit setunit getfac fac getpos g getlen sel getq setq ev gett getf cf getx cx geti ci geto co mute unmute getmute ls save load reset export import i p r s t mins mcut mdup minfo mtempo msig mend ctlconf ctlconfx ctlunconf ctlinfo m metrocf tlist # ct # tnew # tdel # tren texists taddev tsetf tgetf tcheck tcut tclr tpaste tcopy tins tmerge tquant ttransp tevmap tclist tinfo ilist iexists iset inew idel iren iinfo igetc igetd iaddev irmev olist oexists oset onew odel oren oinfo ogetc ogetd oaddev ormev flist fexists fnew fdel fren finfo freset fmap funmap ftransp fvcurve fchgin fchgout fswapin fswapout xlist xexists xnew xdel xren xinfo xrm xsetd xadd shut proclist builtinlist dnew ddel dmtcrx dmmctx dclktx dclkrx dclkrate dinfo dixctl doxctl @@ default_palette_json { "gui" : { "_nama_palette" : { "Capture" : "#f22c92f088d3", "ClockBackground" : "#998ca489b438", "ClockForeground" : "#000000000000", "GroupBackground" : "#998ca489b438", "GroupForeground" : "#000000000000", "MarkArmed" : "#d74a811f443f", "Mixdown" : "#bf67c5a1491f", "MonBackground" : "#9420a9aec871", "MonForeground" : "Black", "Mute" : "#a5a183828382", "OffBackground" : "#998ca489b438", "OffForeground" : "Black", "Play" : "#68d7aabf755c", "RecBackground" : "#d9156e866335", "RecForeground" : "Black", "SendBackground" : "#9ba79cbbcc8a", "SendForeground" : "Black", "SourceBackground" : "#f22c92f088d3", "SourceForeground" : "Black" }, "_palette" : { "ew" : { "background" : "#d915cc1bc3cf", "foreground" : "black" }, "mw" : { "activeBackground" : "#4e097822b438", "background" : "#c2c5d0b5e49a", "foreground" : "black" } } } } @@ banner //////////////////////////////////////////////////////////////////// / / / Nama multitrack recorder v. $VERSION (c)2008-2019 Joel Roth / / / / Audio processing by Ecasound, courtesy of Kai Vehmanen / / / //////////////////////////////////////////////////////////////////// Starting... @@ midi_help tlist return the list of names of the tracks in the song example: print [tlist] tnew trackname create an empty track named ``trackname'' tdel delete the current track. tren newname change the name of the current track to ``newname'' texists trackname return 1 if ``trackname'' is a track, 0 otherwise taddev measure beat tick ev put the event ``ev'' on the current track at the position given by ``measure'', ``beat'' and ``tick'' tsetf filtname set the default filter (for recording) of the current track to ``filtname''. It will be used in performace mode if there is no current filter. tgetf return the default filter (for recording) of the current track, returns ``nil'' if none tcheck check the current track for orphaned notes, nested notes and other anomalies; also removes multiple controllers in the same tick tcut cut the current selection of the current track. tclr clear the current selection of the current track. only events matching the current event selection (see ev function) are removed. tins amount insert ``amount'' empty measures in the current track, at the current position. tpaste copy the hidden temporary track (filled by tcopy) on the current position of the current track. the current event selection (see ev function) are copied tcopy copy the current selection of the current track into a hidden temporary track. Only events matching the current event selection (see ev function) are copied tquant rate quantize the current selection of the current track using the current quantization step (see setq function). Note positions are rounded to the nearest tick multiple of the quantization step; Rate must be between 0 and 100: 0 means no quantization and 100 means full quantization. ttransp halftones transpose note events of current selection of the current track, by ``halftones'' half tones. Only events matching the current event selection (see ev function) are transposed. tevmap evspec1 evspec2 convert events matching evspec1 (source) into events matching evspec2 (destination) in the current selection of the current track. Both evspec1 and evspec2 must have the same number of devices, channels, notes, controllers etc.. trackmerge sourcetrack merge the ``sourcetrack'' into the current track mute trackname Mute the given track, i.e. events from ``trackname'' will not be played during record/playback. unmute trackname Unmute the given track, i.e. events from ``trackname'' will be played during record/playback. getmute trackname Return 1 if the given track is muted and 0 otherwise. tclist Return the list of channels used by events stored in the current track. tinfo scan the current selection of the current track, an for each measure display the number of events that match the current event selection inew channelname {dev midichan} create an new channel named ``channelname'' and assigned the given device and MIDI channel. iset {dev midichan} set the device/channel pair of the current channel. All filters are updated to use the new channel setting as if the appropriate fchin function was invoked for each filter. idel delete current channel. iren newname rename the current channel to ``newname'' iexists channelname return 1 if ``channelname'' is a channel, 0 otherwise igetc return the MIDI channel number of the current channel igetd channelname return the device number of the current channel iaddev event add the event to the configuration of the current channel, it's not used yet. irmev evspec remove all events matching ``evspec'' (see event ranges) from the configuration of the current channel iinfo print all events on the config of the current channel. onew channelname {dev midichan} create an new channel named ``channelname'' and assigned the given device and MIDI channel. Output channels contain a built-in filter having the same name; by defaut it maps all inputs to the newly created output channel. oset {dev midichan} set the device/channel pair of the current channel. All filters are updated to use the new channel setting as if the appropriate fchout function was invoked for each filter. odel delete current channel. oren newname rename the current channel to ``newname'' iexists channelname return 1 if ``channelname'' is a channel, 0 otherwise ogetc return the MIDI channel number of the current channel ogetd channelname return the device number of the current channel oaddev event add the event to the configuration of the current channel, it's not used yet. ormev evspec remove all events matching ``evspec'' (see event ranges) from the configuration of the current channel oinfo print all events on the config of the current channel. fnew filtname create an new filter named ``filtname'' fdel filtname delete the current filter. fren newname rename the current filter to ``newname'' fexists filtname return 1 if ``filtname'' is a filter, 0 otherwise freset remove all rules from the current filter. finfo list all fitering rules of the current filter fchgin old_evspec new_evspec rewrite all filtering rules of the current filter to consume ``new_evspec'' events instead of ``old_evspec'' events. This means that each rule that would consume ``old_evspec'' on the input will start consuming ``new_evspec'' instead. fswapin evspec1 evspec2 Similar to fchgin but swap ``evspec1'' and ``evspec2'' in the source events set of each rule. fchgout old_evspec new_evspec rewrite all filtering rules of the current filter to produce ``new_evspec'' events instead of ``old_evspec'' events. This means that each rule that would produce ``old_evspec'' on the output will start producing ``new_evspec'' instead. fswapout evspec1 evspec2 Similar to fchgout but swap ``evspec1'' and ``evspec2'' in the destination events set of each rule. fmap evspec1 evspec2 add a new rule to the current filter, to make it convert events matching evspec1 (source) into events matching evspec2 (destination). Both evspec1 and evspec2 must have the same number of devices, channels, notes, controllers etc.. funmap evspec1 evspec2 remove event maps from the current filter. Any mapping with source included in evspec1 and destination inluded in evspec2 is deleted. ftransp evspec halftones transpose events generated by the filter and matching ``evspec'' by the give number of halftones fvcurve evspec weight adjusts velocity of note events produced by the filter, using the given ``weight'' in the -63..63 range. If ``weight'' is: negative - sensitivity is decreased positive - sensitivity is increased zero - the velocity is unchanged xnew sysexname create a new bank of sysex messages named ``sysexname'' xdel delete the current bank of sysex messages. xren newname rename the current sysex bank to ``newname'' xexists sysexname return 1 if ``sysexname'' is a sysex bank, 0 otherwise xrm pattern remove all sysex messages starting with ``pattern'' from the current sysex bank. The given pattern is a list of bytes; an empty pattern matches any sysex message. xsetd newdev pattern set device number to ``newdev'' on all sysex messages starting with ``pattern'' in the current sysex bank. The given pattern is a list of bytes; an empty pattern matches any sysex message. xadd devnum data add to the current sysex bank a new sysex message. ``data'' is a list containing the MIDI system exclusive message and ``devname'' is the device number to which the message will be sent when performance mode is entered xinfo print all sysex messages of the current sysex bank. Messages that are too long to be desplayed on a single line are truncated and the ``...'' string is displayed. ximport devnum path replace contents of the current sysex bank by contents of the given .syx file; messages are assigned to ``devnum'' device number. xexport path store contents of the current sysex bank in the given .syx file enter ``idle'' performance mode. Start processing MIDI input and generating MIDI output. data passes through the current filter (if any) or through the current track's filter (if any). p play the song from the current position. Input passes through the current filter (if any) or through the current track's filter (if any). r play the song and record the input. Input passes through the current filter (if any) or through the current track's filter (if any). On startup, this function play one measure of countdown before the data start being recorded. s stop performance and release MIDI devices. I.e. stop the effect ``i'', ``p'' or ``r'' functions; sendraw device arrayofbytes send raw MIDI data to device number ``device'', for debugging purposes only. ev evspec set the current event selection. Most track editing functions will act only on events matching "evspec", ignoring all other events. setq step set the current quantization step to the given note value, as follow: 4 - quarter note 6 - quarter note triplet 8 - eighth note 12 - eighth note triplet 16 - sixteenth note 24 - sixteenth note triplet etc... The quantization step will be used by tquant function and also by all editing functions to optimize event selection. If the special ``nil'' value is specified as quantization step, then quatization is disabled. getq return the current quatization step g measure set the current song position pointer to the given measure number. Record and playback will start a that position. This also defines the beginning of the current selection used by most track editing functions. getpos return the current song position pointer which is also the start position of the current selection. sel length set the length of the current selection to ``length'' measures. The current selection start at the current position set with the ``g'' function. getlen return the length (in measures) of the current selection. ct trackname set the current track. The current track is the one that will be recorded. Most track editing functions act on it. gett return the current track (if any) or ``nil'' cf filtname set the current filter to ``filtname''. The current filter is the one used to process input MIDI events in performance mode. It's also the one affected by all filter editing functions. getf return the current filter or ``nil'' if none cx sysexname set the current sysex bank, i.e. the one that will be recorded. The current sysex back is the one affected by all sysex editing functions. getx return the current sysex bank or ``nil'' if none ci channame set the current (named) input channel. All input channel editing functions will act on it. geti return the name of the current input channel or ``nil'' if none co channame set the current (named) output channel. All output channel editing functions will act on it. geto return the name of the current output channel or ``nil'' if none setunit ticks_per_unit set the time resolution of the sequencer to ``tpu'' ticks in a whole note (1 unit note = 4 quarter notes). The default is 96 ticks, which is the default of the MIDI standard. getunit return the number of ticks in a whole note fac tempo_factor set the tempo factor for play and record to the given integer value. The tempo factor must be between 50 (play half of the real tempo) and 200 (play at twice the real tempo). getfac return the current tempo factor t beats_per_minute set the tempo at the current song position mins amount {num denom} insert ``amount'' blank measures at the current song position. The time signature used is num/denom. If the time signature is an empty list (i.e. ``{}'') then the time signature at the current position is used. mcut cut the current selection of all tracks, including the time structure. mdup where duplicate the current selection inserting a copy of it at the position given by the ``where'' parameter. The target position is a measure number relative to the current selection to be copied. If ``where'' is positive it's relative to the end of the current selection; if it's negative it's relative to the beginning of the current selection. minfo print the meta-track (tempo changes, time signature changes. mtempo Return the tempo at the current song position. The unit is beats per minute. msig Return the time signature at the current song position. The result is a two number list: numerator and denominator. mend Return the ending measure of the song (i.e. its size in measures). ls list all tracks, channels, filters and various default values save filename save the song into the given file. The ``filename'' is a quoted string. load filename load the song from a file named ``filename''. the current song is destroyed, even if the load command fails. reset destroy completely the song, useful to start a new song without restarting the program export filename save the song into a standard MIDI file, ``filename'' is a quoted string. import filename load the song from a standard MIDI file, ``filename'' is a quoted string. Only MIDI file ``type 1'' and ``type 0'' are supported. dlist return the list of attached devices (list of numbers) dnew devnum filename mode attach MIDI device ``filename'' as device number ``devnum''; ``filename'' is a quoted string. The ``mode'' argument is the name of the mode, it can be on if the following: ``ro'' - read-only, for input only devices ``wo'' - write-only, for output only devices ``rw'' - read and write. If midish is configured to use ALSA (default on Linux systems) then ``filename'' should contain the ALSA sequencer port, as listed by ``aseqdump -l'', (eg. ``28:0'', ``FLUID Synth (qsynth)''). If ``nil'' is given instead of the path, then the port is not connected to any existing port; this allows other ALSA sequencer clients to subscribe to it and to provide events to midish or to consume events midish sends to it. ddel devnum detach device number ``devnum'' dmtcrx devnum use device number ``devnum'' as MTC source. In this case, midish will relocate, start and stop according to incoming MTC messages. Midish will generate its clock ticks from MTC, meaning that it will run at the same speed as the MTC device. This is useful to synchronize midish to an audio multi-tracker or any MTC capable audio application. If ``devnum'' is ``nil'', then MTC messages are ignored and the internal timer will be used instead. dmmctx { devnum1 devnum2 ... } Configure the given devices to transmit MMC start, stop and relocate messages. Useful to control MMC-capable audio applications from midish. By default, devices transmit MMC. dclktx { devnum1 devnum2 ... } Configure the given devices to transmit MIDI clock information (MIDI ticks, MIDI start and MIDI stop events). Useful to synchronize an external sequencer to midish. dclkrx devnum set device number ``devnum'' to be the master MIDI clock source. It will give midish MIDI ticks, MIDI start and MIDI stop events. This useful to synchronize midish to an external sequencer. If ``devnum'' is ``nil'', then the internal clock will be used and midish will act as master device. dclkrate devnum ticrate set the number of ticks in a whole note that are transmitted to the MIDI device (if dclktx was called for it). Default value is 96 ticks. This is the standard MIDI value and its not recommended to change it. dinfo devnum Print some information about the MIDI device. dixctl devnum list Setup the list of controllers that are expected to be received as 14-bit numbers (i.e. both coarse and fine MIDI controller messages will be expected). By default only coarse values are used, if unsure let this list empty. devoxctl devnum list Setup the list of controllers that will be transmitted as 14-bit numbers (both coarse and fine MIDI controller messages). diev devnum list Configure the device to process as a single event the following patterns of input MIDI messages. ``xpc'' - group bank select controllers (0 and 32) with program changes into a signle ``xpc'' event. ``nrpn'' - group NRPN controllers (98 and 99) with data entry controllers (6 and 38) into a single ``nrpn'' event. ``rpn'' - same as ``nrpn'', but for RPN controllers (100 and 101). By default all of the above are enabled, which allows banks, NRPNs and RPNs to be handled by midish the standard way. It makes sense to disable grouping of above messages on rare hardware that maps above-mentioned controller numbers (0, 6, 32, 38, 98, 99, 100, 101) to other parameters than bank number and NRPN/RPN. doev devnum list Same as diev but for output MIDI messages. ctlconf ctlname ctlnumber defval Configure controller number ``ctlnumber'' with name ``ctlname'', and default value ``defval''. If defval is ``nil'' then there is no default value and corresponding controller events are not grouped into frames. See sec. Controller frames. ctlconfx ctlname ctlnumber defval Same as ctlconf function, but for 14-bit controllers. Thus defval is in the range 0..16383. ctlconf ctlname Unconfigure the given controller. ``ctlname'' is the identifier that was used with ctlconf ctlinfo Print the list of configured controllers evpat name sysex_pattern Define a new event type corresponding to the given system exclusive message pattern. The pattern is a list of bytes or event parameter identifiers (aka atoms). The following atoms are supported: v0, v0_lo, v0_hi, v1, v1_lo, v1_hi. They correspond to the full 7-bit value (coarse parameter), the low 7-bit nibble and the high 7-bit nibble (fine grained parameters) of the first and second parameters respectively. Example: evpat master {0xf0 0x7f 0x7f 0x04 0x01 v0_lo v0_hi 0xf7} defines a new event type for the standard master volume system exclusive message. evinfo Print the list of event patterns. m mode Set the mode of the metronome. The following modes are available: ``on'' - turned on for both playback and record ``rec'' - turned on for record only ``off'' - turned off metrocf eventhi eventlo select the notes that the metronome plays. The pair of events must be note-ons info display the list of built-in and user-defined procedures and global variables print expression display the value of the expression err string display the given string and abort the statement being executed. h funcname display list of arguments function ``funcname'' exec filename read and executes the script from a file, ``filename'' is a quoted string. The execution of the script is aborted on error. If the script executes an exit statement, only the script is terminated. debug flag val set debug-flag ``flag'' to (integer) value ``val''. It's a developer knob. If ``val=0'' the corresponding debug-info are turned off. ``flag'' can be: ``filt'' - show events passing through the current filter ``mididev'' - show raw MIDI traffic on stderr ``mixout'' - show conflicts in the output MIDI merger ``norm'' - show events in the input normalizer ``pool'' - show pool usage on exit ``song'' - show start/stop events ``timo'' - show timer internal errors ``mem'' - show memory usage version Display midish version. panic Cause the sequencer to core-dump, useful to developpers. proclist Return the list of all user defined procs. builtinlist Return a list of all builtin commands. @@ aux_midi_commands # This is a Midish configuration file written by F. Silvain. # Get Midish from: # http://www.midish.org/ # This file is GPL version 3 or later. # These commands should help you in your sequencing and editing workflow. #--------------------------------------- # If appropriate commands return a positive number on success and 0 or nil # otherwise. In case of errors commands will also print an error message. # To get the most out of these commands define name input and output channels # for your devices. # List of Midish commands: # fw measures # Forward measures from curent position. # rw measures # Rewind measures from current position or if new position would be less than # 0, go to 0. # show # Display all tracks with additional information. # sh # Display information about the current track. # cclrm start_position clear_measures # Remove clear_measure from the current track, starting at start_position. # cclr start_position end_position # Remove everything between start_position and end_position from current track. # clrm track start_position clear_measures # Remove clear_measures from track, starting at start_position. # clr track start_position end_position # Remove everything from track starting at start_position and ending at # end_position. # cmute # Mute the current track # cunmute # Unmute the current track. # csolo # Mute all but the current track. # solo track # Mute all tracks, with the exception of track. # nosolo # Unmute all tracks. # cquantm start_position quantise_measures quantise_note precision # Quantise the current track. # cquant start_position end_position quantise_note precision # Quantise the current track. # quantm track start_position quantise_measures quantise_note precision # Quantise another track. # quant track start_position end_position quantise_note precision # Quantise another track. # ccopym start_position copy_measures dest_track dest_position # copy copy_measures from current track at start_position to dest_track # at dest_position. # icopym start_position copy_measures dest_position # Copy copy_measures from current track at start_position to dest_position # on the current track. No overlap between copy intervals is allowed. # ccopy start_position end_position dest_track dest_position # Copy from current track between start_position and end_position to # dest_track at dest_position. # icopy start_position end_position dest_position # Copy from current track between start_position and end_position to # dest_position on the current track. No overlaps of copy intervals is allowed. # copym src-track start_position copy_measures dest_track dest_position # Copy copy_measures from src_track starting at start_position to dest_track # at dest_position. # copy src_track start_position end_position dest_track dest_position # Copy from src_track between start_position and end_position to dest_track # at dest_position. # chmap source_track start_position end_position dest_track # map/copy all events from source track default channel, from start_position # to end_position, to dest_track on its default channel. # chmapm source_track start_position copy_measures dest_track # (see chmap and copy commands above) # cchmap start_position end_position dest_track # (see chmap and copy commands above) # cchmapm start_position copy_measures dest_track # (see chmap and copy commands above) # rnew intputchannel outputchannel # Route inputchannel to outputchannel for the current track and create # a filter of the same name as the current track. # radd intput output # Add a routing from intput to output on the current track. # rsplit input left right splitpoint # Create a split for the current track, splitting input to left and right # with splitpoint the highest note of left region. # cchdup source_channel dest_track # Copy everything recorded on current track on source_channel to dest_track. # chdup source_track source_channel dest_track # Copy everything from source_track on source_channel to dest_track # gnew # print the command to create a group # gshow group # Display all tracks from group with additional information. # gmute group # Mute all tracks in group. # gunmute group # Unmute all tracks in group. # gsolo group # Mute all tracks, with the exception of those in group. # gclrm group start_position clear_measures # Remove clear_measures, starting at start_position from all tracks in group. # gclr group start_position end_position # Remove everything from start_position to end_position in all tracks # from group. # gquantm group start_position quantise_measures quantise_note precision # Quantise all tracks in a group. # gquant group start_position end_position quantise_note precision # Quantise all tracks in a group. # gcopym group start_position copy_measures dest_position # copy everything starting at start_position for copy_measures measures # from every group track to every group track at dest_position. # gcopy group start_position end_position dest_position # copy everything from start_position to end_position from every group track # to every group track at dest_position # pgrid denomination # Set the step size of the pattern/step sequencing grid # plen note_length # set note length for the pattern/step sequencing commands (legato). # ppenv bar step denomination note velocity # add MIDI note "note" to the current track on the current output channel # at bar, at step on a grid of denomination notes with velocity. # ppen bar step denomination note # add MIDI note "note" to the current track on teh current output channel # at step position on a grid of denomination notes at full velocity (127). # pstepv bar step note velocity # add MIDI-note note to bar at grid position step with velocity. # pstep bar step note # add MIDI-note note to bar at grid position step. # prepeatv bar step note velocity repeat_count note_length # Add repeat_count MIDI-notes "note" at velocity and note_length to bar at # grid position step. # prepeatv bar step note velocity repeat_count note_length # Add repeat_count MIDI-notes "note" of note_length to bar at grid position # step at velocity. # prepeat bar step note repeat_count note_length # add repeat_count MIDI-notes "note" to bar at grid position step and of length # note_length. # snew synth name # Add new softsynth with portname synth and midish name name. # sdel my_name # Delete softsynth with midish name name. # hnew synth name # Add new hardware synth with portname synth and midish name name. # hdel name # Delete hardware synth with midish name name. # ionew name new_name channelnumber # Add new input and output channels for synth with midish name name and # channel number channelnumber. The new channels will be called new_name. #--------------------------------------- ## Auxiliary procedures # subscription operator, indexing starts at 0 proc lsub my_list my_index { let tmp_ind = 0; for i in $my_list { if $tmp_ind != $my_index { let tmp_ind = $tmp_ind + 1; } else { return $i; } } print {"The list doesn't have" ($my_index+1) "elements."}; return nil; } # Count the number of elements in a list. proc lcount my_list { let cur_count = 0; for i in $my_list { let cur_count = $cur_count + 1; } return $cur_count; } # Test if input channel exists and print an error message otherwise. proc eval_inc my_name { if ![iexists $my_name] { print {"Input channel" $my_name "des not exists. Check your spelling."}; return 0; } else { return 1; } } # Test if output channel exists, print an error message otherwise. proc eval_outc my_name { if ![oexists $my_name] { print {"Output channel" $my_name "does exists. Check your spelling."}; return 0; } else { return 1; } } # Evaluate the mute status of the current track proc eval_mute_status { if [gett] != nil { let mute_status = "Play"; if [getmute [gett]] { let mute_status = "Off"; } return $mute_status; } else { return nil; } } # Print the header for the show procedures proc show_header { print "Trackname Mute-status Filter Channels"; } # Print the show output for one track proc tshow my_track { if [texists $my_track] { ct $my_track; let mute_status = [eval_mute_status]; print {[gett] $mute_status [tgetf] [tclist]}; } } # Evaluate position from keyword or number proc eval_pos my_pos { if $my_pos == start { return 0; } if $my_pos == end { return [mend]; } if $my_pos == now { return [getpos]; } return $my_pos; } # Evaluate if second argument is greater than first argument # (for position ranges) proc eval_positive my_start my_end { if $my_start >= $my_end { print "Start must be less or equal to end."; print {"start is:" $my_start}; print {"End is:" $my_end}; return 0; } else { return 1; } } # Check if a track is currently selected and return it. # If no track is selected return nil and print an error message. proc eval_cur_track { if [gett] { return 1; } else { print "No track selected." return 0; } } # Check if a trackname corresponds to a valid track. proc eval_track my_track { if ![texists $my_track] { print {$my_track "is not a track. Check your spelling."}; return 0; } else { return 1; } } # Call ct with check to prevent error messages. proc sec_ct my_track { if $my_track != nil { ct $my_track; return 1; } else { print "A trackname must be specified for this command."; return 0; } } # Check if track exists and create it, if it doesn't. proc eval_dest_track my_track { if ![texists $my_track] { let cur_track = [gett]; tnew $my_track; ct $cur_track; } } #--------------------------------------- # User commands #--------------------------------------- ## Transport functions # Forward forward_measures from the current position # Example: # fw 8 proc fw forward_measures { let new_pos = [getpos] + $forward_measures; g $new_pos; } # Rewind rewind_measures from current position or go to 0 if resulting # position would be below 0. # Example: # g 8 # rw 3 # now position 5 # rw 8 # now position 0, because 5 - 8 = -3 proc rw rewind_measures { let new_pos = [getpos] - $rewind_measures; if $new_pos < 0 { let new_pos = 0; print {"Unable to rewind" $rewind_measures "from" [getpos] "going to 0."}; return 0; } g $new_pos; return 1; } #--------------------------------------- ## Track commands # Print a list of all tracks with additional information proc show { let cur_track = [gett]; show_header; for i in [tlist] { tshow $i; } sec_ct $cur_track; } # Print information about the current track proc sh { if [eval_cur_track] { let mute_status = [eval_mute_status]; print {"Current track:" [gett]}; print {"Used channels:" [tclist]}; print {"Used filter:" [tgetf]}; print {"Play status:" $mute_status}; return 1; } else { print "No track selected."; return 0; } } # Clear clear_measures of current track starting at start_position # Example: # cclr 2 8 # clear 8 measures starting at measure 2 proc cclrm start_position clear_measures { if [eval_cur_track] { let cur_pos = [getpos]; let start_pos = [eval_pos $start_position]; g $start_pos; sel $clear_measures; tclr; g $cur_pos; return 1; } else { return 0; } } # Clear clear_measures from the track starting at start_position. # Exampe: # clr piano now 8 # remove 8 measures starting at current position proc clrm my_track start_position clear_measures { if [eval_track $my_track] { let cur_track = [gett]; ct $my_track; cclrm $start_position $clear_measures; sec_ct $cur_track; return 1; } else { return 0; } } # Clear current track starting at start_position upto end_position # Example: # cclr 6 end # clear from measure 6 to the end of the song proc cclr start_position end_position { let start_pos = [eval_pos $start_position]; let end_pos = [eval_pos $end_position]; if [eval_positive $start_pos $end_pos] { let clear_measures = $end_pos - $start_pos; return [cclrm $start_pos $clear_measures]; } else { return 0; } } # Clear a track starting at start_position upto end_position. # Example: # clr piano 0 end 16 100 proc clr my_track start_position end_position { if [eval_track $my_track] { let cur_track = [gett]; ct $my_track; return [cclr $start_position $end_position]; sec_ct $cur_track; } else { return 0; } } # Mute the current track proc cmute { if [eval_cur_track] { mute [gett]; return 1; } else { return 0; } } # Unmute the current track proc cunmute { if [eval_cur_track] { unmute [gett]; return 1; } else { return 0; } } # Mute all tracks but the current. proc csolo { if [eval_cur_track] { let cur_track = [gett]; for i in [tlist] { mute $i; } unmute $cur_track; return 1; } else { return 0; } } # Mute all tracks, except the given one. proc solo my_track { if [eval_track $my_track] { let cur_track = [gett]; ct $my_track; let cur_return_value = [csolo]; sec_ct $cur_track; return $cur_return_value; } else { return 0; } } # Unmute all tracks proc nosolo { for i in [tlist] { unmute $i; } } # Quantise current track to the nearest quantise_note denomination with a # precision of precision percent starting at start_position for # quantise_measures measures. # Example: # cquantm now 12 16 100 # quantise the next 8 measures to 16th notes with # # 100% precision proc cquantm start_position quantise_measures quantise_note precision { if [eval_cur_track] { let cur_pos = [getpos]; let start_pos = [eval_pos $start_position]; g $start_pos; if [getq] { let cur_quant = [getq]; } else { let cur_quant = 8; } setq $quantise_note; sel $quantise_measures; tquant $precision; g $cur_pos; setq $cur_quant; return 1; } else { return 0; } } # Quantise a track to the nearest quantise_note denomination with a # precision of precision percent starting at start_position for # quantise_measures measures. # Example: # quantm piano 0 10 8 75 # quantise piano from beginning to 10 measures # # to 8th notes with 75% precision proc quantm my_track start_position quantise_measures quantise_note precision { if [eval_track $my_track] { let cur_track = [gett]; ct $my_track; let cur_return_value = [cquant $start_position $quantise_measures $quantise_note $precision]; sec_ct $cur_track; return $cur_return_value; } else { return 0; } } # Quantise the current track to the nearest quantise_note with a # precision of precision percent starting at start_position upto # end_position. # Example: # cquant 0 end 16 95 proc cquant start_position end_position quantise_note precision { let start_pos = [eval_pos $start_position]; let end_pos = [eval_pos $end_position]; if [eval_positive $start_pos $end_pos] { let quantise_measures = $end_pos - $start_pos; return [cquantm $start_pos $quantise_measures $quantise_note $precision]; } else { return 0; } } # Quantise the track to the nearest quantise_note denomination with a # precision of precision percent starting at start_position upto # end_position. # Example: # quant piano 2 end 16 100 proc quant my_track start_position end_position quantise_note precision { let start_pos = [eval_pos $start_position]; let end_pos = [eval_pos $end_position]; if [eval_positive $start_pos $end_pos] { let quantise_measures = $end_pos - $start_pos; return [quantm $my_track $start_pos $quantise_measures $quantise_note $precision]; } else { return 0; } } # Copy copy_measures from current track starting at start_position to # dest_track at dest_position # example: # ccopym now 8 piano 16 # copy 8 measures starting now to piano at # # measure 16 proc ccopym start_position copy_measures dest_track dest_position { if [eval_cur_track] { eval_dest_track $dest_track; let cur_track = [gett]; let cur_pos = [getpos]; let start_pos = [eval_pos $start_position]; g $start_pos sel $copy_measures; tcopy; ct $dest_track; g $dest_position; tpaste; ct $cur_track; g $cur_pos; return 1; } else { return 0; } } # Copy copy_measures from start_position on the current track to dest_position # on the current track. # No action is taken if the two areas overlap. # example: # icopym now 8 52 # copy 16 measures from current position to measure 52 proc icopym start_position copy_measures dest_position { if [eval_cur_track] { let cur_track = [gett]; let start_pos = [eval_pos $start_position]; let end_pos = $start_pos + $copy_measures; if [eval_positive $end_pos $dest_position] { return [ccopym $start_position $copy_measures $cur_track $dest_position]; } else { print "Copy interval will overlap."; print "Too many measures to copy or destination position too early."; return 0; } } else { return 0; } } # Copy everything between start_position and end_position from current track # to dest_track at measure dest_position. # Example: # ccopy 2 end piano 17 # copy from measure 2 to end of song to piano # # at measure 17 proc ccopy start_position end_position dest_track dest_position { let start_pos = [eval_pos $start_position]; let end_pos = [eval_pos $end_position]; if [eval_positive $start_pos $end_pos] { let copy_measures = $end_pos - $start_pos; return [ccopym $start_pos $copy_measures $dest_track $dest_position]; } else { return 0; } } # Copy everything from start_position to end_position on the current track to # destination position on the current track. # If the intervals overlap, nothing will happen. # example: # icopy 2 10 24 # copy measures 2 to 10 to measure 24 on the current track proc icopy start_position end_position dest_position { if [eval_cur_track] { let cur_track = [gett]; let end_pos = [eval_pos $end_position]; if [eval_positive $end_position $dest_position] { return [ccopy $start_position $end_position $cur_track $dest_position]; } else { print "Copy intervals overlap."; print "Copy interval is too long or"; print "destination position is too early."; return 0; } } else { return 0; } } # Copy copy_measures from src_track starting at start_position to # dest_track at dest_position. # Example: # copym piano now 8 clav 16 proc copym src_track start_position copy_measures dest_track dest_position { if [eval_track $src_track] { let cur_track = [gett]; ct $src_track; let cur_return_value = [ccopym $start_position $copy_measures $dest_track $dest_position]; sec_ct $cur_track; return $cur_return_value; } else { return 0; } } # Copy everything between start_position and end_position from src_track to # dest_track at dest_position. # Example: # copy piano 6 end clav now proc copy src_track start_position end_position dest_track dest_position { if [eval_track $src_track] { let cur_track = [gett]; ct $src_track; let cur_return_value = [ccopy $start_position $end_position $dest_track $dest_position]; sec_ct $cur_track; return $cur_return_value; } else { return 0; } } # Copy everything recorded on the current track on channel channelnumber to # dest_track # Example: # tnew manual # tnew pedals # tnew organ # r # record on organ # s # cchdup 0 manual # cchdup 1 pedals proc cchdup channelnumber dest_track { if [eval_cur_track] { eval_dest_track $dest_track; let cur_track = [gett]; let cur_pos = [getpos]; g 0; ev {any $channelnumber}; sel [mend]; tcopy; ct $dest_track; tpaste; ct $cur_track; g $cur_pos; ev {any}; return 1; } else { return 0; } } # Copy everything recorded on track on channel channelnumber to dest_track. # Example: # chdup organ 2 pedals proc chdup my_track channelnumber dest_track { if [eval_track $my_track] { if [eval_cur_track] { let cur_track = [gett]; } else { let cur_track = $my_track; } ct $my_track; let cur_return_value = [cchdup $channelnumber $dest_track]; ct $cur_track; return $cur_return_value; } else { return 0; } } # Copy all events from measure start_position to endPosition from source_track # and its default output channel to the same start_position on dest_track # and its default output channel. # example: # chmap synth1 0 8 synth2 # copy all events from track synth1 and its # # output channel to the track synth2 and # # its output channel, from measure 0 to 8. proc chmap source_track start_position end_position dest_track { if [eval_track $source_track] { let src_track = $source_track; } else { print {"The source track" $source_track "does not exist."}; return 0; } if [eval_track $dest_track] { let d_track = $dest_track; } else { print {"The destination track" $dest_track "does not exist."}; return 0; } if [eval_cur_track] { let cur_track = [gett]; } else { let cur_track = $src_track; } ct $src_track; let ichan = [tclist]; let ichan_count = [lcount $ichan]; ct $dest_track; let ochan = [tclist]; let ochan_count = [lcount $ochan]; if $ichan_count > 1 { print "Too many source channels."; print "Will only copy tracks with one channel."; return 0; } if $ochan_count > 1 { print "Too many destination channels."; print "Will only copy to tracks with one channel."; return 0; } let start_pos = [eval_pos $start_position]; let end_pos = [eval_pos $end_position]; let cur_return = [copy $src_track $start_position $end_position $dest_track $start_position]; if $cur_return == 0 { return 0; } ct $dest_track; let cur_pos = [getpos]; let cur_ochan = [lsub $ochan 0]; let cur_ichan = [lsub $ichan 0]; let map_measures = $end_pos - $start_pos; g $start_pos; sel $map_measures; tevmap {any $cur_ichan} {any $cur_ochan}; ct $cur_track; g $cur_pos; ct $cur_track; return $cur_return; } # Copy all events from source track starting at measure start_position for # copy_measures and the default output channel to dest_track at the same # start_position and its default output channel. # example: # chmapm piano 2 8 bass # Copy 8 measures from track piano, starting at # # measure 2, on the default output channel to # # track bass at measure 2 and its default output # # channel. proc chmapm source_track start_position copy_measures dest_track { let start_pos = [eval_pos $start_position]; let end_pos = $start_pos + $copy_measures; return [chmap $source_track $start_pos $end_pos $dest_track]; } # Copy all events between start_position and end_position from the current # track and its default output channel to dest_track and on its default # output channel at the same start_position. # example: # ct piano # cchamp now end bass # copy everything from the current track's output # # channel, between now and the end to the track # # bass on its default output channel. proc cchmap start_position end_position dest_track { if [eval_cur_track] { let src_track = [gett]; return [chmap $src_track $start_position $end_position $dest_track]; } else { return 0; } } # Copy all events, starting at measure start_position for copy_measures # measures from the current track and its default output channel to dest_track # on its default output channel. # example: # ct piano # cchmapm 7 12 bass # Copy 12 measures, starting at measure 7 from the # # current track - piano - and its default output # # channel to track bass at measure 7 on its default # # output channel proc cchmapm start_position copy_measures dest_track { if [eval_cur_track] { let src_track = [gett]; return [chmapm $src_track $start_position $copy_measures $dest_track]; } else { return 0; } } # Set input and output channel for the current track. # Example: # rnew keyboard module # accept input from keyboard and pass to module proc rnew inputchannel outputchannel { if [eval_cur_track] { let cur_track = [gett]; if [eval_inc $inputchannel] { if [eval_outc $outputchannel] { let fname = [gett]; if [fexists $fname] { print "A routing filter of that name already exists, stopping..."; return 0; } else { fnew $fname; fmap {any $inputchannel} {any $outputchannel}; taddev 0 0 0 {kat $outputchannel 0 0}; tsetf $fname; } } else { return 0; } } else { return 0; } } else { return 0; } return 1; } # Add more channel routing to a track # Examle: # radd keyboard module2 proc radd inputchannel outputchannel { if [eval_cur_track] { let fname = [gett]; if [fexists $fname] { if [eval_inc $inputchannel] { if [eval_outc $outputchannel] { cf $fname; fmap {any $inputchannel} {any $outputchannel}; } else { return 0; } } else { return 0; } } else { print "Routing filter doesn't exist. Use rnew. Stopping..."; return 0; } } else { return 0; } return 1; } # Create a split on the current track. # Example: # rsplit keyboard bass_module lead_synth 60 # split keyboard into two # # regions, note 0-60 for the bass module and 61-127 for the lead synth. proc rsplit inputchannel leftout rightout splitpoint { if [eval_cur_track] { let fname = [gett]; if [fexists $fname] { fmap {any $inputchannel} {any $leftout}; fmap {any $inputchannel} {any $rightout}; fmap {note $inputchannel 0..$splitpoint} {note $leftout 0..$splitpoint}; fmap {note $inputchannel ($splitpoint+1)..127} {note $rightout ($splitpoint+1)..127}; } else { print "The current track has no filter yet. Use rnew to create one, stopping..."; return 0; } } else { return 0; } return 1; } #--------------------------------------- ## Group commands # Explain how to create a group proc gnew { print "A group is simply a list of tracks. Create it like this:"; print "tnew cello"; print "tnew violin"; print "tnew viola"; print "let strings = {violin cello viola}"; } # Mute a group of tracks # Example: # tnew piano # tnew bass # let band = {piano bass} # gmute $band proc gmute my_group { for i in $my_group { mute $i; } return 1; } # Unmute a group of tracks # Example: # tnew piano # tnew bass # let band = {piano bass} # gunmute $band proc gunmute my_group { for i in $my_group { unmute $i; } return 1; } # Solo a group of tracks (mute everything but the group) # Example: # tnew piano # tnew bass # let band = {piano bass} # gsolo $band proc gsolo my_group { for i in [tlist] { mute $i; } for i in $my_group { unmute $i; } return 1; } # Show list of tracks in my_group with additional information # Example: # tnew piano # tnew bass # let band = {piano bass} # gshow $band proc gshow my_group { let cur_track = [gett]; show_header; for i in $my_group { tshow $i; } sec_ct $cur_track; } # Clear clear_measures from all tracks in the group starting at start_position # Example: # tnew piano # tnew bass # let band = {piano bass} # gclrm $band now 8 # clear 8 measures, starting at measure 6 proc gclrm my_group start_position clear_measures { let cur_return_value = 1; for i in $my_group { let tmp_return_value = [clrm $i]; if ($tmp_return_value == 0) { let cur_return_value = 0; } } return $cur_return_value; } # Clear all tracks in the group starting at start_position to end_position # Example: # tnew piano # tnew bass # let band = {piano bass} # gclr $band 2 end proc gclr my_group start_position end_position { let start_pos = [eval_pos $start_position]; let end_pos = [eval_pos $end_position]; if [eval_positive $start_pos $end_pos] { let cur_return_value = 1; let clear_measures = $end_pos - $start_pos; for i in $my_group { let tmp_return_value = [clrm $i $start_pos $clear_measures]; if ($tmp_return_value == 0) { let cur_return_value = 0; } } return $cur_return_value; } else { return 0; } } # Quantise all track in the group to the nearest quantise_notes with a # precision of precision percent, starting at start_position for # quantise_measures measures. # example: # tnew piano # tnew bass # let band = {piano bass} # gquantm $band now 10 16 100 proc gquantm my_group start_position quantise_measures quantise_note precision { let cur_return_value = 1; for i in $my_group { let tmp_return_value = [quantm $i $start_position $quantise_measures $quantise_note $precision]; if ($tmp_return_value == 0) { let cur_return_value = 0; } } return $cur_return_value; } # Quantise all tracks in the group to the nearest quantise_note with a # precision of precision percent starting at start_position upto # end_position. # Example: # tnew piano # tnew bass # let band = {piano bass} # gquant $band 0 now 8 75 # quantise from start to current position to 8th # # with 100% precision proc gquant my_group start_position end_position quantise_note precision { let start_pos = [eval_pos $start_position]; let end_pos = [eval_pos $end_position]; if [eval_positive $start_pos $end_pos] { let quantise_measures = $end_pos - $start_pos; return [gquantm $my_group $start_pos $quantise_measures $quantise_note $precision]; } else { return 0; } } # Copy each track from the group to itself starting at start_position for # copy_measure measures and put it all at dest_position. proc gcopym my_group start_position copy_measures dest_position { let my_return_value = 1; for i in $my_group { let cur_return_value = [copym $i $start_position $copy_measures $i $dest_position]; if ($cur_return_value == 0) { let my_return_value = 0; } } return $my_return_value; } # Copy the contents of each track in the group from start_position to # end_position to itself at dest_position # Example: # gcopy $mystrings 2 9 10 # copy the group mystrings from measure 2 to measure # # to measure 9 to measure 10 proc gcopy my_group start_position end_position dest_position { let start_pos = [eval_pos $start_position]; let end_pos = [eval_pos $end_position]; if [eval_positive $start_pos $end_pos] { let copy_measures = $end_pos - $start_pos; return [gcopym $my_group $start_pos $copy_measures $dest_position]; } else { return 0; } } #--------------------------------------- ## Device commands # Add new synthesizers to your Midish setup # Create new soft synthesizer with portname my_synth and midish name my_name # Example: # snew "LinuxSampler" ls proc snew my_synth my_name { if ![oexists $my_name] { let cur_ochan = [geto]; let index = 0 for i in [dlist] { let index=$index+1; } if ($index == 6) { let index=$index+1; } dnew $index $my_synth wo onew $my_name {$index 0} dclktx [dlist] co $cur_ochan; return $index; } else { print {$my_name "is already the name of an output channel."}; return nil; } } # Delete soft synthesizer my_name from midish # Example: # sdel ls # remove ls with its channel and midish device. proc sdel my_name { if [eval_outc $my_name] { let cur_ochan = [geto]; co $my_name; let devnum = [ogetd $my_name]; odel; ddel $devnum; dclktx [dlist]; co $cur_ochan; return $devnum; } else { return nil; } } # Create a hardware syntheiszer with portname my_synth and midish name my_name # Example: # hnew "MIDI Cable" xv50 proc hnew my_synth my_name { if ![iexists $my_name] { let cur_ichan = [geti]; let devnum = [snew $my_synth $my_name]; if $devnum != nil { inew $my_name {$devnum 0}; ci $cur_ichan; return 1; } else { print "The device number must be between 0 and 15."; return 0; } } else { print {$my_name "is already the name of an input channel."}; return 0; } } # Delete a hardware synth with its name and channel # Example: # hdel ls proc hdel my_name { if [eval_inc $my_name] { let devnum = [sdel $my_name]; if $devnum != nil { let cur_ichan = [geti]; ci $my_name; iddel; ci $cur_i_chan; return 1; } else { print "The device number must be between 0 and 15."; return 0; } } else { return 0; } } # Add new i/o channel channelnumber for synth with midish name my_name and call # them new_name. # Example: # hnew "MIDI Cable" roland_xv # ionew roland_xv xv_drums 10 proc ionew my_name new_name channelnumber { if [eval_inc $my_name] { if ![iexists $new_name] && ![oexists $new_name] { let cur_ichan = [geti]; let cur_ochan = [geto]; ci $my_name; let devnum = [igetd]; inew $new_name {$devnum $channelnumber }; onew $new_name {$devnum $channelnumber }; ci $cur_ichan; co $cur_ochan; return 1; } else { print {$new_name "is already used for an input or output channel."}; return 0; } } else { return 0; } } #--------------------------------------- ## Pattern/Step sequencer commands. # Pattern note length (how much legato/staccato) let pattern_note_length = 16; # Patter grid, the length of a step let pattern_grid = 16; # Set pattern note length # Example: # plen 16 # set pattern note length to 16th. proc plen my_note_length { if ([eval_denom $my_note_length] && $my_note_length >=2) { let pattern_note_length = $my_note_length; return 1; } else { return 0; } } # Set pattern grid, the length of a step # Example: # pgrid 8 # set the grid to 8th notes proc pgrid my_grid { if [eval_denom $my_grid] { let pattern_grid = $my_grid; return 1; } else { return 0; } } # Check if the denomination for the pattern/step sequencer is valid. proc eval_denom my_denom { if ($my_denom == 0) { print "The note denomination may not be 0..."; return 0; } else { if ([getunit] % $my_denom) { print "The note denomination must be compatible to the ticks per bar."; return 0; } else { return 1; } } } # Get basic info for the pattern/step sequencer commands. Return a list of # beats per bar, denomination of beats, ticks per bar and ticks per beat. proc get_step_info { let cur_beats = ([lsub [msig] 0]); let cur_denom = ([lsub [msig] 1]); let cur_ticks_per_bar = ([getunit] * $cur_beats / $cur_denom); let cur_ticks_per_beat = $cur_ticks_per_bar / $cur_denom; return {$cur_beats $cur_denom $cur_ticks_per_bar $cur_ticks_per_beat}; } # Check if a step position is inside the current bar. proc eval_step_in_bar my_step my_denom my_ticks_per_bar { if ([getunit] * $my_step / $my_denom) > $my_ticks_per_bar { print "The position you have specified is outside this bar."; return 0; } else { return 1; } } # Convert a step position to start and stop position in taddev format. proc step_to_addev my_bar my_step my_denom my_step_info { let cur_beats = [lsub $my_step_info 0]; let cur_denom = [lsub $my_step_info 1]; let cur_ticks_per_bar = [lsub $my_step_info 2]; let cur_ticks_per_beat = [lsub $my_step_info 3]; let my_beat = (([getunit] * $my_step / $my_denom) / $cur_ticks_per_beat); let my_tick = (([getunit] * $my_step / $my_denom) % $cur_ticks_per_beat); let my_length = ([step_to_ticks 1 $pattern_note_length] -1); let my_stop = [add_note_position_addev $my_bar $my_step $my_denom $my_length 96]; return {$my_beat $my_tick [lsub $my_stop 0] [lsub $my_stop 1] [lsub $my_stop 2]}; } # Convert a note length and position to ticks. proc step_to_ticks my_step my_denom { return ([getunit] * $my_step / $my_denom); } # Convert ticks to bar_shift step denomination. proc ticks_to_step my_bar my_ticks my_step_info { let cur_beats = [lsub $my_step_info 0]; let cur_denom = [lsub $my_step_info 1]; let cur_ticks_per_bar = [lsub $my_step_info 2]; let cur_ticks_per_beat = [lsub $my_step_info 3]; let out_bar = $my_bar; if ($my_ticks > $cur_ticks_per_bar) { let my_ticks = $my_ticks - $cur_ticks_per_bar; let out_bar = $out_bar + 1; let cur_pos = [getpos]; g $out_bar; let new_step_info = [get_step_info]; g $cur_pos; return [ticks_to_step $out_bar $my_ticks $new_step_info]; } else { return {$out_bar $my_ticks 96}; } } # Convert ticks to a position in taddev format. proc ticks_to_addev my_bar my_ticks my_step_info { let cur_beats = [lsub $my_step_info 0]; let cur_denom = [lsub $my_step_info 1]; let cur_ticks_per_bar = [lsub $my_step_info 2]; let cur_ticks_per_beat = [lsub $my_step_info 3]; let out_bar = $my_bar; if ($my_ticks > $cur_ticks_per_bar) { let my_ticks = $my_ticks - $cur_ticks_per_bar; let out_bar = $out_bar + 1; let cur_pos = [getpos]; g $out_bar; let new_step_info = [get_step_info]; g $cur_pos; return [ticks_to_step $out_bar $my_ticks $new_step_info]; } else { let out_beat = ($my_ticks / $cur_ticks_per_beat); let out_tick = ($my_ticks % $cur_ticks_per_beat); return {$out_bar $out_beat $out_tick}; } } # Add a note value to a position in format bar step denom and # return a position in bar step denom format. proc add_note_position my_lhs_bar my_lhs_step my_lhs_denom my_rhs_step my_rhs_denom { let full_ticks = [step_to_ticks $my_lhs_step $my_lhs_denom] + [step_to_ticks $my_rhs_step $my_rhs_denom]; let cur_pos = [getpos]; g $my_lhs_bar; let my_step_info = [get_step_info]; g $cur_pos; return [ticks_to_step $my_lhs_bar $full_ticks $my_step_info]; } # Add a note value to a position in format bar step denom and return a position # in format bar beat ticks proc add_note_position_addev my_lhs_bar my_lhs_step my_lhs_denom my_rhs_step my_rhs_denom { let full_ticks = [step_to_ticks $my_lhs_step $my_lhs_denom] + [step_to_ticks $my_rhs_step $my_rhs_denom]; let cur_pos = [getpos]; g $my_lhs_bar; let my_step_info = [get_step_info]; g $cur_pos; return [ticks_to_addev $my_lhs_bar $full_ticks $my_step_info]; } # Add MIDI-note "note" to the current track, on the current output channel, at # measure my_bar and position my_step on a grid of my_denom notes, with # my_velocity. # Example: # ppenv 1 3 8 60 100 # add MIDI-note 60 at velocity 100 to bar 1 (second bar), # # on the fourth eighth note (we count from 0)! proc ppenv my_bar my_step my_denom my_note my_velocity { if [eval_cur_track] { if [eval_denom $my_denom] { if ([eval_denom $pattern_note_length]) { let cur_pos = [getpos]; g $my_bar; let my_step_info = [get_step_info]; let cur_beats = [lsub $my_step_info 0]; let cur_denom = [lsub $my_step_info 1]; let cur_ticks_per_bar = [lsub $my_step_info 2]; let cur_ticks_per_beat = [lsub $my_step_info 3]; if [eval_step_in_bar $my_step $my_denom $cur_ticks_per_bar] { let my_notes = [step_to_addev $my_bar $my_step $my_denom $my_step_info]; for i in [tclist] { taddev $my_bar [lsub $my_notes 0] [lsub $my_notes 1] {non $i $my_note $my_velocity}; taddev [lsub $my_notes 2] [lsub $my_notes 3] [lsub $my_notes 4] {noff $i $my_note $my_velocity}; } g $cur_pos;; return 1; } else { g $cur_pos; return 0; } } else { g $cur_pos; return 0; } } else { return 0; } } else { return 0; } } # Add MIDI-note "note" to the current track, on the current output channel, at # measure my_bar and position my_step on a grid of my_denomination notes. # Velocity is fixed to 127. proc ppen my_bar my_step my_denom my_note { return [ppenv $my_bar $my_step $my_denom $my_note 127]; } # Add MIDI-note "note" to the bar my_bar at grid position my_step with velocity # my_velocity. # Example: # pstepv 1 5 60 100 # add MIDI-note 60 at velocity 100 to bar 1 (second bar) # # at step 5 (6th step) on the current grid proc pstepv my_bar my_step my_note my_velocity { return [ppenv $my_bar $my_step $pattern_grid $my_note $my_velocity]; } # Add MIDI-note "note" at full velocity (127) to my_bar at grid position my_step proc pstep my_bar my_step my_note { return [ppenv $my_bar $my_step $pattern_grid $my_note 127]; } # Add MIDI-note note at velocity my_velocity to bar my_bar starting at position # my_step on the grid repeated my_repeat times as my_denom note values. # Example: # prepeat 2 6 60 100 8 16 # repeat MIDI-note 60 at velocity 100 8 times as # # 16th notes to bar 2 step 6 on the grid proc prepeatv my_bar my_step my_note my_velocity my_repeat my_denom { # Notes must be at least 48th or they will have 0 length if ($my_denom >=2) { let cur_note_length = $pattern_note_length; if ($pattern_note_length < $my_denom) { plen $my_denom; } let cur_count = $my_repeat; let cur_note = {$my_bar $my_step $my_denom}; for i in [builtinlist] { ppenv [lsub $cur_note 0] [lsub $cur_note 1] [lsub $cur_note 2] $my_note $my_velocity; let cur_note = [add_note_position [lsub $cur_note 0] [lsub $cur_note 1] [lsub $cur_note 2] 1 $my_denom]; let cur_count = $cur_count -1; if !($cur_count) { plen $cur_note_length; return 1; } } } else { print "Notes must be at least 48th notes."; return 0; } print "You wanted too many repetitions."; return 0; } # Add MIDI-note note at velocity 127 to bar my_bar starting on position my_step # on the grid, repeated my_repeat times as my_denom note length. # Example: # prepeat 1 2 60 12 6 # put in 6 12th MIDI-notes 60 starting on bar 1 at # # position 2 on the grid. proc prepeat my_bar my_step my_note my_repeat my_denom { return [prepeatv $my_bar $my_step $my_note 127 $my_repeat $my_denom]; } print "FS Midish Extram Commands, version 1.5"; print "For patterns to work correctly use the rnew function for your tracks"; print {"The current pattern grid size is" $pattern_grid}; print {"The current note length (denomination) for pattern commands is" $pattern_note_length};Audio-Nama-1.216/lib/Audio/Nama/0000755000175000017500000000000013544212627015165 5ustar jrothjrothAudio-Nama-1.216/lib/Audio/Nama/Globals.pm0000644000175000017500000000677513544212613017120 0ustar jrothjrothpackage Audio::Nama::Globals; use Modern::Perl; # set aliases for common indices *bn = \%Audio::Nama::Bus::by_name; *tn = \%Audio::Nama::Track::by_name; *ti = \%Audio::Nama::Track::by_index; *mn = \%Audio::Nama::Mark::by_name; *en = \%Audio::Nama::Engine::by_name; *fi = \%Audio::Nama::Effect::by_id; # and the graph *g = \$Audio::Nama::ChainSetup::g; use Exporter; use constant { REC => 'REC', PLAY => 'PLAY', MON => 'MON', OFF => 'OFF', }; our @ISA = 'Exporter'; our @EXPORT_OK = qw( $this_track $this_bus $this_bus_o $this_mark $this_edit $this_sequence $this_engine $this_user $prompt %tn %ti %bn %mn %en %fi $g $debug $debug2 $quiet REC MON PLAY OFF $ui $mode $file $graph $setup $config $jack $fx $fx_cache $text $gui $midi $help $mastering $project @tracks_data @bus_data @groups_data @marks_data @fade_data @edit_data @inserts_data @effects_data @global_effect_chain_vars @global_effect_chain_data @project_effect_chain_data $this_track_name %track_comments %track_version_comments @tracked_vars @persistent_vars ); our %EXPORT_TAGS = ( trackrw => [qw(REC PLAY MON OFF)], singletons => [qw( $ui $mode $file $graph $setup $config $jack $fx $fx_cache $text $gui $midi $help $mastering $project )], var_lists => [qw( @tracked_vars @persistent_vars @global_effect_chain_vars )], pronouns => [qw( $this_track $this_bus $this_bus_o $this_mark $this_edit $this_sequence $this_engine $this_user $prompt %tn %ti %bn %mn %en %fi $g $debug $debug2 $quiet REC MON PLAY OFF )], serialize => [qw( @tracks_data @bus_data @groups_data @marks_data @fade_data @edit_data @inserts_data @effects_data @global_effect_chain_vars @global_effect_chain_data @project_effect_chain_data $this_track_name %track_comments %track_version_comments @tracked_vars @persistent_vars )], ); our $ui = 'bullwinkle'; # for testing { my %seen; push @{$EXPORT_TAGS{all}}, grep {!$seen{$_}++} @{$EXPORT_TAGS{$_}} foreach keys %EXPORT_TAGS; } 1; __END__ =head1 NAME Audio::Nama::Globals - Nama global variables Variables are listed in multiple files in the source. =head2 Exported L exports Nama globals, which it gets by merging the contents of the following files: =over =item F Pronouns (e.g. C<$this_track>) and indices (e.g. C<%tn>, get track by name) =item F Marshalling variables for serializing/deserializing (e.g. C<@tracks_data>) =item F Simple hash structures (such as C<$config>) or objects such as F<$file> that aggregate data. The hashes can be invested with object properties as need be. =back =head2 Other lists =over =item F Maps keys in F<.namarc> (e.g. I) to the corresponding Nama internal scalar (e.g. C<$config-E{mix_to_disk_format}> =item F List of allowed singleton hash keys. Keys of variables appearing in ./var_singletons should be listed in var_keys or in var_config. Undeclared keys will trigger warnings during build. =head2 F Declares lists of variables used in serializing/deserializing. =item C<@global_effect_chain_vars> Mainly user defined and system-wide effect chains, stored in F in the Nama project root directory. =item C<@tracked_vars> These variables are saved to F in the project directory and placed under version control. =item C<@persistent_vars> These Variables saved to F, I under version control. including project-specific effect-chain definitions, and track/version comments. =back =cutAudio-Nama-1.216/lib/Audio/Nama/EcasoundRun.pm0000644000175000017500000001355713544212613017757 0ustar jrothjrothpackage Audio::Nama::EcasoundRun; use Role::Tiny; use Modern::Perl; use Audio::Nama::Globals qw(:all); use Audio::Nama::Log qw(logpkg logsub); sub start { package Audio::Nama; my $self = shift; $self->valid_setup or throw("\nAudio engine is not configured. Cannot start.\n"),return; # use gradual unmuting to avoid pop on start # # # mute unless recording # start # wait 0.5s # unmute # start heartbeat # report engine status # sleep 1s # pager("\n\nStarting at ". current_position()) unless $quiet; schedule_wraparound(); mute(); $self->start_command; $self->{started}++; start_midi_transport() if midi_run_ready(); # limit engine run time if we are in mixdown or edit mode, # or if requested by user, set timer to specified time # defaulting to the result of cs-get-length limit_processing_time( ($setup->{runtime_limit} || $setup->{audio_length}) + $setup->{extra_run_time}) if mixing_only() or edit_mode() or defined $setup->{runtime_limit}; # TODO and live processing #$project->{events}->{post_start_unmute} = AE::timer(0.5, 0, sub{unmute()}); sleeper(0.5); unmute(); sleeper(0.5); $ui->set_engine_mode_color_display(); start_heartbeat(); engine_status() unless $quiet; } sub stop { package Audio::Nama; my $self = shift; if ($self->running()) { # Since the playback position advances slightly during # the fade, we restore the position to exactly where the # stop command was issued. my $pos; $pos = $self->ecasound_iam('getpos') if ! Audio::Nama::ChainSetup::really_recording(); mute(); $self->stop_command; disable_length_timer(); if ( ! $quiet ){ sleeper(0.5); engine_status(current_position(),2,0); } unmute(); stop_heartbeat(); $ui->project_label_configure(-background => $gui->{_old_bg}); # restore exact position transport stop command was issued set_position($pos) if $pos } } sub stop_command { $_[0]->ecasound_iam('stop-sync') } sub start_command { $_[0]->ecasound_iam('start') } ### routines defined in the root namespace package Audio::Nama; use Modern::Perl; use Carp; no warnings 'uninitialized'; use Audio::Nama::Util qw(process_is_running); sub mixing_only { my $i; my $am_mixing; for (Audio::Nama::ChainSetup::really_recording()){ $i++; $am_mixing++ if /Mixdown/; } $i == 1 and $am_mixing } sub sync_transport_position { } sub midish_running { $setup->{midish_running} } sub toggle_transport { $this_engine->running() ? stop_transport() : start_transport() } sub disconnect_transport { return if $this_engine->running; teardown_engine(); } sub engine_is { my $pos = shift; "\n\nEngine is ". $this_engine->ecasound_iam("engine-status"). ( $pos ? " at $pos" : "" ) } sub engine_status { my ($pos, $before_newlines, $after_newlines) = @_; pager("\n" x $before_newlines, engine_is($pos), "\n" x $after_newlines); } sub current_position { my $pos = $this_engine->ecasound_iam("getpos"); colonize(int($pos || 0)) } sub start_heartbeat { $project->{events}->{poll_engine} = AE::timer(0, 1, \&Audio::Nama::heartbeat); $ui->setup_playback_indicator(); } sub stop_heartbeat { # the following test avoids double-tripping rec_cleanup() # following manual stop return unless $project->{events}->{poll_engine}; undef $project->{events}->{poll_engine}; undef $project->{events}->{update_playback_position_display}; $ui->reset_engine_mode_color_display(); rec_cleanup() } sub heartbeat { # print "heartbeat fired\n"; my $here = $this_engine->ecasound_iam("getpos"); my $status = $this_engine->ecasound_iam('engine-status'); if( $status =~ /finished|error/ ){ engine_status(current_position(),2,1); revise_prompt(); stop_heartbeat(); sleeper(0.2); delete $this_engine->{started}; set_position(0); } #print join " ", $status, colonize($here), $/; my ($start, $end); $start = Audio::Nama::Mark::loop_start(); $end = Audio::Nama::Mark::loop_end(); schedule_wraparound() if $mode->{loop_enable} and defined $start and defined $end and ! Audio::Nama::ChainSetup::really_recording(); update_clock_display(); } sub update_clock_display { $ui->clock_config(-text => current_position()); } sub schedule_wraparound { return unless $mode->{loop_enable}; my $here = $this_engine->ecasound_iam("getpos"); my $start = Audio::Nama::Mark::loop_start(); my $end = Audio::Nama::Mark::loop_end(); my $diff = $end - $here; logpkg(__FILE__,__LINE__,'debug', "here: $here, start: $start, end: $end, diff: $diff"); if ( $diff < 0 ){ # go at once set_position($start); cancel_wraparound(); } elsif ( $diff < 3 ) { #schedule the move wraparound($diff, $start); } } sub cancel_wraparound { $project->{events}->{wraparound} = undef; } sub limit_processing_time { my $length = shift; $project->{events}->{processing_time} = AE::timer($length, 0, sub { Audio::Nama::stop_transport(); print prompt() }); } sub disable_length_timer { $project->{events}->{processing_time} = undef; undef $setup->{runtime_limit}; } sub wraparound { my ($diff, $start) = @_; #print "diff: $diff, start: $start\n"; $project->{events}->{wraparound} = undef; $project->{events}->{wraparound} = AE::timer($diff,0, sub{set_position($start)}); } sub stop_do_start { my ($coderef, $delay) = @_; $this_engine->started() ? _stop_do_start( $coderef, $delay) : $coderef->() } sub _stop_do_start { my ($coderef, $delay) = @_; $this_engine->stop_command(); my $result = $coderef->(); sleeper($delay) if $delay; $this_engine->start_command(); $result } sub restart_ecasound { pager_newline("killing ecasound processes @{$en{$Audio::Nama::config->{ecasound_engine_name}}->{pids}}"); kill_my_ecasound_processes(); pager_newline(q(restarting Ecasound engine - your may need to use the "arm" command)); initialize_ecasound_engine(); reconfigure_engine('force'); } sub kill_my_ecasound_processes { my @signals = (15, 9); map{ kill $_, @{$en{$Audio::Nama::config->{ecasound_engine_name}}->{pids}}; sleeper(1)} @signals; } 1Audio-Nama-1.216/lib/Audio/Nama/Modes.pm0000644000175000017500000000633013544212613016567 0ustar jrothjroth# ----------- Modes: mastering, preview, doodle --------- package Audio::Nama; use Modern::Perl; { sub set_preview_mode { # set preview mode, releasing doodle mode if necessary logsub("&preview"); # do nothing if already in 'preview' mode return if $mode->preview; disable_preview_modes(); { no warnings 'uninitialized'; $mode->{preview}++; } pager( <<'MSG'); Setting preview mode. Recording of audio files is disabled. Type 'arm' to enable recording. MSG } sub set_doodle_mode { logsub("&doodle"); return if $this_engine->started() and Audio::Nama::ChainSetup::really_recording(); disable_preview_modes(); { no warnings 'uninitialized'; $mode->{doodle}++; } $tn{Mixdown}->set(rw => OFF); # reconfigure_engine will generate setup and start transport pager( <<'MSG' ); Setting doodle mode. Using live inputs only. Duplicate inputs are excluded. Recording of audio files is disabled. Exit using 'preview' or 'arm' commands MSG } sub exit_preview_modes { logsub("&exit_preview_modes"); return unless $mode->{preview} or $mode->{doodle}; disable_preview_modes(); stop_transport(); pager("Exiting preview/doodle mode"); } sub disable_preview_modes { undef $mode->{preview}; undef $mode->{doodle}; } sub master_on { return if $mode->mastering; # create mastering tracks if needed # we use hiding/unhiding status of Eq track to indicate # mastering mode, so no explicity use of $mode->{mastering} if ( ! $tn{Eq} ){ local $this_track; add_mastering_tracks(); add_mastering_effects(); } else { unhide_mastering_tracks(); map{ $ui->track_gui($tn{$_}->n) } @{$mastering->{track_names}}; } } sub master_off { return if ! $mode->mastering; hide_mastering_tracks(); map{ $ui->remove_track_gui($tn{$_}->n) } @{$mastering->{track_names}}; $this_track = $tn{Main} if grep{ $this_track->name eq $_} @{$mastering->{track_names}}; ; } sub add_mastering_tracks { map{ my $track = Audio::Nama::MasteringTrack->new( name => $_, rw => MON, group => 'Mastering', ); $ui->track_gui( $track->n ); } grep{ $_ ne 'Boost' } @{$mastering->{track_names}}; my $track = Audio::Nama::BoostTrack->new( name => 'Boost', rw => MON, group => 'Mastering', width => 2, source_type => undef, source_id => undef, ); $ui->track_gui( $track->n ); } sub add_mastering_effects { $this_track = $tn{Eq}; nama_cmd("add_effect $mastering->{fx_eq}"); $this_track = $tn{Low}; nama_cmd("add_effect $mastering->{fx_low_pass}"); nama_cmd("add_effect $mastering->{fx_compressor}"); nama_cmd("add_effect $mastering->{fx_spatialiser}"); $this_track = $tn{Mid}; nama_cmd("add_effect $mastering->{fx_mid_pass}"); nama_cmd("add_effect $mastering->{fx_compressor}"); nama_cmd("add_effect $mastering->{fx_spatialiser}"); $this_track = $tn{High}; nama_cmd("add_effect $mastering->{fx_high_pass}"); nama_cmd("add_effect $mastering->{fx_compressor}"); nama_cmd("add_effect $mastering->{fx_spatialiser}"); $this_track = $tn{Boost}; nama_cmd("add_effect $mastering->{fx_limiter}"); # insert after vol } sub unhide_mastering_tracks { nama_cmd("for Mastering; set_track hide 0 rw MON"); } sub hide_mastering_tracks { nama_cmd("for Mastering; set_track hide 1 rw OFF"); } } 1; __END__Audio-Nama-1.216/lib/Audio/Nama/Fade.pm0000644000175000017500000002115313544212613016357 0ustar jrothjroth# ----------- Fade ------------ package Audio::Nama::Fade; use Modern::Perl; use List::Util qw(min); our $VERSION = 1.0; use Carp; use warnings; no warnings qw(uninitialized); our @ISA; use vars qw($n %by_index); use Audio::Nama::Globals qw(:singletons %tn @fade_data); use Audio::Nama::Log qw(logsub logpkg); use Audio::Nama::Effect qw(remove_effect add_effect update_effect); # we don't import 'type' as it would clobber our $fade->type attribute use Audio::Nama::Object qw( n type mark1 mark2 duration relation track class ); initialize(); sub initialize { %by_index = (); @fade_data = (); # for save/restore } sub next_n { my $n = 1; while( $by_index{$n} ){ $n++} $n } sub new { my $class = shift; my %vals = @_; croak "undeclared field: @_" if grep{ ! $_is_field{$_} } keys %vals; my $object = bless { # class => $class, # not needed yet n => next_n(), relation => 'fade_from_mark', @_ }, $class; $by_index{$object->n} = $object; logpkg(__FILE__,__LINE__,'debug',"object class: $class, object type: ", ref $object); my $id = add_fader($object->track); my $track = $tn{$object->track}; Audio::Nama::request_setup(); # fades take effect after next engine stop $object } # helper routines sub refresh_fade_controller { my $track = shift; my @pairs = fader_envelope_pairs($track); add_fader($track->name); my $operator = Audio::Nama::fxn($track->fader)->type; my $off_level = $config->{mute_level}->{$operator}; my $on_level = $config->{unity_level}->{$operator}; my @controllers = @{Audio::Nama::fxn($track->fader)->owns}; logpkg(__FILE__,__LINE__,'debug',$track->name, ": existing controllers: @controllers"); for my $controller (@controllers) { logpkg(__FILE__,__LINE__,'debug',"removing fade controller $controller"); remove_effect($controller); } # add controller my $reuseid = pop @controllers; # we expect only one logpkg(__FILE__,__LINE__,'debug',"applying fade controller"); add_effect({ track => $track, id => $reuseid, parent => $track->fader, type => 'klg', # Ecasound controller params => [ 1, # modify first parameter of fader op $off_level, $on_level, @pairs, ] }); # set fader to correct initial value # first fade is type 'in' : 0 # first fade is type 'out' : 100% update_effect($track->fader,0, initial_level($track->name) * 100) } sub all_fades { my $track_name = shift; sort { $Audio::Nama::Mark::by_name{$a->mark1}->{time} <=> $Audio::Nama::Mark::by_name{$b->mark1}->{time} } grep { $_->track eq $track_name } values %by_index } sub fades { # get fades within playable region my $track_name = shift; my $track = $tn{$track_name}; my @fades = all_fades($track_name); return @fades if ! $mode->{offset_run}; # handle offset run mode my @in_bounds; my $play_end = Audio::Nama::play_end_time(); my $play_start_time = Audio::Nama::play_start_time(); my $length = $track->wav_length; for my $fade (@fades){ my $play_end_time = $play_end ? min($play_end, $length) : $length; my $time = $Audio::Nama::Mark::by_name{$fade->mark1}->{time}; push @in_bounds, $fade if $time >= $play_start_time and $time <= $play_end_time; } @in_bounds } # our envelope must include a straight segment from the # beginning of the track (or region) to the fade # start. Similarly, we need a straight segment # from the last fade to the track (or region) end # - If the first fade is a fade-in, the straight # segment will be at zero-percent level # (otherwise 100%) # # - If the last fade is fade-out, the straight # segment will be at zero-percent level # (otherwise 100%) # although we can get the precise start and endpoints, # I'm using 0 and $track->shifted_playat_time + track length sub initial_level { # return 0, 1 or undef # 0: track starts silent # 1: track starts at full volume my $track_name = shift; my @fades = fades($track_name) or return undef; # if we fade in we'll hold level zero from beginning (scalar @fades and $fades[0]->type eq 'in') ? 0 : 1 } sub exit_level { my $track_name = shift; my @fades = fades($track_name) or return undef; # if we fade out we'll hold level zero from end (scalar @fades and $fades[-1]->type eq 'out') ? 0 : 1 } sub initial_pair { # duration: zero to... my $track_name = shift; my $init_level = initial_level($track_name); defined $init_level or return (); (0, $init_level ) } sub final_pair { # duration: .... to length my $track_name = shift; my $exit_level = exit_level($track_name); defined $exit_level or return (); my $track = $tn{$track_name}; ( $track->shifted_playat_time + $track->wav_length, $exit_level ); } sub fader_envelope_pairs { # return number_of_pairs, pos1, val1, pos2, val2,... my $track = shift; my @fades = fades($track->name); my @specs; for my $fade ( @fades ){ # calculate fades my $marktime1 = Audio::Nama::Mark::mark_time($fade->mark1); my $marktime2 = Audio::Nama::Mark::mark_time($fade->mark2); if ($marktime2) {} # nothing to do elsif( $fade->relation eq 'fade_from_mark') { $marktime2 = $marktime1 + $fade->duration } elsif( $fade->relation eq 'fade_to_mark') { $marktime2 = $marktime1; $marktime1 -= $fade->duration } else { $fade->dumpp; die "fade processing failed" } logpkg(__FILE__,__LINE__,'debug',"marktime1: $marktime1, marktime2: $marktime2"); push @specs, [ $marktime1, $marktime2, $fade->type, Audio::Nama::fxn($track->fader)->type, ]; } # sort fades - may not need this @specs = sort{ $a->[0] <=> $b->[0] } @specs; logpkg(__FILE__,__LINE__,'debug',sub{Audio::Nama::json_out( \@specs)}); my @pairs = map{ spec_to_pairs($_) } @specs; # WEIRD message - try to figure this out # XXX results in bug via AUTOLOAD for Edit # @pairs = (initial_pair($track->name), @pairs, final_pair($track->name)); # add flat segments # - from start to first fade # - from last fade to end # prepend number of pairs; unshift @pairs, (scalar @pairs / 2) if @pairs; @pairs; } # each 'spec' is an array reference of the form [ $from, $to, $type, $op ] # # $from: time (in seconds) # $to: time (in seconds) # $type: 'in' or 'out' # $op: 'ea' or 'eadb' sub spec_to_pairs { my ($from, $to, $type, $op) = @{$_[0]}; logpkg(__FILE__,__LINE__,'debug',"from: $from, to: $to, type: $type"); my $cutpos; my @pairs; # op 'eadb' uses two-stage fade if ($op eq 'eadb'){ if ( $type eq 'out' ){ $cutpos = $from + $config->{fade_time1_fraction} * ($to - $from); push @pairs, ($from, 1, $cutpos, $config->{fade_down_fraction}, $to, 0); } elsif( $type eq 'in' ){ $cutpos = $from + $config->{fade_time2_fraction} * ($to - $from); push @pairs, ($from, 0, $cutpos, $config->{fade_down_fraction}, $to, 1); } } # op 'ea' uses one-stage fade elsif ($op eq 'ea'){ if ( $type eq 'out' ){ push @pairs, ($from, 1, $to, 0); } elsif( $type eq 'in' ){ push @pairs, ($from, 0, $to, 1); } } else { die "missing or illegal fader op: $op" } @pairs } # the following routine makes it possible to # remove an edit fade by the name of the edit mark # ???? does it even work? sub remove_by_mark_name { my $mark1 = shift; my ($i) = map{ $_->n} grep{ $_->mark1 eq $mark1 } values %by_index; remove($i) if $i; } sub remove_by_index { my $i = shift; my $fade = $by_index{$i}; $fade->remove; } sub remove { my $fade = shift; my $track = $tn{$fade->track}; my $i = $fade->n; # remove object from index delete $by_index{$i}; # remove fader entirely if this is the last fade on the track my @track_fades = all_fades($fade->track); if ( ! @track_fades ){ remove_effect($track->fader); $tn{$fade->track}->set(fader => undef); } else { refresh_fade_controller($track) } } sub add_fader { # if it is missing my $name = shift; my $track = $tn{$name}; my $id = $track->fader; # create a fader if necessary, place before first effect # if it exists if (! $id or ! Audio::Nama::fxn($id)){ my $first_effect = $track->ops->[0]; $id = add_effect({ before => $first_effect, track => $track, type => $config->{fader_op}, params => [0], # XX hardcoded for -ea chain operator }); $track->set(fader => $id); } $id } package Audio::Nama; sub fade_uses_mark { my $mark_name = shift; grep{ $_->mark1 eq $mark_name or $_->mark2 eq $mark_name } values %Audio::Nama::Fade::by_index; } sub setup_fades { # + data from Fade objects residing in %Audio::Nama::Fade::by_name # + apply to tracks # * that are part of current chain setup # * that have a fade operator (i.e. most user tracks) map{ Audio::Nama::Fade::refresh_fade_controller($_) } grep{$_->{fader} } Audio::Nama::ChainSetup::engine_tracks(); } 1;Audio-Nama-1.216/lib/Audio/Nama/MuteSoloFade.pm0000644000175000017500000000544513544212613020055 0ustar jrothjroth# ------------- Mute and Solo routines ----------- package Audio::Nama; use Modern::Perl; sub mute { return if $config->{opts}->{F}; return if $tn{Main}->rw eq OFF or Audio::Nama::ChainSetup::really_recording(); $tn{Main}->mute; } sub unmute { return if $config->{opts}->{F}; return if $tn{Main}->rw eq OFF or Audio::Nama::ChainSetup::really_recording(); $tn{Main}->unmute; } sub fade_around { my ($coderef, @args) = @_; if( $this_engine->started() ) { mute(); $coderef->(@args); unmute(); } else { $coderef->(@args) } } sub solo { my @args = @_; # get list of already muted tracks if I haven't done so already if ( ! @{$fx->{muted}} ){ @{$fx->{muted}} = map{ $_->name } grep{ defined $_->old_vol_level} user_tracks() } logpkg(__FILE__,__LINE__,'debug', join " ", "already muted:", sub{map{$_->name} @{$fx->{muted}}}); # convert bunches to tracks my @names = map{ bunch_tracks($_) } @args; # use hashes to store our list my %to_mute; my %not_mute; # get dependent tracks my @dependents = map{ $tn{$_}->bus_tree() } @names; # store solo tracks and dependent tracks that we won't mute map{ $not_mute{$_}++ } @names, @dependents; # find all siblings tracks not in depends list # - get buses list corresponding to our non-muting tracks my %buses; $buses{Main}++; # we always want Main map{ $buses{$_}++ } # add to buses list map { $tn{$_}->group } # corresponding bus (group) names keys %not_mute; # tracks we want # - get sibling tracks we want to mute map{ $to_mute{$_}++ } # add to mute list grep{ ! $not_mute{$_} } # those we *don't* want map{ $bn{$_}->tracks } # tracks list keys %buses; # buses list # mute all tracks on our mute list (do we skip already muted tracks?) do_many_tracks( { tracks => [ keys %to_mute ], method => 'mute' } ); # unmute all tracks on our wanted list do_many_tracks( { tracks => [ keys %not_mute ], method => 'unmute' } ); $mode->{soloing} = 1; } sub nosolo { # unmute all except in @{$fx->{muted}} list # unmute all tracks do_many_tracks( { tracks => [ map{$_->name} user_tracks() ], method => 'unmute' } ); # re-mute previously muted tracks if (@{$fx->{muted}}){ do_many_tracks( { tracks => [ @{$fx->{muted}} ], method => 'mute' } ); } # remove listing of muted tracks @{$fx->{muted}} = (); $mode->{soloing} = 0; } sub all { # unmute all tracks do_many_tracks( { tracks => [ Audio::Nama::Track::user() ], method => 'unmute' } ); # remove listing of muted tracks @{$fx->{muted}} = (); $mode->{soloing} = 0; } sub do_many_tracks { # args: { tracks => [ track objects ], method => method_name } my $args = shift; my $method = $args->{method}; my $delay = $args->{delay} || $config->{engine_muting_time}; map{ $tn{$_}->$method('nofade'); sleeper($delay) } @{$args->{tracks}}; } 1; __END__Audio-Nama-1.216/lib/Audio/Nama/EcasoundSetup.pm0000644000175000017500000001612413544212613020304 0ustar jrothjrothpackage Audio::Nama::EcasoundSetup; use Role::Tiny; use Modern::Perl; use Audio::Nama::Globals qw(:all); use Audio::Nama::Log qw(logpkg logsub); sub setup { package Audio::Nama; no warnings 'uninitialized'; my $self = shift; # return 1 if successful # catch errors from generate_setup_try() and cleanup logsub("&setup"); # extra argument (setup code) will be passed to generate_setup_try() my (@extra_setup_code) = @_; # save current track local $this_track; # prevent engine from starting an old setup ecasound_iam('cs-disconnect') if ecasound_iam('cs-connected'); Audio::Nama::ChainSetup::initialize(); # this is our chance to save state without the noise # of temporary tracks, avoiding the issue of getting diffs # in the project data from each new chain setup. autosave() if $config->{autosave} eq 'setup' and $project->{name} and $config->{use_git} and $project->{repo}; # TODO: use try/catch # catch errors unless testing (no-terminal option) local $@ unless $config->{opts}->{T}; track_memoize(); # freeze track state my $success = $config->{opts}->{T} # don't catch errors during testing ? Audio::Nama::ChainSetup::generate_setup_try(@extra_setup_code) : eval { Audio::Nama::ChainSetup::generate_setup_try(@extra_setup_code) }; track_unmemoize(); # unfreeze track state if ($@){ throw("error caught while generating setup: $@"); Audio::Nama::ChainSetup::initialize(); return } $success; } ### legacy ecasound support routines in root namespace package Audio::Nama; use Modern::Perl; no warnings 'uninitialized'; sub find_duplicate_inputs { # in Main bus only %{$setup->{tracks_with_duplicate_inputs}} = (); %{$setup->{inputs_used}} = (); logsub("&find_duplicate_inputs"); map{ my $source = $_->source; $setup->{tracks_with_duplicate_inputs}->{$_->name}++ if $setup->{inputs_used}->{$source} ; $setup->{inputs_used}->{$source} //= $_->name; } grep { $_->rw eq REC } map{ $tn{$_} } $bn{Main}->tracks(); # track names; } sub load_ecs { my $setup = shift; #say "setup file: $setup " . ( -e $setup ? "exists" : ""); return unless -e $setup; #say "passed conditional"; teardown_engine(); ecasound_iam("cs-load $setup"); ecasound_iam("cs-select $setup"); # needed by Audio::Ecasound, but not Net-ECI !! my $result = ecasound_iam("cs-selected"); $setup eq $result or throw("$result: failed to select chain setup"); logpkg(__FILE__,__LINE__,'debug',sub{map{ecasound_iam($_)} qw(cs es fs st ctrl-status)}); 1; } sub teardown_engine { ecasound_iam("cs-disconnect") if ecasound_iam("cs-connected"); ecasound_iam("cs-remove") if ecasound_iam("cs-selected"); } sub arm { logsub("&arm"); exit_preview_modes(); reconfigure_engine('force'); } # substitute all live inputs by clock-sync'ed # Ecasound null device 'rtnull' sub arm_rtnull { local %Audio::Nama::IO::io_class = qw( null_in Audio::Nama::IO::from_null null_out Audio::Nama::IO::to_null soundcard_in Audio::Nama::IO::from_rtnull soundcard_out Audio::Nama::IO::to_rtnull wav_in Audio::Nama::IO::from_wav wav_out Audio::Nama::IO::to_wav loop_source Audio::Nama::IO::from_loop loop_sink Audio::Nama::IO::to_loop jack_manual_in Audio::Nama::IO::from_rtnull jack_manual_out Audio::Nama::IO::to_rtnull jack_ports_list_in Audio::Nama::IO::from_rtnull jack_ports_list_out Audio::Nama::IO::to_rtnull jack_multi_in Audio::Nama::IO::from_rtnull jack_multi_out Audio::Nama::IO::to_rtnull jack_client_in Audio::Nama::IO::from_rtnull jack_client_out Audio::Nama::IO::to_rtnull ); arm(); } sub something_to_run { $en{ecasound}->valid_setup or midi_run_ready() } sub midi_run_ready { $config->{use_midi} and $en{midish} and $en{midish}->is_active } sub connect_transport { logsub("&connect_transport"); remove_riff_header_stubs(); register_other_ports(); # we won't see Nama ports since Nama hasn't started load_ecs($file->chain_setup) or Audio::Nama::throw("failed to load chain setup"), return; $this_engine->valid_setup() or throw("Invalid chain setup, engine not ready."),return; find_op_offsets(); setup_fades(); apply_ops(); ecasound_iam('cs-connect'); #or throw("Failed to connect setup, engine not ready"),return; my $status = ecasound_iam("engine-status"); if ($status ne 'not started'){ throw("Invalid chain setup, cannot connect engine.\n"); return; } ecasound_iam('engine-launch'); $status = ecasound_iam("engine-status"); if ($status ne 'stopped'){ throw("Failed to launch engine. Engine status: $status\n"); return; } $setup->{audio_length} = ecasound_iam('cs-get-length'); # returns zero if unknown sync_effect_parameters(); register_own_ports(); $ui->length_display(-text => colonize($setup->{audio_length})); ecasound_iam("cs-set-length $setup->{audio_length}") if $tn{Mixdown}->rec_status eq REC and $setup->{audio_length}; $ui->clock_config(-text => colonize(0)); sleeper(0.2); # time for ecasound engine to launch # set delay for seeking under JACK # we use a heuristic based on the number of tracks # but it should be based on the number of PLAY tracks my $track_count; map{ $track_count++ } Audio::Nama::ChainSetup::engine_tracks(); $jack->{seek_delay} = $jack->{jackd_running} ? $config->{engine_base_jack_seek_delay} * ( 1 + $track_count / 20 ) : 0; connect_jack_ports_list(); transport_status() unless $quiet; something_to_run() or throw("Neither audio nor MIDI tracks active. Nothing to run."), return; $ui->flash_ready(); #print ecasound_iam("fs"); 1; } sub transport_status { map{ pager("Warning: $_: input ",$tn{$_}->source, " is already used by track ",$setup->{inputs_used}->{$tn{$_}->source},".") if $setup->{tracks_with_duplicate_inputs}->{$_}; } grep { $tn{$_}->rec } $bn{Main}->tracks; # assume transport is stopped # print looping status, setup length, current position my $start = Audio::Nama::Mark::loop_start(); my $end = Audio::Nama::Mark::loop_end(); #print "start: $start, end: $end, loop_enable: $mode->{loop_enable}\n"; if (ref $setup->{record_midi} and %{$setup->{record_midi}}){ pager(join(" ", keys %{$setup->{record_midi}}), ": ready for caching"); } if ($mode->{loop_enable} and $start and $end){ #if (! $end){ $end = $start; $start = 0} pager("looping from ", heuristic_time($start), "to ", heuristic_time($end)); } pagers("\nNow at: ", current_position()); pagers("Engine is ". ( $this_engine->started() ? "running." : "ready.")); pagers("\nPress SPACE to start or stop engine.") if $config->{press_space_to_start}; } sub trigger_rec_setup_hooks { map { system($_->rec_setup_script) } grep { logpkg(__FILE__,__LINE__,'trace', join "\n", "track ".$_->name, "rec status is: ".$_->rec_status, "old rec status: ".$setup->{_old_rec_status}->{$_->name}, "script was ". (-e $_->rec_setup_script ) ? "found" : "not found" ); $_->rec and $setup->{_old_rec_status}->{$_->name} ne REC and -e $_->rec_setup_script } rec_hookable_tracks(); } sub trigger_rec_cleanup_hooks { map { system($_->rec_cleanup_script) } grep { ! $_->rec and $setup->{_old_rec_status}->{$_->name} eq REC and -e $_->rec_cleanup_script } rec_hookable_tracks(); } 1Audio-Nama-1.216/lib/Audio/Nama/Lat.pm0000644000175000017500000000130713544212613016237 0ustar jrothjrothpackage Audio::Nama::Lat; use Modern::Perl; our @ISA; use Data::Dumper::Concise; use overload '+' => \&add_latency, "\"\"" => sub { join ' ',$_[0]->min, $_[0]->max }; sub new { my $class = shift; my ($min, $max) = @_; defined $min and defined $max or die "undefined field: min: $min or max; $max"; die "Lat object has Min ($min) greater than Max ($max)" if $min > $max; my $self = bless [$min, $max], $class; $self; } sub add_latency { my (@latencies) = @_[0,1]; # throw away swap argument my ($min, $max) = (0,0); map{ $min += $_->min; $max += $_->max } @latencies; Audio::Nama::Lat->new($min, $max); } sub min {$_[0]->[0] } sub max {$_[0]->[1] } sub values { $_[0]->min, $_[0]->max } 1; __END__Audio-Nama-1.216/lib/Audio/Nama/ChainSetup.pm0000644000175000017500000004272313544212613017571 0ustar jrothjroth# ---------- ChainSetup----------- package Audio::Nama::ChainSetup; use Audio::Nama::Globals qw($file $config $jack $setup %tn %bn %en $mode :trackrw $this_engine); use Audio::Nama::Log qw(logsub logpkg); use Modern::Perl; use Data::Dumper::Concise; use Storable qw(dclone); use Audio::Nama::Util qw(signal_format input_node output_node); use Audio::Nama::Assign qw(json_out); no warnings 'uninitialized'; our ( $g, # routing graph object - # based on project data # the routing graph is generated, # then traversed over, and integrated # with track data to generate # Audio::Nama::IO objects. Audio::Nama::IO objects are iterated # over to generate # the Ecasound chain setup text (c.f. chains command) @io, # IO objects corresponding to chain setup %is_ecasound_chain, # chains in final chain seutp # for sorting final result %inputs, %outputs, %post_input, %pre_output, # for final result @input_chains, # list of input chain segments @output_chains, # list of output chain segments @post_input, # post-input chain operators @pre_output, # pre-output chain operators $chain_setup, # final result as string ); sub remove_temporary_tracks { logsub("&remove_temporary_tracks"); map { logpkg(__FILE__,__LINE__,'debug',"removing temporary track ",$_->group.'/'.$_->name); $_->remove } grep{ $_->group eq 'Temp' } Audio::Nama::audio_tracks(); } sub initialize { remove_temporary_tracks(); # we will generate them again $setup->{audio_length} = 0; @io = (); # IO object list Audio::Nama::IO::initialize(); $g = Graph->new(); %inputs = %outputs = %post_input = %pre_output = (); %is_ecasound_chain = (); @input_chains = @output_chains = @post_input = @pre_output = (); undef $chain_setup; Audio::Nama::disable_length_timer(); reset_aux_chain_counter(); unlink $file->chain_setup; delete $this_engine->{valid_setup}; $g; } sub ecasound_chain_setup { $chain_setup } sub is_ecasound_chain { $is_ecasound_chain{$_[0]} } sub engine_tracks { # tracks that belong to current chain setup map{$Audio::Nama::ti{$_}} grep{$Audio::Nama::ti{$_}} keys %is_ecasound_chain; } sub is_engine_track { # takes Track object, name or index # returns object if corresponding track belongs to current chain setup my $t = shift; my $n; if( (ref $t) =~ /Track/){ $n = $t->n } if( $t =~ ! /\D/ ) { $n = $t } if( $t =~ /\D/ and $tn{$_} ){ $n = $Audio::Nama::tn{$t}->n} $Audio::Nama::ti{$n} if $is_ecasound_chain{$n} } sub engine_wav_out_tracks { grep{$_->rec} engine_tracks(); } # return file output entries, including Mixdown sub really_recording { my @files = map{ /-o:(.+?\.wav)$/} grep{ /-o:/ and /\.wav$/} split "\n", $chain_setup; wantarray() ? @files : scalar @files; } sub show_io { my $output = json_out( \%inputs ). json_out( \%outputs ); Audio::Nama::pager( $output ); } sub generate_setup_try { logsub("&generate_setup_try"); my $extra_setup_code = shift; # start with bus routing map{ $_->apply($g) } Audio::Nama::Bus::all(); logpkg(__FILE__,__LINE__,'debug',"Graph after bus routing:\n$g"); # now various manual routing add_paths_for_aux_sends(); logpkg(__FILE__,__LINE__,'debug',"Graph after aux sends:\n$g"); add_paths_from_Main(); logpkg(__FILE__,__LINE__,'debug',"Graph with paths from Main:\n$g"); add_paths_for_mixdown_handling(); logpkg(__FILE__,__LINE__,'debug',"Graph with mixdown mods:\n$g"); # run extra setup $extra_setup_code->($g) if $extra_setup_code; prune_graph(); logpkg(__FILE__,__LINE__,'debug',"Graph after pruning unterminated branches:\n$g"); Audio::Nama::Graph::expand_graph($g); logpkg(__FILE__,__LINE__,'debug',"Graph after adding loop devices:\n$g"); # insert handling Audio::Nama::Graph::add_inserts($g); logpkg(__FILE__,__LINE__,'debug',"Graph with inserts:\n$g"); # Mix tracks to mono if Main is mono # (instead of just throwing away right channel) if ($g->has_vertex('Main') and $tn{Main}->width == 1) { $g->set_vertex_attribute('Main', 'ecs_extra' => '-chmix:1') } #logpkg(__FILE__,__LINE__,'info',sub{"Graph object dump:\n",Dumper($g)}); # create IO lists %inputs and %outputs if ( process_routing_graph() ){ write_chains(); 1 } else { Audio::Nama::throw("No audio tracks to record or play."); 0 } } sub add_paths_for_aux_sends { # not including Main # currently this routing is track-oriented # we could add this to the Audio::Nama::Bus base class # then suppress it in Mixdown and Main groups logsub("&add_paths_for_aux_sends"); map { Audio::Nama::Graph::add_path_for_aux_send($g, $_ ) } grep { (ref $_) !~ /Slave/ and $_->group !~ /Mixdown|Null/ and $_->send_type and $_->rec_status ne OFF } Audio::Nama::audio_tracks(); } sub add_paths_from_Main { logsub("&add_paths_from_Main"); if ($mode->mastering){ $g->add_path(qw[Main Eq Low Boost]); $g->add_path(qw[Eq Mid Boost]); $g->add_path(qw[Eq High Boost]); } else { $g->add_path('Main', output_node($tn{Main}->send_type)) if $tn{Main}->mon and ! $tn{Mixdown}->rec # tests require this, why not generated by # add_paths_for_aux_sends() ?? } } sub add_paths_for_mixdown_handling { logsub("&add_paths_for_mixdown_handling"); my $final_leg_origin = $mode->mastering ? 'Boost' : 'Main'; if ($tn{Mixdown}->rw eq REC ){ # don't monitor via soundcard $g->delete_edge('Main','soundcard_out'); $g->delete_edge('Boost','soundcard_out'); my @p = ($final_leg_origin, ,'Mixdown', 'wav_out'); $g->add_path(@p); $g->set_vertex_attributes('Mixdown', { format_template => $config->{mix_to_disk_format}, chain_id => "Mixdown" }, ); # no effects will be applied because effects are on chain 2 # Mixdown handling - playback } elsif ($tn{Mixdown}->rw eq PLAY and $tn{Mixdown}->playback_version()){ my @e = ('wav_in','Mixdown',output_node($tn{Main}->send_type)); $g->add_path(@e); $g->set_vertex_attributes('Mixdown', { send_type => $tn{Main}->send_type, send_id => $tn{Main}->send_id, chain => "Mixdown" }); # no effects will be applied because effects are on chain 2 } } sub prune_graph { logsub("&prune_graph"); Audio::Nama::Graph::simplify_send_routing($g); logpkg(__FILE__,__LINE__,'debug',"Graph after simplify_send_routing:\n$g"); Audio::Nama::Graph::remove_out_of_bounds_tracks($g) if Audio::Nama::edit_mode(); logpkg(__FILE__,__LINE__,'debug',"Graph after remove_out_of_bounds_tracks:\n$g"); Audio::Nama::Graph::recursively_remove_inputless_tracks($g); logpkg(__FILE__,__LINE__,'debug',"Graph after recursively_remove_inputless_tracks:\n$g"); Audio::Nama::Graph::recursively_remove_outputless_tracks($g); logpkg(__FILE__,__LINE__,'debug',"Graph after recursively_remove_outputless_tracks:\n$g"); } # object based dispatch from routing graph sub process_routing_graph { logsub("&process_routing_graph"); # generate a set of IO objects from edges @io = map{ dispatch($_) } $g->edges; logpkg(__FILE__,__LINE__,'debug', sub{ join "\n",map $_->dump, @io }); # sort chain_ids by attached input object # one line will show all with that one input # -a:3,5,6 -i:foo map { $inputs{$_->ecs_string} //= []; push @{$inputs{$_->ecs_string}}, $_->chain_id; $post_input{$_->chain_id} .= $_->ecs_extra if $_->ecs_extra; $post_input{$_->chain_id} .= join ' ', map{ $_->ecasound_format } $_->channel_ops if $_->channel_ops } grep { $_->direction eq 'input' } @io; map { $outputs{$_->ecs_string} //= []; push @{$outputs{$_->ecs_string}}, $_->chain_id; $pre_output{$_->chain_id} = $_->ecs_extra if $_->ecs_extra; } grep { $_->direction eq 'output' } @io; no warnings 'numeric'; my @in_keys = values %inputs; my @out_keys = values %outputs; use warnings 'numeric'; %is_ecasound_chain = map{ $_, 1} map{ @$_ } values %inputs; # sort entries into an aesthetic order my %rinputs = reverse %inputs; my %routputs = reverse %outputs; @input_chains = sort map {'-a:'.join(',',sort by_chain @$_)." $rinputs{$_}"} @in_keys; @output_chains = sort map {'-a:'.join(',',sort by_chain @$_)." $routputs{$_}"} @out_keys; @post_input = sort by_index map{ "-a:$_ $post_input{$_}"} keys %post_input; @pre_output = sort by_index map{ "-a:$_ $pre_output{$_}"} keys %pre_output; @input_chains + @output_chains # to sense empty chain setup } { my ($m,$n,$o,$p,$q,$r); sub by_chain { ($m,$n,$o) = $a =~ /(\D*)(\d+)(\D*)/ ; ($p,$q,$r) = $b =~ /(\D*)(\d+)(\D*)/ ; if ($n != $q){ $n <=> $q } elsif ( $m ne $p){ $m cmp $p } else { $o cmp $r } } } sub by_index { my ($i) = $a =~ /(\d+)/; my ($j) = $b =~ /(\d+)/; $i <=> $j } sub non_track_dispatch { # loop -> loop # # assign chain_id to edge based on chain_id of left-side loop's # corresponding track: # # hihat_out -- J7a -> Main_in # # soundcard_in -> wav_out (rec_file) # # currently handled using an anonymous track # # we expect edge attributes # to have been provided for handling this. # loop -> soundcard_out # # track7-soundcard_out as aux_send will have chain id S7 # that will be transferred by expand_graph() to # the new edge, loop-soundcard-out # we will issue two IO objects, one for the chain input # fragment, one for the chain output my $edge = shift; logpkg(__FILE__,__LINE__,'debug',"non-track IO dispatch:",join ' -> ',@$edge); my $eattr = $g->get_edge_attributes(@$edge) // {}; logpkg(__FILE__,__LINE__,'debug',"found edge attributes: ",json_out($eattr)) if $eattr; my $vattr = $g->get_vertex_attributes($edge->[0]) // {}; logpkg(__FILE__,__LINE__,'debug',"found vertex attributes: ",json_out($vattr)) if $vattr; if ( ! $eattr->{chain_id} and ! $vattr->{chain_id} ){ my $n = $eattr->{n} || $vattr->{n}; $eattr->{chain_id} = jumper_count($n); } my @direction = qw(input output); map{ my $direction = shift @direction; my $class = Audio::Nama::IO::get_class($_, $direction); my $attrib = {%$vattr, %$eattr}; $attrib->{endpoint} //= $_ if Audio::Nama::Graph::is_a_loop($_); logpkg(__FILE__,__LINE__,'debug',"non-track: $_, class: $class, chain_id: $attrib->{chain_id},","device_id: $attrib->{device_id}"); my $io = $class->new($attrib ? %$attrib : () ) ; $g->set_edge_attribute(@$edge, $direction, $io); $io; } @$edge; } { ### counter for jumper chains # # sequence: J1 J1a J1b J1c, J2, J3, J4, J4d, J4e my %used; my $counter; my $prefix = 'J'; reset_aux_chain_counter(); sub reset_aux_chain_counter { %used = (); $counter = 'a'; } sub jumper_count { my $track_index = shift; my $try1 = $prefix . $track_index; $used{$try1}++, return $try1 unless $used{$try1}; $try1 . $counter++; } } sub dispatch { # creates an IO object from a graph edge my $edge = shift; return non_track_dispatch($edge) if not grep{ $tn{$_} } @$edge ; logpkg(__FILE__,__LINE__,'debug','dispatch: ',join ' -> ', @$edge); my($name, $endpoint, $direction) = decode_edge($edge); logpkg(__FILE__,__LINE__,'debug',"name: $name, endpoint: $endpoint, direction: $direction"); my $track = $tn{$name}; my $class = Audio::Nama::IO::get_class( $endpoint, $direction ); # we need the $direction because there can be # edges to and from loop,Main_in my @args = (track => $name, endpoint => massaged_endpoint($track, $endpoint, $direction), chain_id => $tn{$name}->n, # default override($name, $edge)); # priority: edge > node #say "dispatch class: $class"; my $io = $class->new(@args); $g->set_edge_attribute(@$edge, $direction => $io ); $io } sub massaged_endpoint { my ($track, $endpoint, $direction) = @_; if ( $endpoint =~ /^(loop_in|loop_out)$/ ){ my $final = ($direction eq 'input' ? $track->source_id : $track->send_id ); $final =~ s/^loop,//; $final } else { $endpoint } } sub decode_edge { # assume track-endpoint or endpoint-track # return track, endpoint my ($a, $b) = @{$_[0]}; #say "a: $a, b: $b"; my ($name, $endpoint) = $tn{$a} ? @{$_[0]} : reverse @{$_[0]} ; my $direction = $tn{$a} ? 'output' : 'input'; ($name, $endpoint, $direction) } sub override { # data from edges has priority over data from vertexes # we specify $name, because it could be left or right # vertex logsub("&override"); my ($name, $edge) = @_; (override_from_vertex($name), override_from_edge($edge)) } sub override_from_vertex { my $name = shift; warn("undefined graph\n"), return () unless (ref $g) =~ /Graph/; my $attr = $g->get_vertex_attributes($name); $attr ? %$attr : (); } sub override_from_edge { my $edge = shift; warn("undefined graph\n"), return () unless (ref $g) =~ /Graph/; my $attr = $g->get_edge_attributes(@$edge); $attr ? %$attr : (); } sub write_chains { logsub("&write_chains"); ## write general options my @globals; my $format = signal_format($config->{devices}->{jack}->{signal_format},2); # HARDCODED XXX push @globals, $config->{ecasound_globals}->{common}; push @globals, "-f:$format", join(',', '-G:jack', $config->{ecasound_jack_client_name}, $config->{jack_transport_mode} ) if $jack->{jackd_running}; push @globals, "-b", $config->buffersize, $config->globals_realtime; my $globals = join " ", @globals; my $ecs_file = join "\n\n", "# ecasound chainsetup file", "# general", $globals, "# audio inputs", join("\n", @input_chains), ""; $ecs_file .= join "\n\n", "# post-input processing", join("\n", @post_input), "" if @post_input; $ecs_file .= join "\n\n", "# pre-output processing", join("\n", @pre_output), "" if @pre_output; $ecs_file .= join "\n\n", "# audio outputs", join("\n", @output_chains), ""; logpkg(__FILE__,__LINE__,'debug',"Chain setup:\n",$ecs_file); open(my $fh, ">", $file->chain_setup) or die("can't open chain setup file ".$file->chain_setup.": $!"); print $fh $ecs_file; close $fh; $chain_setup = $ecs_file; } sub setup_requires_realtime { my $prof = $config->{realtime_profile}; if( $prof eq 'auto'){ grep{ ! $_->is_mixing and $_->is_user_track and ($_->rec or $_->mon) } Audio::Nama::audio_tracks() } elsif ( $prof eq 'realtime') { my @fields = qw(soundcard jack_client jack_manual jack_ports_list); grep { has_vertex("$_\_in") } @fields or grep { has_vertex("$_\_out") } @fields } elsif ( $prof eq 'nonrealtime' or !$prof){} } sub has_vertex { $g->has_vertex($_[0]) } 1; =head1 NAME Audio::Nama::ChainSetup - routines for generating Ecasound chain setup =head2 Overview For the Ecasound engine to run, it must be configured into a signal processing network. This configuration is called a "chain setup". It is a graph comprised of multiple signal processing chains, each of which consists of exactly one input and one output. When user input requires a change of configuration, Nama generates an new chain setup file. These files are guaranteed to be consistent with the rules of Ecasound's routing language. After initializing the data structures, Nama iterates over project tracks and buses to create a first-stage graph. This graph is successively transformed as more routing details are added, then each edge of the graph is processed into a pair of IO objects--one for input and one for output--that together constitute an Ecasound chain. With a bit more processing, the configuration is written out as text in the chain setup file. =head2 The Graph and its Transformations Generating a chain setup starts with each bus iterating over its member tracks, and connecting them to its mix track. (See man Audio::Nama::Bus.) In the case of one track belonging to the Main (default) bus, the initial graph would be: soundcard_in -> sax -> Main -> soundcard_out "soundcard_in" and "soundcard_out" will eventually be mapped to the appropriate JACK or ALSA source, depending on whether jackd is running. The Main track hosts the master fader, connects to the main output, and serves as the mix track for the Main bus. If we've asked to record the input, we automatically get this route: soundcard_in -> sax-rec-file -> wav_out The track 'sax-rec-file' is a temporary clone (slave) of track 'sax' and connects to all the same inputs. A 'send' (for example, a instrument monitor for the sax player) generates this additional route: sax -> soundcard_out Ecasound requires that we insert a loop device where signals fan out or fan in. soundcard_in -> sax -> sax_out -> Main -> soundcard_out sax_out -> soundcard_out Here 'sax_out' is a loop device. (Note that we prohibit track names matching *_out or *_in.) Inserts are incorporated by replacing the edge either before or after a track vertex with a network of auxiliary tracks and loop devices. (See man Audio::Nama::Insert.) Unterminated parts of the network are discarded. Then redundant loop devices are removed from the graph to minimize latency. =head2 Dispatch After routing is complete, Nama iterates over the graph's edges, transforming them into pairs of IO objects that become the inputs and outputs of Ecasound chains. To create an Ecasound chain from Main -> soundcard_out Nama uses 'Main' track attributes to provide data. For example track index (1) serves as the chain_id, and the track's send settings determine the soundcard channel or other destination. Some edges are without a track at either terminal. For example this auxiliary send: sax_out -> soundcard_out In this case, the track, chain_id and other data can be specified as vertex or edge attributes. Edge attributes override vertex attributes, which override track attributes. This allows routing to be edited and annotated to behaviors different from what the track wants. When a temporary track is used for recording, for example sax-rec-file -> wav_out The 'sax-rec-file' vertex is assigned the 'chain_id' attribute 'R3' rather than the track index assigned to 'sax-rec-file'. Audio-Nama-1.216/lib/Audio/Nama/Latency.pm0000644000175000017500000003162713544212613017126 0ustar jrothjroth# ----------- Latency Compensation ----------- package Audio::Nama; use Modern::Perl; no warnings 'uninitialized'; use Audio::Nama::Globals qw(:all); use Storable qw(dclone); use List::Util qw(max); use Carp qw(confess); my $lg; # latency_graph, alias to $jack->{graph} latency_memoize(); sub initialize_jack_graph { # make our own copy of the signal network, and an alias $lg = $jack->{graph} = dclone($g); # remove record-to-disk branches of the graph # which are unrelated to latency compensation remove_connections_to_wav_out($lg); # want to deal with specific ports, # so substitute them into the graph replace_terminals_by_jack_ports($lg); } sub propagate_latency { logsub('&propagate_latency'); initialize_jack_graph(); logpkg(__FILE__,__LINE__,'debug',"jack graph\n","$lg"); parse_port_connections(); start_latency_watcher(); propagate_capture_latency(); #propagate_playback_latency(); } sub propagate_capture_latency { my @sinks = grep{ $lg->is_sink_vertex($_) } $lg->vertices(); logpkg(__FILE__,__LINE__,'debug',"recurse through latency graph starting at sinks: @sinks"); latency_rememoize(); map{ latency_of($lg,'capture',$_) } @sinks; } sub propagate_playback_latency { logsub('&propagate_playback_latency'); logpkg(__FILE__,__LINE__,'debug',"jack graph\n","$lg"); my @sources = grep{ $lg->is_source_vertex($_) } $lg->vertices(); logpkg(__FILE__,__LINE__,'debug',"recurse through latency graph starting at sources: @sources"); latency_rememoize(); map{ latency_of($lg,'playback',$_) } @sources; } sub predecessor_latency { scalar @_ > 2 and die "too many args to predecessor_latency: @_"; my ($g, $v) = @_; my $latency = latency_of($g, 'capture', $g->predecessors($v)); logpkg(__FILE__,__LINE__,'debug',"$v: predecessor latency is $latency"); $latency; } sub successor_latency { scalar @_ > 2 and die "too many args to successor_latency: @_"; my ($g, $v) = @_; my $latency = latency_of($g, 'playback', $g->successors($v)); logpkg(__FILE__,__LINE__,'debug',"$v: successor latency is $latency"); $latency } sub latency_of { my ($g, $direction, @v) = @_; if ($direction eq 'capture' and $g->is_sink_vertex(@v)){ die "too many args: @v" if scalar @v > 1; my $latency = predecessor_latency($g, @v); set_capture_latency($latency->values, jack_port_to_nama(@v)); $latency } elsif($direction eq 'playback' and $g->is_source_vertex(@v)){ die "too many args: @v" if scalar @v > 1; my $latency = successor_latency($g,@v); set_playback_latency($latency->values, jack_port_to_nama(@v)); $latency } elsif(scalar @v == 1){ self_latency($g, $direction, @v) } elsif(scalar @v > 1){ sibling_latency($g, $direction, @v) } } sub track_ops_latency { my $track = shift; my $total = 0;; map { $total += op_latency($_) } $track->user_ops; Audio::Nama::Lat->new($total,$total); } sub op_latency { my $op = shift; my $FX = fxn($op); return 0 if $FX->is_controller; # skip controllers my $p = latency_param($op); defined $p and ! $FX->bypassed ? get_live_param($op, $p) : 0 } sub loop_device_latency { Audio::Nama::Lat->new($config->buffersize, $config->buffersize) } sub input_latency { my $port = shift; my $latency = get_capture_latency($port); carp("port $port, asymmetrical latency $latency found\n") if is_asymmetrical($latency); set_capture_latency($latency->values, jack_port_to_nama($port)); $latency } sub is_asymmetrical { my $lat = shift; $lat->min != $lat->max } { my %loop_adjustment; sub sibling_latency { my ($g, $direction, @siblings) = @_; logpkg(__FILE__,__LINE__,'debug',"direction: $direction, Siblings were: @siblings"); if ($direction eq 'capture'){ %loop_adjustment = (); #@siblings = map{ advance_sibling($g, $_) } @siblings; logpkg(__FILE__,__LINE__,'debug',"Siblings are now: @siblings"); my $max = max map {$_->max} map{ self_latency($g, $direction, $_) } @siblings; logpkg(__FILE__,__LINE__,'debug',"$max frames max latency among siblings: @siblings"); for (@siblings) { my $latency = self_latency($g, $direction, $_); my $delay = $max - $latency->max; logpkg(__FILE__,__LINE__,'debug',"$_: self latency: $latency frames"); logpkg(__FILE__,__LINE__,'debug',"$_: delay $delay frames"); compensate_latency($tn{$_},$delay); } Audio::Nama::Lat->new($max,$max); } elsif ($direction eq 'playback'){ my ($final_min, $final_max); for (@siblings){ my $latency = self_latency($g, $direction, $_); my ($min,$max) = $latency->values; $final_min //= $min; $final_min = $min if $min < $final_min; $final_max //= $max; $final_max = $max if $max > $final_max; } $final_min, $final_max } else { die "missing or illegal direction: $direction" } } # not object method sub loop_adjustment { my $trackname = shift; my $delta = $loop_adjustment{$trackname} || 0; Audio::Nama::Lat->new($delta, $delta) } sub self_latency { my ($g, $direction, $node_name) = @_; return input_latency($node_name) if $g->is_source_vertex($node_name); my $latency = my $predecessor_or_successor_latency = $direction eq 'capture' ? predecessor_latency($g, $node_name) : successor_latency($g, $node_name); ref $latency eq 'Audio::Nama::Lat' or die "wrong type for $node_name".Dumper $latency; return( $predecessor_or_successor_latency + track_ops_latency($tn{$node_name}) + loop_adjustment($node_name) + Audio::Nama::Insert::soundcard_delay($node_name) # if we're a wet return track and insert is # a hardware type, i.e. via the soundcard ) if Audio::Nama::Graph::is_a_track($node_name); return( $predecessor_or_successor_latency + loop_device_latency() ) if Audio::Nama::Graph::is_a_loop($node_name); die "shouldn't reach here\nnodename: $node_name, graph:$g"; } } sub remove_connections_to_wav_out { my $g = shift; Audio::Nama::Graph::remove_branch($g,'wav_out'); Audio::Nama::Graph::remove_isolated_vertices($g); } sub replace_terminals_by_jack_ports { my $g = shift; my @sinks = grep{ $g->is_sink_vertex($_) } $g->vertices(); my @sources = grep{ $g->is_source_vertex($_) } $g->vertices(); for my $sink (@sinks) { #logpkg(__FILE__,__LINE__,'debug') logpkg(__FILE__,__LINE__,'debug',"found sink $sink"); my @predecessors = $g->predecessors($sink); logpkg(__FILE__,__LINE__,'debug',"preceeded by: @predecessors"); my @edges = map{ [$_, $sink] } @predecessors; ; logpkg(__FILE__,__LINE__,'debug',"edges: ",json_out(\@edges)); for my $edge ( @edges ) { logpkg(__FILE__,__LINE__,'debug',"edge: @$edge"); my $output = $g->get_edge_attribute(@$edge, "output") || $g->get_vertex_attribute($edge->[0], "output"); logpkg(__FILE__,__LINE__,'debug',Dumper $output); logpkg(__FILE__,__LINE__,'debug', join " ", "JACK client:", $output->client, $output->ports); $g->delete_edge(@$edge); for my $port($output->ports()){ $g->add_edge($edge->[0], $port); #$g->set_edge_attribute($edge->[0], $port, "output", $output); } } } for my $source (@sources) { #logpkg(__FILE__,__LINE__,'debug') logpkg(__FILE__,__LINE__,'debug',"found source $source"); my @successors = $g->successors($source); logpkg(__FILE__,__LINE__,'debug',"succeeded by: @successors"); my @edges = map{ [$source, $_] } @successors; ; logpkg(__FILE__,__LINE__,'debug',"edges: ",json_out(\@edges)); for my $edge ( @edges ) { my $input = $g->get_edge_attribute(@$edge, "input") ; logpkg(__FILE__,__LINE__,'debug',Dumper $edge, Dumper $input); logpkg(__FILE__,__LINE__,'debug', join " ", "JACK client:", $input->client, $input->ports); $g->delete_edge(@$edge); for my $port($input->ports()){ $g->add_edge($port, $edge->[1]); #$g->set_edge_attribute($port, $edge->[1], "input", $input); } } } Audio::Nama::Graph::remove_isolated_vertices($g); } ###### # # remove (or reset) latency operators # generate and connect setup # determine latency # add (or set) operators # (to optimize: add operators only to plural sibling edges, not only edges) sub compensate_latency { my $track = shift; my $delay = shift || 0; my $units = shift; # because of brass_out -> system:playback_1, we # need to advance past brass_out and do # latency compensation on 'brass' instead, # adding in the loop device. my $id = $track->latency_op || add_latency_compensation_op ( $track ); # execute coderef to modify effect, adjusting for units # assume frames by default # but don't convert to frames if $delay is 0 $config->{latency_op_set}->( $id, (! $delay or $units =~ /^s/i) ? $delay : frames_to_secs($delay) ); $id; } sub add_latency_compensation_op { # add the effect, and set the track's latency_op field my $track = shift; my @args = @_; @args or @args = (2,0); my $id = $track->latency_op; # create a delay effect if necessary, place before first effect # if it exists if (! $id){ my $first_effect = $track->ops->[0]; $id = add_effect({ before => $first_effect, track => $track, type => $config->{latency_op}, params => \@args, }); $track->set(latency_op => $id); } $id } sub reset_latency_compensation { map{ compensate_latency($_, 0) } grep{ $_->latency_op } Audio::Nama::audio_tracks(); } { my %reverse = qw(input output output input); sub jack_port_latency { my ($dir, $name) = @_; my $direction; $direction = 'capture' if $dir eq 'input'; $direction = 'playback' if $dir eq 'output'; $direction or confess "$direction: illegal or missing direction"; logpkg(__FILE__,__LINE__,'debug', "name: $name, dir: $dir, direction: $direction"); if ($name !~ /:/) { # we have only the client name, i.e. "system" # pick a port from the ports list logpkg(__FILE__,__LINE__,'debug',"$name is client desriptor, lacks specific port"); # replace with a full port descriptor, i.e. "system:playback_1" # but reverse direction for this: my $node = jack_client($name); $name = $node->{$reverse{$dir}}->[0]; logpkg(__FILE__,__LINE__,'debug', "replacing with $name"); } my ($client, $port) = client_port($name); logpkg(__FILE__,__LINE__,'debug',"name: $name, client: $client, port: $port, dir: $dir, direction: $direction"); my $node = jack_client($client) or Audio::Nama::pager_newline("$name: non existing JACK client"), return; $node->{$port}->{latency}->{$direction}->{min} ne $node->{$port}->{latency}->{$direction}->{max} and Audio::Nama::pager_newline('encountered unmatched latencies', sub{ json_out($node) }); $node->{$port}->{latency}->{$direction}->{min} } } sub latency_param { my $op = shift; my $i = effect_index(type($op)); my $p = 0; for my $param ( @{ $fx_cache->{registry}->[$i]->{params} } ) { $p++; return $p if lc( $param->{name}) eq 'latency' and $param->{dir} eq 'output'; } undef } sub get_live_param { # for effect, not controller # $param is position, starting at one local $config->{category} = 'ECI_FX'; my ($op, $param) = @_; my $FX = fxn($op); my $n = $FX->chain; my $i = $FX->ecasound_effect_index; die "convert these direct IAM calls to cache"; ecasound_iam("c-select $n"); ecasound_iam("cop-select $i"); ecasound_iam("copp-select $param"); ecasound_iam("copp-get") } sub frames_to_secs { # One time conversion for delay op my $frames = shift; $frames / $project->{sample_rate}; } sub start_latency_watcher { $jack->{watcher} ||= jacks::JsClient->new("Nama latency manager", undef, $jacks::JackNullOption, 0); } sub get_latency { my ($pname, $direction) = @_; my %io = ( capture => $jacks::JackCaptureLatency, playback => $jacks::JackPlaybackLatency, ); my $port = $jack->{watcher}->getPort($pname); my $dir = $io{$direction}; die "illegal direction $direction" unless defined $dir; # get latency as Jacks objects my $latency = $port->getLatencyRange($dir); # convert to Nama object $latency = Audio::Nama::Lat->new($latency->min, $latency->max); } sub set_latency { my ($pname, $direction, $min, $max) = @_; my %io = ( capture => $jacks::JackCaptureLatency, playback => $jacks::JackPlaybackLatency, ); my $port = $jack->{watcher}->getPort($pname); my $dir = $io{$direction}; die "illegal direction $direction" unless defined $io{$direction}; $port->setLatencyRange($dir, $min, $max); my $latency = get_latency($pname, $direction); my ($gmin,$gmax) = $latency->values; logpkg(__FILE__,__LINE__,'debug',"set port $pname, $direction latency: $min, $max"); logpkg(__FILE__,__LINE__,'debug', ($min != $gmin and $max != $gmax) ? "Bad: got port $pname, $direction latency: $gmin, $gmax" : "Verified!" ); } sub set_multiport_latency { my ($direction, $min, $max, @pnames) = @_; map{ set_latency($_, $direction,$min, $max) } @pnames; } sub set_playback_latency { my ($min, $max, @pnames) = @_; set_multiport_latency('playback',$min, $max, @pnames) } sub set_capture_latency { my ($min, $max, @pnames) = @_; set_multiport_latency('capture',$min, $max, @pnames) } sub get_capture_latency { get_latency($_[0], 'capture' )} sub get_playback_latency { get_latency($_[0], 'playback')} sub recompute_latencies { $jack->{watcher}->recomputeLatencies(); } 1;Audio-Nama-1.216/lib/Audio/Nama/Edit.pm0000644000175000017500000005760013544212613016413 0ustar jrothjroth{ package Audio::Nama::Edit; use Audio::Nama::Globals qw(:singletons :trackrw); # each edit is identified by: # - host track name # - host track version # - edit name (i.e. sax-v1) used as key in %by_name # use Modern::Perl; our $VERSION = 1.0; use Carp; no warnings qw(uninitialized); our @ISA; use vars qw($n %by_index %by_name ); use Audio::Nama::Object qw( n play_start_mark_name rec_start_mark_name rec_end_mark_name host_track host_version fades ); sub initialize { $n = 0; %by_name = (); %by_index = (); @Audio::Nama::edits_data = (); # for save/restore } sub next_n { ++$n } sub new { my $class = shift; my %vals = @_; croak "undeclared field: @_" if grep{ ! $_is_field{$_} } keys %vals; my $self = bless { n => next_n(), fades => [], @_ }, $class; $by_name{ $self->edit_name } = $self; $by_index{ $self->n } = $self; #print "self class: $class, self type: ", ref $self, $/; my $name = $self->host_track; my $host = $Audio::Nama::tn{$name}; confess( Audio::Nama::project_dir().": missing host_track". $Audio::Nama::this_track->dump. $self->dump. Audio::Nama::nama_cmd("dumpa")) if !$host; # Routing: # # sax-v5-original --+ # version_bus/version_mix host_bus/host # | # sax-v5-edit1 -----+--- sax-v5 (bus/track) --- sax (bus/track) ----- # prepare top-level bus and mix track $host->activate_bus; # i.e. sax (bus/track) # create the version-level bus and mix track # i.e. sax-v5 (bus/track) # (maybe it already exists) my $version_mix = Audio::Nama::Track->new( name => $self->edit_root_name, # i.e. sax-v5 # rw => REC, # set by ->activate_bus source_type => 'bus', source_id => 'bus', width => 2, # default to stereo group => $self->host_track, # i.e. sax hide => 1, ); $version_mix->activate_bus; # create host track alias if necessary # To ensure that users don't get into trouble, we would like to # restrict this track: # - version number must *not* be allowed to change # - rw setting must be fixed to PLAY # # The easiest way may be to subclass the 'set' routine my $host_track_alias = $Audio::Nama::tn{$self->host_alias} // Audio::Nama::VersionTrack->new( name => $self->host_alias, version => $host->playback_version, # static target => $host->name, rw => PLAY, # do not REC group => $self->edit_root_name, # i.e. sax-v5 hide => 1, ); # create edit track # - same name as edit # - we expect to record # - source_type and source_id come from host track my $edit_track = Audio::Nama::EditTrack->new( name => $self->edit_name, rw => REC, source_type => $host->source_type, source_id => $host->source_id, group => $self->edit_root_name, # i.e. sax-v5 hide => 1, ); $self } sub edit_root_name { my $self = shift; join '-', $self->host_track, 'v'.$self->host_version; } sub edit_name { my $self = shift; join '-', $self->edit_root_name, 'edit'.$self->n } sub host_alias { my $self = shift; join '-', $self->edit_root_name, 'original' } # default mark names sub play_start_name { my $self = shift; $self->play_start_mark_name || (join '-', $self->edit_name,'play-start') } sub rec_start_name { my $self = shift; $self->rec_start_mark_name || (join '-', $self->edit_name,'rec-start') } sub rec_end_name { my $self = shift; $self->rec_end_mark_name || (join '-', $self->edit_name,'rec-end') } sub play_start_mark { $Audio::Nama::Mark::by_name{$_[0]->play_start_name} } sub rec_start_mark { $Audio::Nama::Mark::by_name{$_[0]->rec_start_name} } sub rec_end_mark { $Audio::Nama::Mark::by_name{$_[0]->rec_end_name} } # the following are unadjusted values sub play_start_time { my $self = shift; $self->marktime('play_start_name') } sub rec_start_time { my $self = shift; $self->marktime('rec_start_name') } sub rec_end_time { my $self = shift; $self->marktime('rec_end_name') } sub play_end_time { my $self = shift; $self->marktime('rec_end_name') + $config->{edit_playback_end_margin} } sub marktime { my ($self,$markfield) = @_; $Audio::Nama::Mark::by_name{$self->$markfield}->{time} } sub store_fades { # replacing previous my $edit = shift; my @fades = @_; my @indices = map{$_->n} @fades; $edit->remove_fades; $edit->set(fades => \@indices) } sub remove_fades { my $edit = shift; map{ $_->remove } map{ $Audio::Nama::Fade::by_index{$_} } @{$edit->fades}; $edit->set(fades => []); } sub destroy { my $edit = shift; # remove object from index hash delete $by_index{$edit->n}; delete $by_name{$edit->edit_name}; # list edit track WAV files my @wavs = values %{$edit->edit_track->targets}; # track removal also takes care of fades # VERIFY # my $fades = $edit->fades; # map{ $Audio::Nama::Fade::by_index{$_}->remove } @$fades; # remove edit track $edit->edit_track->remove; my @sister_edits = grep{ $edit->host_track eq $_->host_track and $edit->host_version == $_->host_version } values %by_index; # if we are the last edit, remove all auxiliary tracks/buses if ( ! @sister_edits ){ $edit->host_alias_track->remove; $edit->version_bus->remove; # note: bus->remove will not delete a mix track with WAV files # The host may have a version symlinked to a WAV file # belonging to the version mix track. So we remove # the track, but not the wav files. $edit->version_mix->remove if defined $edit->version_mix; $edit->host_bus->remove; } # remove edit track WAV files if we've reached here map{ my $path = Audio::Nama::join_path(Audio::Nama::this_wav_dir(), $_); Audio::Nama::pager("removing $path"); #unlink $path; } @wavs; } sub host { $Audio::Nama::tn{$_[0]->host_track} } # top-level mix track, i.e. 'sax' sub host_bus { $Audio::Nama::Bus::by_name{$_[0]->host_track} } # top-level bus sub version_mix { $Audio::Nama::tn{$_[0]->edit_root_name} } # in top-level bus sub version_bus { $Audio::Nama::Bus::by_name{$_[0]->edit_root_name} } # version-level bus sub host_alias_track{ $Audio::Nama::tn{$_[0]->host_alias} } # in version_bus sub edit_track { $Audio::Nama::tn{$_[0]->edit_name} } # in version_bus # utility routines } # -------- Edit routines; Main Namespace ------ { package Audio::Nama; use Modern::Perl; use Carp; no warnings 'uninitialized'; our ( %tn, %ti, %bn, $this_track, $this_edit, ); sub detect_keystroke_p { $project->{events}->{stdin} = AE::io(*STDIN, 0, sub { &{$text->{term_attribs}->{'callback_read_char'}}(); abort_set_edit_points(), return if $text->{term_attribs}->{line_buffer} eq "q" or $text->{term_attribs}->{line_buffer} eq "Q"; if ( $text->{term_attribs}->{line_buffer} eq "p" or $text->{term_attribs}->{line_buffer} eq "P"){ get_edit_mark()} else{ reset_input_line() } }); } sub reset_input_line { $text->{term_attribs}->{line_buffer} = q(); $text->{term_attribs}->{point} = 0; $text->{term_attribs}->{end} = 0; } { my $p; my @_edit_points; my @names = qw(dummy play-start rec-start rec-end); sub initialize_edit_points { $p = 0; @_edit_points = (); } sub abort_set_edit_points { Audio::Nama::throw("...Aborting!"); reset_input_line(); ecasound_iam('stop'); initialize_edit_points(); detect_spacebar(); } sub get_edit_mark { $p++; if($p <= 3){ # record mark my $pos = ecasound_iam('getpos'); push @_edit_points, $pos; Audio::Nama::pager(" got $names[$p] position ".d1($pos)); reset_input_line(); if( $p == 3){ complete_edit_points() } else{ $text->{term}->stuff_char(10); &{$text->{term_attribs}->{'callback_read_char'}}(); } } } sub complete_edit_points { @{$setup->{edit_points}} = @_edit_points; # save to global ecasound_iam('stop'); Audio::Nama::pager("\nEngine is stopped\n"); detect_spacebar(); print prompt(), " "; } } sub set_edit_points { $tn{$this_edit->edit_name}->set(rw => OFF) if defined $this_edit; Audio::Nama::throw("You must use a playback-only mode to setup edit marks. Aborting"), return 1 if Audio::Nama::ChainSetup::really_recording(); Audio::Nama::throw("You need stop the engine first. Aborting"), return 1 if $this_engine->started(); Audio::Nama::pager("Ready to set edit points!"); sleeper(0.2); Audio::Nama::pager(q(Press the "P" key three times to mark positions for: + play-start + record-start + record-end Press "Q" to quit. Engine will start in 2 seconds.)); initialize_edit_points(); $project->{events}->{set_edit_points} = AE::timer(2, 0, sub { reset_input_line(); detect_keystroke_p(); ecasound_iam('start'); Audio::Nama::pager("\n\nEngine is running\n"); print prompt(); }); } sub transfer_edit_points { Audio::Nama::throw("Use 'set_edit_points' command to specify edit region"), return unless scalar @{$setup->{edit_points}}; my $edit = shift; Audio::Nama::Mark->new( name => $edit->play_start_name, time => $setup->{edit_points}->[0]); Audio::Nama::Mark->new( name => $edit->rec_start_name, time => $setup->{edit_points}->[1]); Audio::Nama::Mark->new( name => $edit->rec_end_name, time => $setup->{edit_points}->[2]); @{$setup->{edit_points}} = (); } sub generate_edit_record_setup { # for current edit # set edit track to REC # set global region start offset # set global region length cutoff # set regenerate_setup flag # insert host track fades # mute edit track # schedule unmuting at rec-start point - fade-in # schedule muting at rec-end point - fade-out } sub new_edit { # abort for many different reasons Audio::Nama::throw("You must use 'set_edit_points' before creating a new edit. Aborting."), return unless @{$setup->{edit_points}}; my $overlap = grep { my $fail; my $rst = $_->rec_start_time; my $ret = $_->rec_end_time; my $nst = $setup->{edit_points}->[1]; my $net = $setup->{edit_points}->[2]; my $rst1 = d1($rst); my $ret1 = d1($ret); my $nst1 = d1($nst); my $net1 = d1($net); Audio::Nama::throw("New rec-start time $nst1 conflicts with Edit ", $_->n, ": $rst1 < $nst1 < $ret1"), $fail++ if $rst < $nst and $nst < $ret; Audio::Nama::throw("New rec-end time $net1 conflicts with Edit ", $_->n, ": $rst1 < $net1 < $ret1"), $fail++ if $rst < $net and $net < $ret; Audio::Nama::throw("New rec interval $nst1 - $net1 conflicts with Edit ", $_->n, ": $rst1 - $ret1"), $fail++ if $nst < $rst and $ret < $net; $fail } grep{ $_->host_track eq $this_track->name} values %Audio::Nama::Edit::by_name; Audio::Nama::throw("Aborting."), return if $overlap; my $name = $this_track->name; my $editre = qr($name-v\d+-edit\d+); Audio::Nama::throw("$name: editing of edits is not currently allowed."), return if $name =~ /-v\d+-edit\d+/; Audio::Nama::throw("$name: must be in PLAY mode. Edits will be applied against current version"), return unless $this_track->play or $this_track->rec and grep{ /$editre/ } keys %Audio::Nama::Track::by_name; # create edit my $v = $this_track->playback_version; Audio::Nama::pager("$name: creating new edit against version $v"); my $edit = Audio::Nama::Edit->new( host_track => $this_track->name, host_version => $v, ); $this_track->current_edit->{$v} = $edit->n; $this_edit = $edit; transfer_edit_points($edit); #select_edit($this_edit->n); edit_action('preview_edit_in'); } {my %edit_actions = ( record_edit => sub { $this_edit->edit_track->set(rw => REC); $this_edit->store_fades(std_host_fades(), edit_fades()); }, play_edit => sub { $this_edit->edit_track->set(rw => PLAY); $this_edit->store_fades(std_host_fades(), edit_fades()); }, preview_edit_in => sub { $this_edit->edit_track->set(rw => OFF); $this_edit->store_fades(std_host_fades()); }, preview_edit_out => sub { $this_edit->edit_track->set(rw => OFF); $this_edit->store_fades(reverse_host_fades()); }, ); sub edit_action { my $action = shift; defined $this_edit or Audio::Nama::throw("Please select an edit and try again."), return; set_edit_mode(); $this_edit->host_alias_track->set(rw => PLAY); # all $edit_actions{$action}->(); request_setup(); # TODO: looping # my $is_setup = generate_setup(); # return unless $is_setup; # if ($action !~ /record/){ # $mode->{loop_enable}++; # @{$setup->{loop_endpoints}} = (0,$setup->{audio_length} - 0.05); # # and transport_start() # } # connect_transport(); } } sub end_edit_mode { # regenerate fades $mode->{offset_run} = 0; $mode->{loop_enable} = 0; disable_offset_run_mode(); $this_track = $this_edit->host if defined $this_edit; undef $this_edit; request_setup(); } sub destroy_edit { Audio::Nama::throw("no edit selected"), return unless $this_edit; my $reply = $text->{term}->readline('destroy edit "'.$this_edit->edit_name. qq(" and all its WAV files?? [n] )); if ( $reply =~ /y/i ){ Audio::Nama::pager("permanently removing edit"); $this_edit->destroy; } $text->{term}->remove_history($text->{term}->where_history); $this_track = $this_edit->host; end_edit_mode(); } sub set_edit_mode { $mode->{offset_run} = edit_mode_conditions() ? 1 : 0 } sub edit_mode { $mode->{offset_run} and defined $this_edit} sub edit_mode_conditions { defined $this_edit or Audio::Nama::throw('No edit is defined'), return; defined $this_edit->play_start_time or Audio::Nama::throw('No edit points defined'), return; $this_edit->host_alias_track->play or Audio::Nama::throw('host alias track : ',$this_edit->host_alias, " status must be PLAY"), return; # the following conditions should never be triggered $this_edit->host_alias_track->playback_version == $this_edit->host_version or die('host alias track: ',$this_edit->host_alias, " must be set to version ",$this_edit->host_version), return 1; } sub reverse_host_fades { host_fades('in','out') } sub std_host_fades { host_fades('out','in') } sub host_fades { my ($first,$second) = @_; Audio::Nama::Fade->new( type => $first, mark1 => $this_edit->rec_start_name, duration => $config->{edit_crossfade_time}, relation => 'fade_from_mark', track => $this_edit->host_alias, ), Audio::Nama::Fade->new( type => $second, mark1 => $this_edit->rec_end_name, duration => $config->{edit_crossfade_time}, relation => 'fade_from_mark', track => $this_edit->host_alias, ), } sub edit_fades { Audio::Nama::Fade->new( type => 'in', mark1 => $this_edit->rec_start_name, duration => $config->{edit_crossfade_time}, relation => 'fade_from_mark', track => $this_edit->edit_name, ), Audio::Nama::Fade->new( type => 'out', mark1 => $this_edit->rec_end_name, duration => $config->{edit_crossfade_time}, relation => 'fade_from_mark', track => $this_edit->edit_name, ); } ### edit region computations # pass $args hash with following fields: # ### track values # trackname # playat # region_start # region_end # setup_length # ### edit values # edit_play_start # edit_play_end # ### dispatch tables # playat_dispatch # region_start_dispatch # region_end_dispatch # ### output values # # new_playat # new_region_start # new_region_end sub region_start_dispatch { my ($args, $key) = @_; my %table = ( out_of_bounds_near => "*", out_of_bounds_far => "*", play_start_during_playat_delay => $args->{region_start}, no_region_play_start_during_playat_delay => 0, play_start_within_region => $args->{region_start} + $args->{edit_play_start} - $args->{playat}, no_region_play_start_after_playat_delay => $args->{region_start} + $args->{edit_play_start} - $args->{playat}, ); $table{$key} } sub playat_dispatch { my ($args, $key) = @_; my %table = ( out_of_bounds_near => "*", out_of_bounds_far => "*", play_start_during_playat_delay => $args->{playat} - $args->{edit_play_start}, no_region_play_start_during_playat_delay => $args->{playat} - $args->{edit_play_start}, play_start_within_region => 0, no_region_play_start_after_playat_delay => 0, ); $table{$key} } sub region_end_dispatch { my ($args, $key) = @_; my %table = ( out_of_bounds_near => "*", out_of_bounds_far => "*", play_start_during_playat_delay => $args->{region_start} + $args->{edit_play_end} - $args->{playat}, no_region_play_start_during_playat_delay => $args->{edit_play_end} - $args->{playat}, play_start_within_region => $args->{region_start} + $args->{edit_play_end} - $args->{playat}, no_region_play_start_after_playat_delay => $args->{edit_play_end} - $args->{playat}, ); $table{$key} } sub new_playat { my $args = shift; playat_dispatch($args, edit_case($args)); } sub new_region_start { my $args = shift; region_start_dispatch($args, edit_case($args)); } sub new_region_end { my $args = shift; my $end = region_end_dispatch($args, edit_case($args)); return $end if $end eq '*'; $end < $args->{setup_length} ? $end : $args->{setup_length} }; # the following value will always allow enough time # to record the edit. it may be longer than the # actual WAV file in some cases. (I doubt that # will be a problem.) sub edit_case { my $args = shift; # logic for no-region case if ( ! $args->{region_start} and ! $args->{region_end} ) { if( $args->{edit_play_end} < $args->{playat}) { "out_of_bounds_near" } elsif( $args->{edit_play_start} > $args->{playat} + $args->{setup_length}) { "out_of_bounds_far" } elsif( $args->{edit_play_start} >= $args->{playat}) {"no_region_play_start_after_playat_delay"} elsif( $args->{edit_play_start} < $args->{playat} and $args->{edit_play_end} > $args->{playat} ) { "no_region_play_start_during_playat_delay"} } # logic for region present case elsif ( defined $args->{region_start} and defined $args->{region_end} ) { if ( $args->{edit_play_end} < $args->{playat}) { "out_of_bounds_near" } elsif ( $args->{edit_play_start} > $args->{playat} + $args->{region_end} - $args->{region_start}) { "out_of_bounds_far" } elsif ( $args->{edit_play_start} >= $args->{playat}) { "play_start_within_region"} elsif ( $args->{edit_play_start} < $args->{playat} and $args->{playat} < $args->{edit_play_end}) { "play_start_during_playat_delay"} else {carp "$args->{trackname}: fell through if-then"} } else { carp "$args->{trackname}: improperly defined region" } } sub play_start_time { defined $this_edit ? $this_edit->play_start_time : $setup->{offset_run}->{start_time} # zero unless offset run mode } sub play_end_time { defined $this_edit ? $this_edit->play_end_time : $setup->{offset_run}->{end_time} # undef unless offset run mode } sub edit_vars { my $edit = shift || $this_edit; Audio::Nama::throw("edit is undefined"), return unless $edit; my $track = $Audio::Nama::tn{$edit}->{host_track}; { trackname => $track->name, playat => $track->playat_time, region_start => $track->region_start_time, region_end => $track->region_end_time, edit_play_start => $edit->play_start_time(), edit_play_end => $edit->play_end_time(), setup_length => $track->wav_length(), } } sub list_edits { my @edit_data = map{ s/^---//; s/...\s$//; $_ } map{ $_->dump } sort{$a->n <=> $b->n} values %Audio::Nama::Edit::by_index; Audio::Nama::pager(@edit_data); } sub explode_track { my $track = shift; # quit if I am already a mix track Audio::Nama::throw($track->name,": I am already a mix track. I cannot explode!"),return if $track->is_mixing; # XX should not be allowed to explode if track is set to PLAY my @versions = @{ $track->versions }; # quit if I have only one version Audio::Nama::throw($track->name,": Only one version. Skipping."), return if scalar @versions == 1; $track->activate_bus; my $host = $track->name; my @names = map{ "$host-v$_"} @versions; my @exists = grep{ $Audio::Nama::tn{$_} } @names; Audio::Nama::throw("@exists: tracks already exist. Aborting."), return if @exists; my $current = cwd; chdir this_wav_dir(); for my $i (@versions){ # make a track my $name = "$host-v$i"; Audio::Nama::Track->new( name => $name, rw => MON, group => $host, ); # symlink the WAV file we want symlink $track->targets->{$i}, "$name.wav"; } chdir $current; } sub select_edit { my $n = shift; my ($edit) = grep{ $_->n == $n } values %Audio::Nama::Edit::by_name; # check that conditions are met Audio::Nama::throw("Edit $n not found. Skipping."),return if ! $edit; Audio::Nama::throw( qq(Edit $n applies to track "), $edit->host_track, qq(" version ), $edit->host_version, ". This does does not match the current monitor version (", $edit->host->playback_version,"). Set the correct version and try again."), return if $edit->host->playback_version != $edit->host_version; # select edit $this_edit = $edit; # turn on top-level bus and mix track $edit->host->activate_bus; # turn off all version level buses/mix_tracks map{ $tn{$_}->set(rw => OFF); # version mix tracks } $this_edit->host_bus->tracks; # use same name for track/bus # turn on what we want $edit->version_mix->activate_bus; $edit->host_alias_track->set(rw => PLAY); $edit->edit_track->set(rw => PLAY); $this_track = $edit->host; } sub disable_edits { Audio::Nama::throw("Please select an edit and try again."), return unless defined $this_edit; my $edit = $this_edit; $edit->host->set(rw => OFF); # XXX or what it was before?? $edit->version_mix->set( rw => OFF); } sub merge_edits { my $edit = $this_edit; Audio::Nama::throw("Please select an edit and try again."), return unless defined $edit; Audio::Nama::throw($edit->host_alias, ": track must be PLAY status. Aborting."), return unless $edit->host_alias_track->play; Audio::Nama::throw("Use exit_edit_mode and try again."), return if edit_mode(); # create merge message my $v = $edit->host_version; my %edits = map{ my ($edit) = $tn{$_}->name =~ /edit(\d+)$/; my $ver = $tn{$_}->playback_version; $edit => $ver } grep{ $tn{$_}->name =~ /edit\d+$/ and $tn{$_}->play} $edit->version_bus->tracks; my $msg = "merges ".$edit->host_track."_$v.wav w/edits ". join " ",map{$_."v$edits{$_}"} sort{$a<=>$b} keys %edits; # merges mic_1.wav w/mic-v1-edits 1_2 2_1 Audio::Nama::pager($msg); # cache at version_mix level my $output_wav = cache_track($edit->version_mix); # promote to host track my $new_version = $edit->host->last + 1; add_system_version_comment($edit->host, $new_version, $msg); add_system_version_comment($edit->version_mix, $edit->version_mix->last, $msg); my $old = cwd(); chdir this_wav_dir(); my $new_host_wav = $edit->host_track . "_" . $new_version . ".wav"; symlink $output_wav, $new_host_wav; $edit->host->set(version => undef); # default to latest $edit->host->{version_comment}{$new_version}{system} = $msg; chdir $old; disable_edits(); $this_track = $edit->host; } # offset recording # Note that although we use ->shifted_* methods, all are # executed outside of edit mode, so we get unadjusted values. sub setup_length { my $setup_length; map{ my $l = $_->shifted_length; $setup_length = $l if $l > $setup_length } grep{ $_-> play } Audio::Nama::ChainSetup::engine_tracks(); $setup_length } sub set_offset_run_mark { Audio::Nama::throw("This function not available in edit mode. Aborting."), return if edit_mode(); my $markname = shift; $setup->{offset_run}->{start_time} = $Audio::Nama::Mark::by_name{$markname}->time; $setup->{offset_run}->{end_time} = setup_length(); $setup->{offset_run}->{mark} = $markname; enable_offset_run_mode(); request_setup(); } sub clear_offset_run_vars { $setup->{offset_run}->{start_time} = 0; $setup->{offset_run}->{end_time} = undef; $setup->{offset_run}->{mark} = undef; } sub enable_offset_run_mode { undef $this_edit; $mode->{offset_run}++ } sub disable_offset_run_mode { undef $mode->{offset_run}; clear_offset_run_vars(); Audio::Nama::request_setup(); } sub is_offset_run_mode { $mode->{offset_run} and ! defined $this_edit } sub select_edit_track { my $track_selector_method = shift; Audio::Nama::throw("You need to select an edit first (list_edits, select_edit)\n"), return unless defined $this_edit; $this_track = $this_edit->$track_selector_method; nama_cmd('show_track'); } } # end package 1; __END__Audio-Nama-1.216/lib/Audio/Nama/Log.pm0000644000175000017500000001151113544212613016236 0ustar jrothjroth# ----------- Logging ------------ package Audio::Nama::Log; use Modern::Perl; use Log::Log4perl qw(get_logger :levels); use Exporter; use Carp qw(carp cluck confess croak); our @ISA = 'Exporter'; our @EXPORT_OK = qw(logit loggit logpkg logsub initialize_logger); our $appender; sub initialize_logger { my $cat_string = shift; my @all_cats = qw( Audio::Nama::AnalyseLV2 Audio::Nama::Assign Audio::Nama::Bunch Audio::Nama::Bus Audio::Nama::BusUtil Audio::Nama::CacheTrack Audio::Nama::ChainSetup Audio::Nama::Config Audio::Nama::Custom Audio::Nama::EcasoundCleanup Audio::Nama::EcasoundRun Audio::Nama::EcasoundSetup Audio::Nama::Edit Audio::Nama::EffectChain Audio::Nama::EffectNickname Audio::Nama::Effect Audio::Nama::EffectsRegistry Audio::Nama::Engine Audio::Nama::EngineSetup Audio::Nama::Fade Audio::Nama::Git Audio::Nama::Globals Audio::Nama::Grammar Audio::Nama::Graphical Audio::Nama::Graph Audio::Nama::Help Audio::Nama::Initializations Audio::Nama::Insert Audio::Nama::IO Audio::Nama::Jack Audio::Nama::Latency Audio::Nama::Lat Audio::Nama::Log Audio::Nama::Mark Audio::Nama::Memoize Audio::Nama::Midi Audio::Nama::Mix Audio::Nama::Modes Audio::Nama::MuteSoloFade Audio::Nama::Nama Audio::Nama::Object Audio::Nama::Options Audio::Nama::Persistence Audio::Nama::Project Audio::Nama::Regions Audio::Nama::Sequence Audio::Nama::StatusSnapshot Audio::Nama::Terminal Audio::Nama::Text Audio::Nama::TrackComment Audio::Nama::TrackEffect Audio::Nama::TrackIO Audio::Nama::TrackLatency Audio::Nama::Track Audio::Nama::TrackRegion Audio::Nama::TrackUtils Audio::Nama::TrackWaveform Audio::Nama::Util Audio::Nama::Waveform Audio::Nama::Wavinfo Audio::Nama::WavModify Audio::Nama::Wav ); push @all_cats, 'ECI','SUB'; my %negate; %negate = map{ $_ => 1} map{ s/^#//; $_ } grep{ /^#/ } expand_cats(split q(,), $cat_string) if $cat_string; #say("negate\n",Audio::Nama::json_out(\%negate)); my $layout = "[\%r] %c %m%n"; # backslash to protect from source filter my $logfile = $ENV{NAMA_LOGFILE}; $SIG{ __DIE__ } = sub { Carp::confess( @_ ) } if $cat_string; $appender = $logfile ? 'FILE' : 'STDERR'; $logfile //= "/dev/null"; my @cats; @cats = expand_cats(split ',', $cat_string) if $cat_string; #logpkg(__FILE__,__LINE__,'debug',"initial logging categories: @cats"); #logpkg(__FILE__,__LINE__,'trace',"all cats: @all_cats"); @cats = grep{ ! $negate{$_} } @all_cats if grep {$_ eq 'ALL'} @cats; #logpkg(__FILE__,__LINE__,'debug',"Final logging categories: @cats"); my $conf = qq( #log4perl.rootLogger = DEBUG, $appender #log4perl.category.Audio.Nama = DEBUG, $appender # dummy entry - avoid no logger/no appender warnings log4perl.category.DUMMY = DEBUG, DUMMY log4perl.appender.DUMMY = Log::Log4perl::Appender::Screen log4perl.appender.DUMMY.layout = Log::Log4perl::Layout::NoopLayout # screen appender log4perl.appender.STDERR = Log::Log4perl::Appender::Screen log4perl.appender.STDERR.layout = Log::Log4perl::Layout::PatternLayout log4perl.appender.STDERR.layout.ConversionPattern = $layout # file appender log4perl.appender.FILE = Log::Log4perl::Appender::File log4perl.appender.FILE.filename = $logfile log4perl.appender.FILE.layout = Log::Log4perl::Layout::PatternLayout log4perl.appender.FILE.layout.ConversionPattern = $layout #log4perl.additivity.SUB = 0 # doesn't work... why? ); # add lines for the categories we want to log $conf .= join "\n", "", map{ cat_line($_)} @cats if @cats; #say $conf; Log::Log4perl::init(\$conf); return( { map { $_, 1 } @cats } ) } sub cat_line { "log4perl.category.$_[0] = DEBUG, $appender" } sub expand_cats { # Convert Module -> Audio::Nama::Module -> Audio::Nama::Module # Convert !Module -> !Audio::Nama::Module -> !Audio::Nama::Module no warnings 'uninitialized'; my @cats = @_; map { s/^(#)?::/$1Audio::Nama::/; $_} # SKIP_PREPROC map { s/^(#)?/$1::/ unless /^::/ or /^#?ECI/ or /^#?SUB/ or /^ALL$/; $_ }# SKIP_PREPROC @cats; } { my %is_method = map { $_ => 1 } qw( trace debug info warn error fatal logwarn logdie logcarp logcroak logcluck logconfess); sub logit { my ($line_number, $category, $level, @message) = @_; #say qq($line_number, $category, $level, @message) ; #confess("first call to logit"); my $line_number_output = $line_number ? " (L $line_number) ": ""; cluck "illegal level: $level" unless $is_method{$level}; my $logger = get_logger($category); $logger->$level($line_number_output, @message); } } sub logsub { logit(__LINE__,'SUB','debug',$_[0]) } *loggit = \&logit; # to avoid source filter on logit call below sub logpkg { my( $file, $line_no, $level, @message) = @_; # convert Effects.pm to Audio::Nama::Effects to support logpkg my $pkg = $file; ($pkg) = $file =~ m| ([^/]+)\.pm$ |x; $pkg //= "Dummy::Pkg"; $pkg = "Audio::Nama::$pkg"; # SKIP_PREPROC #say "category: $pkg"; logit ($line_no,$pkg,$level, @message) } 1;Audio-Nama-1.216/lib/Audio/Nama/Wav.pm0000644000175000017500000000675613544212613016271 0ustar jrothjrothpackage Audio::Nama::Wav; our $VERSION = 1.001; use Audio::Nama::Globals qw(:all); use Audio::Nama::Util qw(:all); use Audio::Nama::Assign qw(:all); use Audio::Nama::Util qw(join_path); use Audio::Nama::Log qw(logsub logpkg); use Memoize qw(memoize unmemoize); # called by code in Audio::Nama::Memoize.pm use warnings; no warnings qw(uninitialized); use Carp; use Role::Tiny; sub wav_length { my $track = shift; Audio::Nama::wav_length($track->full_path) } sub wav_format{ my $track = shift; Audio::Nama::wav_format($track->full_path) } sub dir { my $self = shift; $self->project ? join_path(Audio::Nama::project_root(), $self->project, '.wav') : Audio::Nama::this_wav_dir(); } sub basename { my $self = shift; $self->target || $self->name } sub full_path { my $track = shift; join_path($track->dir, $track->current_wav) } sub group_last { my $track = shift; my $bus = $bn{$track->group}; $bus->last; } sub last { $_[0]->versions->[-1] || 0 } sub current_wav { my $track = shift; my $last = $track->current_version; if ($track->rec){ $track->name . '_' . $last . '.wav' } elsif ( $track->rw eq PLAY){ my $filename = $track->targets->{ $track->playback_version } ; $filename } else { logpkg(__FILE__,__LINE__,'debug', "track ", $track->name, ": no current version") ; undef; } } sub current_version { my $track = shift; # two possible version numbers, depending on REC/PLAY status if ($track->rec) { my $last = $config->{use_group_numbering} ? Audio::Nama::Bus::overall_last() : $track->last; return ++$last } elsif ($track->play){ return $track->playback_version } else { return 0 } } sub playback_version { my $track = shift; return $track->version if $track->version and grep {$track->version == $_ } @{$track->versions} ; $track->last; } sub targets { # WAV file targets, distinct from 'target' attribute my $self = shift; _targets(dir => $self->dir, name => $self->basename) } sub versions { my $self = shift; _versions(dir => $self->dir, name => $self->basename) } sub get_versions { my %args = @_; $args{sep} //= '_'; $args{ext} //= 'wav'; my ($sep, $ext) = ($args{sep}, $args{ext}); my ($dir, $basename) = ($args{dir}, $args{name}); logpkg(__FILE__,__LINE__,'debug',"getver: dir $dir basename $basename sep $sep ext $ext"); my %versions = (); for my $candidate ( candidates($dir) ) { # logpkg(__FILE__,__LINE__,'debug',"candidate: $candidate"); my( $match, $dummy, $num) = ( $candidate =~ m/^ ( $basename ($sep (\d+))? \.$ext ) $/x ); # regex statement if ( $match ) { $versions{ $num || 'bare' } = $match } } logpkg(__FILE__,__LINE__,'debug',sub{"get_version: " , Audio::Nama::json_out(\%versions)}); %versions; } sub candidates { my $dir = shift; $dir = File::Spec::Link->resolve_all( $dir ); opendir my $wavdir, $dir or die "cannot open $dir: $!"; my @candidates = readdir $wavdir; closedir $wavdir; @candidates = grep{ ! (-s join_path($dir, $_) == 44 ) } @candidates; #logpkg(__FILE__,__LINE__,'debug',join $/, @candidates); @candidates; } sub _targets { my %args = @_; # $Audio::Nama::debug2 and print "&targets\n"; my %versions = get_versions(%args); if ($versions{bare}) { $versions{1} = $versions{bare}; delete $versions{bare}; } logpkg(__FILE__,__LINE__,'debug',sub{"\%versions\n================\n", json_out(\%versions)}); \%versions; } sub _versions { # $Audio::Nama::debug2 and print "&versions\n"; my %args = @_; [ sort { $a <=> $b } keys %{ _targets(%args)} ] } 1;Audio-Nama-1.216/lib/Audio/Nama/Engine.pm0000644000175000017500000002322013544212613016722 0ustar jrothjroth{ package Audio::Nama::Engine; our $VERSION = 1.0; use Modern::Perl; use Carp; our @ISA; our %by_name; our @ports = (57000..57050); our %port = ( fof => 57201, bus => 57202, ); use Audio::Nama::Globals qw(:all); use Role::Tiny::With; with 'Audio::Nama::EcasoundSetup'; use Audio::Nama::Object qw( name port jack_seek_delay jack_transport_mode events socket pids ecasound buffersize ready ); sub new { my $class = shift; my %vals = @_; croak "undeclared field: @_" if grep{ ! $_is_field{$_} } keys %vals; Audio::Nama::pager_newline("$vals{name}: returning existing engine"), return $by_name{$vals{name}} if $by_name{$vals{name}}; my $self = bless { name => 'default', %vals }, $class; #print "object class: $class, object type: ", ref $self, $/; $by_name{ $self->name } = $self; $self->initialize_ecasound(); $this_engine = $self; } sub initialize_ecasound { my $self = shift; my @existing_pids = split " ", qx(pgrep ecasound); $self->launch_ecasound_server; $self->{pids} = [ grep{ my $pid = $_; ! grep{ $pid == $_ } @existing_pids } split " ", qx(pgrep ecasound) ]; } sub launch_ecasound_server {} sub kill_and_reap { my $self = shift; Audio::Nama::kill_and_reap( @{$self->{pids}} ); } sub tracks { my $self = shift; my @tracks = grep { $self->name eq $_->engine_group } Audio::Nama::all_tracks(); } sub ecasound_iam {} # the purpose of the following methods is to cache results # from the engine, so we don't burden it with extra # commands while the engine is running. #sub started { $_[0]->{started} } # cached sub started { $_[0]->running } # not cached sub stopped { ! $_[0]->started } # cached sub running { no warnings 'uninitialized'; $_[0]->ecasound_iam("engine-status") eq 'running' } sub current_item { my ($self, $n, $field, $cmd, $reset_sub) = @_; no warnings 'uninitialized'; logpkg(__FILE__,__LINE__,'debug',"field: $field, n: $n, was: $self->{field} cmd: $cmd, reset sub: ", $reset_sub ? "yes" : "no"); # caching behavior: # do not execute if newly assigned value same as stored value return $self->{$field} if ! $n or $n > 0 and $self->{$field} == $n; # otherwise execute command and cache new value $self->ecasound_iam("$cmd $n"); &$reset_sub if $reset_sub; $self->{$field} = $n; } sub current_chain { my ($self, $n) = @_; $self->current_item($n, 'current_chain', 'c-select', \&reset_ecasound_selections_cache); } sub reset_ecasound_selections_cache { my $self = shift; delete $self->{$_} for qw( current_chain current_chain_operator current_chain_operator_parameter current_controller current_controller_parameter); } sub reset_current_controller { my $self = shift; delete $self->{$_} for qw(current_controller current_controller_parameter) } sub current_chain_operator { my ($self, $n) = @_; $self->current_item($n, 'current_chain_operator', 'cop-select', \&reset_ecasound_selections_cache) } sub current_chain_operator_parameter { my ($self, $n) = @_; $self->current_item($n, 'current_chain_operator_parameter', 'copp-select', \&reset_current_controller); } sub current_controller { my ($self, $n) = @_; $self->current_item($n, 'current_controller', 'ctrl-select', \&reset_current_controller); } sub current_controller_parameter { my ($self, $n) = @_; $self->current_item($n, 'current_controller_parameter', 'ctrlp-select'); } sub valid_setup { my ($self) = @_; $self->ecasound_iam('cs-is-valid'); } ### class methods sub engines { values %by_name } sub sync_action { my ($method, @args) = @_; $_->$method(@args) for engines() } } { package Audio::Nama::NetEngine; our $VERSION = 1.0; use Modern::Perl; use Audio::Nama::Log qw(logpkg logit); use Audio::Nama::Globals qw(:all); use Carp qw(carp); use Role::Tiny::With; with 'Audio::Nama::EcasoundRun'; with 'Audio::Nama::EcasoundCleanup'; our @ISA = 'Audio::Nama::Engine'; sub init_ecasound_socket { my $self = shift; my $port = $self->port; Audio::Nama::pager_newline("Creating socket on port $port."); $self->{socket} = new IO::Socket::INET ( PeerAddr => 'localhost', PeerPort => $port, Proto => 'tcp', ); die "Could not create socket: $!\n" unless $self->{socket}; } sub launch_ecasound_server { my $self = shift; my $port = $self->port; # we'll try to communicate with an existing ecasound # process provided: # # started with --server option # --server-tcp-port option matches my $command = "ecasound -K -C --server --server-tcp-port=$port"; my $redirect = ">/dev/null &"; my $ps = qx(ps ax); if ( $ps =~ /ecasound/ and $ps =~ /--server/ and ($ps =~ /tcp-port=$port/) ) { Audio::Nama::pager_newline("Found existing Ecasound server on port $port") } else { Audio::Nama::pager_newline("Starting Ecasound server on port $port"); system("$command $redirect") == 0 or carp("system $command failed: $?\n") } sleep 1; $self->init_ecasound_socket(); } sub ecasound_iam{ my $self = shift; my $cmd = shift; #my $category = Audio::Nama::munge_category(shift()); my $category = "ECI"; logit(__LINE__,$category, 'debug', "Net-ECI sent: $cmd"); $cmd =~ s/\s*$//s; # remove trailing white space $en{$Audio::Nama::config->{ecasound_engine_name}}->{socket}->send("$cmd\r\n"); my $buf; # get socket reply, restart ecasound on error my $result = $en{$Audio::Nama::config->{ecasound_engine_name}}->{socket}->recv($buf, $config->{engine_command_output_buffer_size}); defined $result or Audio::Nama::throw("Ecasound failed to respond"), return; my ($return_value, $setup_length, $type, $reply) = $buf =~ /(\d+)# digits, log_level \ # space (\d+)# digits, msg_size \ # space ([^\r\n]+) # string, return_type \r\n # newline (.+) # rest of string, message /sx; # s-flag: . matches newline if( ! $return_value == 256 ){ logit(__LINE__,$category,'error',"Net-ECI bad return value: $return_value (expected 256)"); } no warnings 'uninitialized'; $reply =~ s/\s+$//; if( $type eq 'e') { logit(__LINE__,$category,'error',"ECI error! Command: $cmd. Reply: $reply"); } else { logit(__LINE__,$category,'debug',"Net-ECI got: $reply"); $reply } } sub configure { package Audio::Nama; my $self = shift; my $force = shift; # don't disturb recording/mixing return if Audio::Nama::ChainSetup::really_recording() and $this_engine->started(); # store a lists of wav-recording tracks for the rerecord # function restart_wav_memoize(); # check if someone has snuck in some files find_duplicate_inputs(); # we will warn the user later if( $force or $setup->{changed} ){ logpkg(__FILE__,__LINE__,'debug',"reconfigure requested"); $setup->{_old_snapshot} = status_snapshot_string(); } else { my $old = $setup->{_old_snapshot}; my $current = $setup->{_old_snapshot} = status_snapshot_string(); if ( $current eq $old){ logpkg(__FILE__,__LINE__,'debug',"no change in setup"); return; } logpkg(__FILE__,__LINE__,'debug',"detected configuration change"); logpkg(__FILE__,__LINE__,'debug', diff(\$old, \$current)); } $setup->{changed} = 0 ; # reset for next time nama_cmd('show_tracks'); { local $quiet = 1; stop_transport() } trigger_rec_cleanup_hooks(); trigger_rec_setup_hooks(); $setup->{_old_rec_status} = { map{$_->name => $_->rec_status } rec_hookable_tracks() }; if ( $self->setup() ){ $self->{valid_setup} = 1; reset_latency_compensation() if $config->{opts}->{Q}; logpkg(__FILE__,__LINE__,'debug',"I generated a new setup"); { local $quiet = 1; connect_transport() } propagate_latency() if $config->{opts}->{Q} and $jack->{jackd_running}; show_status(); if ( Audio::Nama::ChainSetup::really_recording() ) { $project->{playback_position} = 0 } else { set_position($project->{playback_position}) if $project->{playback_position} } $self->start_transport('quiet') if $mode->eager and ($mode->doodle or $mode->preview); transport_status(); $ui->flash_ready; 1 } } } # end package { package Audio::Nama::LibEngine; our $VERSION = 1.0; use Modern::Perl; use Audio::Nama::Globals qw(:all); use Audio::Nama::Log qw(logit); our @ISA = 'Audio::Nama::Engine'; use Role::Tiny::With; with 'Audio::Nama::EcasoundRun'; sub launch_ecasound_server { my $self = shift; Audio::Nama::pager_newline("Using Ecasound via Audio::Ecasound (libecasoundc)"); $self->{audio_ecasound} = Audio::Ecasound->new(); } sub ecasound_iam{ #logsub("&ecasound_iam"); my $self = shift; my $cmd = shift; my $category = Audio::Nama::munge_category(shift()); logit(__LINE__,$category,'debug',"LibEcasound-ECI sent: $cmd"); my (@result) = $self->{audio_ecasound}->eci($cmd); logit(__LINE__,$category, 'debug',"LibEcasound-ECI got: @result") if $result[0] and not $cmd =~ /register/ and not $cmd =~ /int-cmd-list/; my $errmsg = $self->{audio_ecasound}->errmsg(); if( $errmsg ){ Audio::Nama::throw("Ecasound error: $errmsg") if $errmsg =~ /in engine-status/; $self->{audio_ecasound}->errmsg(''); } "@result"; } sub configure { Audio::Nama::NetEngine::configure(@_) } } # end package { package Audio::Nama::MidiEngine; use Modern::Perl; use SUPER; use Audio::Nama::Globals qw($config %tn); our @ISA = 'Audio::Nama::Engine'; sub new { my $self = super(); $self->{pids} = [ Audio::Nama::start_midish_process() ]; $self } sub configure { } sub setup { Audio::Nama::reconfigure_midi() } sub stop { Audio::Nama::stop_midi_transport() } sub cleanup { Audio::Nama::midi_rec_cleanup() } sub start { Audio::Nama::start_midi_transport() } sub rec_tracks { grep {$_->rec} $_[0]->user_tracks } sub system_tracks { $tn{$config->{midi_record_buffer}}} sub user_tracks { grep { $_->name ne $config->{midi_record_buffer} } $_[0]->tracks } sub play_tracks { grep {$_->play} $_[0]->user_tracks } sub is_active { $_[0]->rec_tracks or $_[0]->play_tracks } } # end package 1 __END__Audio-Nama-1.216/lib/Audio/Nama/CacheTrack.pm0000644000175000017500000002541313544212613017513 0ustar jrothjroth# -------- CacheTrack ------ package Audio::Nama; use Modern::Perl; use Storable 'dclone'; use Try::Tiny; use Audio::Nama::Globals qw(:all); # The $args hashref passed among the subroutines in this file # has these fields: # track # additional_time # processing_time # original_version # complete_caching_ref # output_wav # orig_volume # orig_pan sub cache_track { # launch subparts if conditions are met logsub('&cache_track'); my $args = {}; (my $track, $args->{additional_time}) = @_; local $this_track; throw("Set track to MON or PLAY"), return if $track->rw eq OFF and $track->is_mixer and $track->targets; $args->{track} = $track; $args->{additional_time} //= 0; $args->{is_mixing}++ if $track->is_mixing; $args->{original_version} = $track->is_mixing ? 0 : $args->{track}->playback_version; $args->{cached_version} = $args->{track}->last + 1; $args->{track_rw} = $track->{rw}; $args->{main_rw} = $tn{Main}->{rw}; $tn{Main}->set( rw => OFF); $track->set( rw => REC); pagers($track->name. ": preparing to cache ". ($track->is_mixing ? 'a bus' : 'an ordinary track')); throw($track->name. ": nothing to cache! Skipping."), return unless $track->is_mixing or $track->user_ops or $track->has_insert or $track->is_region; if($track->is_mixing) { generate_cache_graph_bus($args) } else { generate_cache_graph($args) } my $result = process_cache_graph($g); if ( $result ) { pager("generated graph"); deactivate_vol_pan($args); cache_engine_run($args); reactivate_vol_pan($args); return $args->{output_wav} } else { throw("Empty routing graph. Aborting."); return; } } sub deactivate_vol_pan { my $args = shift; unity($args->{track}, 'save_old_vol'); pan_check($args->{track}, 50); } sub reactivate_vol_pan { my $args = shift; pan_back($args->{track}); vol_back($args->{track}); } sub generate_cache_graph_bus { my $args = shift; my $g = Audio::Nama::ChainSetup::initialize(); $args->{graph} = $g; my $track = $args->{track}; # set WAV output format $args->{complete_caching_ref} = \&update_cache_map_bus; map{ $_->apply($g) } grep{ (ref $_) =~ /SubBus/ } Audio::Nama::Bus::all(); $g->set_vertex_attributes( $track->name, { format => signal_format($config->{cache_to_disk_format},$track->width), version => ($args->{track_result_version}), } ); # grep { $_->name ne 'Main' } } sub generate_cache_graph { logsub('&generate_cache_graph'); my $args = shift; my $g = Audio::Nama::ChainSetup::initialize(); $args->{graph} = $g; # We route the signal thusly: # # Target track --> CacheRecTrack --> wav_out # # CacheRecTrack slaves to target target # - same name # - increments track version by one my $cooked = Audio::Nama::CacheRecTrack->new( name => $args->{track}->name . '_cooked', group => 'Temp', target => $args->{track}->name, hide => 1, ); $g->add_path($args->{track}->name, $cooked->name, 'wav_out'); # save the output file name to return later $args->{output_wav} = $cooked->current_wav; # set WAV output format my $to_name = $args->{track}->name . '_' . $args->{cached_version} . '.wav'; my $to_path = join_path($args->{track}->dir, $to_name); $g->set_vertex_attributes( $cooked->name, { format => signal_format($config->{cache_to_disk_format},$cooked->width), full_version => $to_path, } ); $args->{complete_caching_ref} = \&update_cache_map; # set the input path $g->add_path('wav_in',$args->{track}->name); logpkg(__FILE__,__LINE__,'debug', "The graph after setting input path:\n$g"); my $from_name = $args->{track}->name . '_' . $args->{original_version} . '.wav'; my $from_path = join_path($args->{track}->dir, $from_name); $g->set_vertex_attributes( $args->{track}->name, { full_path => $from_path } ); } sub process_cache_graph { logsub('&process_cache_graph'); my $g = shift; logpkg(__FILE__,__LINE__,'debug', "The graph after bus routing:\n$g"); Audio::Nama::ChainSetup::prune_graph(); logpkg(__FILE__,__LINE__,'debug', "The graph after pruning:\n$g"); Audio::Nama::Graph::expand_graph($g); logpkg(__FILE__,__LINE__,'debug', "The graph after adding loop devices:\n$g"); Audio::Nama::Graph::add_inserts($g); logpkg(__FILE__,__LINE__,'debug', "The graph with inserts:\n$g"); my $success = Audio::Nama::ChainSetup::process_routing_graph(); if ($success) { Audio::Nama::ChainSetup::write_chains(); Audio::Nama::ChainSetup::remove_temporary_tracks(); } $success } sub cache_engine_run { logsub("&cache_engine_run"); my $args = shift; connect_transport() or throw("Couldn't connect engine! Aborting."), return; $args->{processing_time} = $setup->{audio_length} + $args->{additional_time}; pagers($args->{track}->name.": processing time: ". d2($args->{processing_time}). " seconds"); pagers("Starting cache operation. Please wait."); revise_prompt(" "); # we try to set processing time this way ecasound_iam("cs-set-length $args->{processing_time}"); ecasound_iam("start"); # ensure that engine stops at completion time $setup->{cache_track_args} = $args; $project->{events}->{poll_engine} = AE::timer(1, 0.5, \&poll_cache_progress); # complete_caching() contains the remainder of the caching code. # It is triggered by stop_polling_cache_progress() } sub complete_caching { logsub('&complete_caching'); my $args = shift; my $name = $args->{track}->name; my @files = grep{/$name/} new_files_were_recorded(); if (@files ){ $args->{complete_caching_ref}->($args) if defined $args->{complete_caching_ref}; post_cache_processing($args); } else { throw("track cache operation failed!") } undef $setup->{cache_track_args}; } sub update_cache_map { logsub('&update_cache_map'); my $args = shift; logpkg(__FILE__,__LINE__,'debug', "updating track cache_map"); logpkg(__FILE__,__LINE__,'debug', "current track cache entries:", sub { join "\n","cache map", map{($_->dump)} Audio::Nama::EffectChain::find(track_cache => 1) }); my @inserts_list = $args->{track}->get_inserts; # include all ops, include vol/pan operators # which serve as placeholders, won't overwrite # the track's current vol/pan operators my $track = $args->{track}; my @ops_list = @{$track->ops}; my @ops_remove_list = $track->user_ops; if ( @inserts_list or @ops_remove_list or $track->is_region ) { my %args = ( track_cache => 1, track_name => $track->name, track_version_original => $args->{original_version}, track_version_result => $args->{cached_version}, project => 1, system => 1, ops_list => \@ops_list, inserts_data => \@inserts_list, ); # is_mixing => $track->is_mixing, $args{region} = [ $track->region_start, $track->region_end ] if $track->is_region; $args{fade_data} = [ map { $_->as_hash } $track->fades ]; $args{track_target_original} = $track->target if $track->target; # late, because this changes after removing target field map{ delete $track->{$_} } qw(target); # update track settings my $ec = Audio::Nama::EffectChain->new( %args ); map{ $_->remove } $track->fades; map{ remove_effect($_) } @ops_remove_list; map{ $_->remove } @inserts_list; map{ delete $track->{$_} } qw( region_start region_end target ); pagers(qq(Saving effects for cached track "). $track->name. '".'); pagers(qq('uncache' will restore effects and set version $args->{original_version}\n)); } } sub update_cache_map_bus { my $args = shift; my $track = $args->{track}; my $filename = $track->targets->{$args->{cached_version}}; # system version comment with git tag my $tagname = my $msg = join " ","bus", $track->source_id, "cached as", $filename; $tagname =~ s/ /-/g; try{ git(tag => $tagname, '-a','-m',$msg) }; $track->add_system_version_comment($args->{cached_version}, $msg); pagers($msg); pagers(qq(To return this track to the state prior to caching, simply say '$track->{name} mon' The state of the project is saved and available through the tag $tagname)); } sub post_cache_processing { my $args = shift; $args->{track}->{rw} = $args->{track_rw}; $tn{Main}->{rw} = $args->{main_rw}; $args->{track}->set( rw => PLAY); $ui->global_version_buttons(); # recreate $ui->refresh(); revise_prompt("default"); } sub poll_cache_progress { my $args = $setup->{cache_track_args}; print "."; my $status = ecasound_iam('engine-status'); my $here = ecasound_iam("getpos"); update_clock_display(); logpkg(__FILE__,__LINE__,'debug', "engine time: ". d2($here)); logpkg(__FILE__,__LINE__,'debug', "engine status: $status"); return unless $status =~ /finished|error|stopped/ or $here > $args->{processing_time}; pagers("Done."); logpkg(__FILE__,__LINE__,'debug', engine_status(current_position(),2,1)); #revise_prompt(); stop_polling_cache_progress($args); } sub stop_polling_cache_progress { my $args = shift; $project->{events}->{poll_engine} = undef; $ui->reset_engine_mode_color_display(); complete_caching($args); } sub uncache_track { my $track = shift; local $this_track; $track->play or throw($track->name, ": cannot uncache unless track is set to PLAY"), return; my $version = $track->playback_version; my ($ec) = is_cached($track, $version); if (not defined $ec) { if ($track->source_type eq 'bus') { $track->set(rw => MON); pager("Enabling bus $track->{group} by setting mix track $track->{name} to MON"); return } else{ throw($track->name, ": version $version is not cached"), return } } $track->user_ops and throw($track->name, ": cannot uncache while user effects are present\n", "You must delete them before you can uncache this WAV version."), return; $track->is_region and throw($track->name, ": cannot uncache while region is set for this track\n", "Remove it and try again."), return; # $ec->inserts and $track->inserts and throw($track->name, # ": cannot uncache inserts because an insert is already set for this track\n", # "Remove it and try again."), return; $ec->add($track); # replace track's effect list with ours $track->{ops} = dclone($ec->ops_list); # applying the the effect chain doesn't set the version or target # so we do it here $track->set(version => $ec->track_version_original); $track->set(target => $ec->track_target_original) if $ec->track_target_original; pager($track->name, ": setting uncached version ", $track->version, $/); pager($track->name, ": setting original region bounded by marks ", $track->region_start, " and ", $track->region_end, $/) if $track->is_region; } sub is_cached { my ($track, $version) = @_; my @results = Audio::Nama::EffectChain::find( project => 1, track_cache => 1, track_name => $track->name, track_version_result => $version, ); scalar @results > 1 and warn ("more than one EffectChain matching query!, found", map{ json_out($_->as_hash) } @results); $results[-1] } 1; __END__Audio-Nama-1.216/lib/Audio/Nama/Mix.pm0000644000175000017500000000620313544212613016254 0ustar jrothjrothpackage Audio::Nama; use Modern::Perl; sub check_level { my $track = shift; my $ev = add_effect( { track => $track, type => 'ev' } ); # disable Main so unused tracks are pruned $tn{Main}->set(rw => OFF); # direct target track to null my $null_routing = sub { my $g = shift; $g->add_path($track->name, output_node('null')) }; generate_setup($null_routing) or throw("check_level: generate_setup failed!"), return; connect_transport(); ecasound_iam('start'); # don't use heartbeat sleep 2; # time for engine to stabilize while( ecasound_iam('engine-status') ne 'finished'){ print q(.); sleep 1; update_clock_display()}; print " Done\n"; my $cs = ecasound_iam('cop-status'); my ($level_output) = $cs =~ /Status info:\s*?\n(.+)\z/s; pager($level_output); # restore previous state remove_effect($ev); $tn{Main}->set(rw => MON); Audio::Nama::request_setup(); } sub automix { # get working track set my @tracks = grep{ $tn{$_}->play or $bn{$_} and $tn{$_}->rec } $bn{Main}->tracks; pager("tracks: @tracks"); ## we do not allow automix if inserts are present throw("Cannot perform automix if inserts are present. Skipping."), return if grep{$tn{$_}->prefader_insert || $tn{$_}->postfader_insert} @tracks; #use Smart::Comments '###'; # add -ev to summed signal my $ev = add_effect( { chain => $tn{Main}->n, type => 'ev' } ); ### ev id: $ev # turn off audio output my $old_send_type = $tn{Main}->{send_type}; my $old_send_id = $tn{Main}->{send_id}; $tn{Main}->set(send_type => 'null', send_id => 'null'); ### Status before mixdown: nama_cmd('show'); ### reduce track volume levels to 10% ## accommodate ea and eadb volume controls my $vol_operator = fxn($tn{$tracks[0]}->vol)->type; my $reduce_vol_command = $vol_operator eq 'ea' ? 'vol / 10' : 'vol - 10'; my $restore_vol_command = $vol_operator eq 'ea' ? 'vol * 10' : 'vol + 10'; ### reduce vol command: $reduce_vol_command for (@tracks){ nama_cmd("$_ $reduce_vol_command") } nama_cmd('show'); generate_setup('automix') # pass a bit of magic or throw("automix: generate_setup failed!"), return; connect_transport(); # start_transport() does a rec_cleanup() on transport stop ecasound_iam('start'); # don't use heartbeat sleep 2; # time for engine to stabilize while( ecasound_iam('engine-status') ne 'finished'){ print q(.); sleep 1; update_clock_display()}; print " Done\n"; # parse cop status my $cs = ecasound_iam('cop-status'); ### cs: $cs my $cs_re = qr/Chain "1".+?result-max-multiplier ([\.\d]+)/s; my ($multiplier) = $cs =~ /$cs_re/; ### multiplier: $multiplier remove_effect($ev); # deal with all silence case, where multiplier is 0.00000 if ( $multiplier < 0.00001 ){ throw("Signal appears to be silence. Skipping."); for (@tracks){ nama_cmd("$_ $restore_vol_command") } $tn{Main}->set(rw => MON); return; } ### apply multiplier to individual tracks for (@tracks){ nama_cmd( "$_ vol*$multiplier" ) } ### mixdown nama_cmd('mixdown; arm; start'); ### restore audio output $tn{Main}->set( send_type => $old_send_type, send_id => $old_send_id); #no Smart::Comments; } 1 __END__Audio-Nama-1.216/lib/Audio/Nama/Text.pm0000644000175000017500000000312613544212613016444 0ustar jrothjroth# -------- Text Interface ----------- ## The following subroutines/methods belong to the Text interface class ## the grammar of the command processor is defined in # grammar_body.pl with subroutines in Grammar.p package Audio::Nama::Text; use Modern::Perl; use Carp; no warnings 'uninitialized'; use Audio::Nama::Globals qw(:all); use Audio::Nama::Assign qw(:all); our @ISA = 'Audio::Nama'; our $VERSION = 1.071; sub hello {"hello world!";} sub loop { package Audio::Nama; initialize_prompt(); $Event::DIED = sub { my ($event, $errmsg) = @_; throw($errmsg); $text->{term_attribs}->{line_buffer} = q(); if($text->{term}){ $text->{term}->clear_message(); $text->{term}->rl_reset_line_state(); } }; use Data::Dumper::Concise; Event::loop(); } ## NO-OP GRAPHIC METHODS no warnings qw(redefine); sub init_gui {} sub transport_gui {} sub group_gui {} sub track_gui {} sub preview_button {} sub create_master_and_mix_tracks {} sub time_gui {} sub refresh {} sub refresh_group {} sub refresh_track {} sub flash_ready {} sub update_master_version_button {} sub update_version_button {} sub paint_button {} sub project_label_configure{} sub length_display{} sub clock_display {} sub clock_config {} sub manifest {} sub global_version_buttons {} sub destroy_widgets {} sub destroy_marker {} sub restore_time_marks {} sub show_unit {} sub add_effect_gui {} sub remove_effect_gui {} sub marker {} sub init_palette {} sub save_palette {} sub paint_mute_buttons {} sub remove_track_gui {} sub reset_engine_mode_color_display {} sub set_engine_mode_color_display {} sub setup_playback_indicator {} 1; __END__Audio-Nama-1.216/lib/Audio/Nama/Object.pm0000644000175000017500000000506313544212613016730 0ustar jrothjrothpackage Audio::Nama::Object; use Modern::Perl; use Carp; use Audio::Nama::Assign qw(json_out); use Storable qw(dclone); use Data::Dumper::Concise; no strict; # Enable during dev and testing BEGIN { require 5.004; $Audio::Nama::Object::VERSION = '1.04'; } sub import { return unless shift eq 'Audio::Nama::Object'; my $pkg = caller; my $child = 0+@{"${pkg}::ISA"}; eval join '', "package $pkg;\n", ' use vars qw(%_is_field); ', ' map{ $_is_field{$_}++ } @_;', ($child ? () : "\@${pkg}::ISA = Audio::Nama::Object;\n"), map { defined and ! ref and /^[^\W\d]\w*$/s or die "Invalid accessor name '$_'"; "sub $_ { \$_[0]->{$_} }" } @_; die "Failed to generate $pkg" if $@; return 1; } sub new { my $class = shift; bless { @_ }, $class; } sub is_legal_key { # The behavior I want here is: # # Example class hierachy: Audio::Nama::Object, Audio::Nama::Wav, Audio::Nama::Track, Audio::Nama::SimpleTrack # By inheriting from Track, SimpleTrack gets all the # attributes of Track and Wav, without having to include # them in the Track class definition my ($class, $key) = @_; $class = ref $class if ref $class; # support objects return 1 if ${"$class\::_is_field"}{$key}; my ($parent_class) = @{"$class\::ISA"}; return unless $parent_class and $parent_class !~ /Object::Tiny/; # this should be: # return unless $parent_class and $parent_class !~ /Object/; is_legal_key($parent_class,$key); } sub set { my $self = shift; my $class = ref $self; #print "class: $class, args: @_\n"; croak "odd number of arguments ",join "\n--\n" ,@_ if @_ % 2; my %new_vals = @_; map{ $self->{$_} = $new_vals{$_} ; my $key = $_; is_legal_key(ref $self, $key) or croak "illegal key: $_ for object of type ", ref $self; } keys %new_vals; } sub dumpp { my $self = shift; print $self->dump } sub dump { my $self = shift; my $output = Dumper($self); return $output; } sub as_hash { my $self = shift; my $class = ref $self; bless $self, 'HASH'; # easy magic my %guts = %{ $self }; bless $self, $class; $guts{class} = $class if is_legal_key(ref $self, 'class'); return \%guts; } 1; __END__ =pod =head1 NAME Audio::Nama::Object - Class builder =head1 SYNOPSIS # Define a class package Foo; use Audio::Nama::Object qw{ bux baz }; 1; # Use the class my $object = Foo->new( bux => 1 ); $object->set( bux => 2); print "bux is " . $object->bux . "\n"; # Define a subclass (automatically inherits parent attributes) package Bar; our @ISA = 'Foo'; my $lonely_bar = Bar->new(); $lonely_bar->set(bux => 3); Audio-Nama-1.216/lib/Audio/Nama/Grammar.pm0000644000175000017500000004242213544212613017110 0ustar jrothjroth# --------------------- Command Grammar ---------------------- package Audio::Nama; use Audio::Nama::Effect qw(:all); use Modern::Perl; sub setup_grammar { ### COMMAND LINE PARSER logsub("&setup_grammar"); $text->{commands_yml} = get_data_section("commands_yml"); $text->{commands_yml} = quote_yaml_scalars($text->{commands_yml}); $text->{commands} = yaml_in( $text->{commands_yml}) ; map { my $full_name = $_; my $shortcuts = $text->{commands}->{$full_name}->{short}; my @shortcuts = (); @shortcuts = split " ", $shortcuts if $shortcuts; map{ $text->{command_shortcuts}->{$_} = $full_name } @shortcuts; } keys %{$text->{commands}}; $Audio::Nama::AUTOSTUB = 1; $Audio::Nama::RD_TRACE = 1; $Audio::Nama::RD_ERRORS = 1; # Make sure the parser dies when it encounters an error $Audio::Nama::RD_WARN = 1; # Enable warnings. This will warn on unused rules &c. $Audio::Nama::RD_HINT = 1; # Give out hints to help fix problems. $text->{grammar} = get_data_section('grammar'); $text->{parser} = Parse::RecDescent->new($text->{grammar}) or croak "Bad grammar!\n"; # Midish command keywords # prepend 'm' to all midish commands # suppress midi record, play, stop commands - Nama will handle them # also suppress ct tnew tdel tren my %skip = map{$_, 1} qw(r p s ct tnew tdel tren); $text->{midi_cmd} = { map{ 'm'.$_, 1} grep{ !$skip{$_} } split " ", get_data_section("midi_commands") }; for (keys %{$text->{midi_cmd}}){ say "$_: midi command same as Nama command" if $text->{commands}->{$_} } } sub process_line { state $total_effects_count; logsub("&process_line"); no warnings 'uninitialized'; my ($user_input) = @_; # convert hyphenated commands to underscore form while( my ($from, $to) = each %{$text->{hyphenated_commands}}) { $user_input =~ s/$from/$to/g } logpkg(__FILE__,__LINE__,'debug',"user input: $user_input"); if (defined $user_input and $user_input !~ /^\s*$/) { $text->{term}->addhistory($user_input) unless $user_input eq $text->{previous_cmd} or ! $text->{term}; $text->{previous_cmd} = $user_input; my $context = context(); my $success = nama_cmd( $user_input ); my $command_stamp = { context => $context, command => $user_input }; push(@{$project->{command_buffer}}, $command_stamp); if ( $config->{autosave} eq 'undo' and $config->{use_git} and $project->{name} and $project->{repo} and ! $this_engine->started() ){ local $quiet = 1; Audio::Nama::ChainSetup::remove_temporary_tracks(); autosave() unless $config->{opts}->{R}; reconfigure_engine(); } reconfigure_engine(); # reset current track to Main if it is # undefined, or the track has been removed # from the index $this_track = $tn{Main} if ! $this_track or (ref $this_track and ! $tn{$this_track->name}); setup_hotkeys() if $config->{hotkeys_always}; } if (! $this_engine->started() ){ my $result = check_fx_consistency(); logpkg(__FILE__,__LINE__,'logcluck',"Inconsistency found in effects data", Dumper ($result)) if $result->{is_error}; } my $output = delete $text->{output_buffer}; revise_prompt(); } sub context { my $context = {}; $context->{track} = $this_track->name; $context->{bus} = $this_bus; $context->{op} = $this_track->op; $context } sub nama_cmd { my $input_was = my $input = shift; # parse repeatedly until all input is consumed # return true on complete success # return false if any part of command fails my $was_error = 0; try { while (do { no warnings 'uninitialized'; $input =~ /\S/ }) { logpkg(__FILE__,__LINE__,'debug',"input: $input"); $text->{parser}->meta(\$input) or do { throw("bad command: $input_was\n"); $was_error++; system($config->{beep_command}) if $config->{beep_command}; last; }; } } catch { $was_error++; warn "caught error: $_" }; $ui->refresh; # in case we have a graphic environment set_current_bus(); # select chain operator if appropriate # and there is a current track $this_engine->valid_setup() or return; if ($this_track){ my $FX = fxn($this_track->op); if ($FX and $this_track->n eq $FX->chain){ $this_engine->current_chain($this_track->n); $FX->is_controller ? $this_engine->current_controller($FX->ecasound_controller_index) : $this_engine->current_chain_operator($FX->ecasound_effect_index); } } ! $was_error } sub do_user_command { my($cmd, @args) = @_; $text->{user_command}->{$cmd}->(@args); } sub do_script { my $name = shift; my $script; if ($name =~ / /){ $script = $name } else { my $filename; # look in project_dir() and project_root() # if filename provided does not contain slash if( $name =~ m!/!){ $filename = $name } else { $filename = join_path(project_dir(),$name); if(-e $filename){} else{ $filename = join_path(project_root(),$name) } } -e $filename or throw("$filename: file not found. Skipping"), return; $script = read_file($filename) } my @lines = split "\n",$script; my $old_opt_r = $config->{opts}->{R}; $config->{opts}->{R} = 1; # turn off auto reconfigure map{ s/#.*$// } @lines; for my $input (@lines) { process_line($input) unless $input =~ /^\s*#/}; $config->{opts}->{R} = $old_opt_r; } sub dump_all { my $tmp = ".dump_all"; my $format = "json"; my $fname = join_path( project_root(), $tmp); save_system_state($fname,$format); file_pager("$fname.$format"); } sub set_current_track { my $cmd = shift; if( my $track = $tn{$cmd} || $ti{$cmd} ){ logpkg(__FILE__,__LINE__,'debug',"Selecting track ",$track->name); $track->select_track; 1 } } ### allow commands to abbreviate Audio::Nama::Class as ::Class # SKIP_PREPROC { my @namespace_abbreviations = qw( Assign Track Bus Mark IO Graph Wav Insert Fade Edit Text Effect EffectChain ChainSetup ); my $namespace_root = 'Audio::Nama'; sub eval_perl { my $code = shift; map{ $code =~ s/(^|[^A-Za-z])::$_/$1$namespace_root\::$_/ } @namespace_abbreviations; # SKIP_PREPROC my $err; undef $text->{eval_result}; my @result = eval $code; if ($@){ throw( "Perl command failed: \ncode: $code\nerror: $@"); undef $@; } else { no warnings 'uninitialized'; @result = map{ dumper($_) } @result; $text->{eval_result} = join " ", @result; pager(join "\n", @result) } } } # end namespace abbreviations #### Formatted text output sub show_versions { no warnings 'uninitialized'; if (@{$this_track->versions} ){ "All versions: ". join(" ", map { my $cached = is_cached($this_track, $_) ? 'c' : ''; $cached .= 'C' if $this_track->is_version_comment($_); $_ . $cached } @{$this_track->versions} ). $/ } else {} } sub show_track_comment { my $track = shift; my $text = $track->is_comment; $text and "Track comment: $text\n"; } sub show_version_comment { my ($track, $version) = @_; my $text = $track->is_version_comment($version); $text and "Version comment: $text\n"; } sub show_send { "Send: ". $this_track->send_id. $/ if ! $this_track->off and $this_track->send_id } sub show_bus { "Bus: ". $this_track->group. $/ if $this_track->group ne 'Main' } sub show_effects { Audio::Nama::sync_effect_parameters(); join "", map { show_effect($_) } @{ $this_track->ops }; } sub list_effects { Audio::Nama::sync_effect_parameters(); join "", "Effects on ", $this_track->name,":\n", map{ list_effect($_) } @{ $this_track->ops }; } sub list_effect { my $op_id = shift; my $FX = fxn($op_id); my $line = $FX->nameline; $line .= q(, bypassed) if $FX->bypassed; ($op_id eq $this_track->op ? ' *' : ' ') . $line; } sub show_effect { my $op_id = shift; my $with_track = shift; my $FX = fxn($op_id); return unless $FX; my @lines = $FX->nameline; #EQ: GVerb, gverb, 1216, bypassed, famp5, neap my $i = $FX->registry_index; my @pnames = @{$fx_cache->{registry}->[ $i ]->{params}}; { no warnings 'uninitialized'; push @lines, parameter_info_padded($op_id, $_) for 0..scalar @pnames - 1; } scalar @{$FX->params} - scalar @pnames - 1 and push @lines, parameter_info_padded($op_id, $_) for scalar @pnames .. (scalar @{$FX->params} - 1); @lines } sub parameter_info { no warnings 'uninitialized'; my ($op_id, $parameter) = @_; # zero based my $FX = fxn($op_id); return unless $FX; my $entry = $FX->about->{params}->[$parameter]; my $name = $entry->{name}; $name .= " (read-only)" if $entry->{dir} eq 'output'; ($parameter+1).q(. ) . $name . ": ". $FX->params->[$parameter]; } sub parameter_info_padded { " "x 4 . parameter_info(@_) . "\n"; } sub named_effects_list { my @ops = @_; join("\n", map{ "$_ (" . fxn($_)->name. ")" } @ops), "\n"; } sub show_modifiers { join "", "Modifiers: ",$this_track->modifiers, $/ if $this_track->modifiers; } sub show_region { my $t = $Audio::Nama::this_track; return unless $t->play; my @lines; push @lines,join " ", "Length:",time2($t->shifted_length),"\n"; $t->playat and push @lines,join " ", "Play at:",time2($t->shifted_playat_time), join($t->playat, qw[ ( ) ])."\n"; $t->region_start and push @lines,join " ", "Region start:",time2($t->shifted_region_start_time), join($t->region_start, qw[ ( ) ])."\n"; $t->region_end and push @lines,join " ", "Region end:",time2($t->shifted_region_end_time), join($t->region_end, qw[ ( ) ])."\n"; return(join "", @lines); } sub time2 { package Audio::Nama; my $n = shift; dn($n,3),"/",colonize(int ($n + 0.5)); } sub show_status { package Audio::Nama; my @output; my @modes; push @modes, 'preview' if $mode->{preview}; push @modes, 'doodle' if $mode->{doodle}; push @modes, "master" if $mode->mastering; push @modes, "edit" if Audio::Nama::edit_mode(); push @modes, "offset run" if Audio::Nama::is_offset_run_mode(); push @output, "Modes settings: ", join(", ", @modes), $/ if @modes; my @actions; push @actions, "record" if grep{ ! /Mixdown/ } Audio::Nama::ChainSetup::really_recording(); push @actions, "playback" if grep { $_->play } map{ $tn{$_} } $bn{Main}->tracks, q(Mixdown); # We only check Main bus for playback. # buses will route their playback signals through the # Main bus, however it may be that other bus mixdown # tracks are set to REC (with rec-to-file disabled) push @actions, "mixdown" if $tn{Mixdown}->rec; push @output, "Pending actions: ", join(", ", @actions), $/ if @actions; push @output, "Main bus version: ",$bn{Main}->version, $/ if $bn{Main}->version; push @output, "Setup length is: ", Audio::Nama::heuristic_time($setup->{audio_length}), $/; push @output, "Run time limit: ", Audio::Nama::heuristic_time($setup->{runtime_limit}), $/ if $setup->{runtime_limit}; @output } sub placeholder { my $val = shift; return $val if defined $val and $val !~ /^\s*$/; $config->{use_placeholders} ? q(--) : q() } sub show_inserts { my $output; $output = $Audio::Nama::Insert::by_index{$this_track->prefader_insert}->dump if $this_track->prefader_insert; $output .= $Audio::Nama::Insert::by_index{$this_track->postfader_insert}->dump if $this_track->postfader_insert; "Inserts:\n".join( "\n",map{" "x4 . $_ } split("\n",$output))."\n" if $output; } $text->{format_top} = <{format_divider} = '-' x 77 . "\n"; my $format_picture = <> @<<<<<<<<<<<<<< @>>> @<<<<<< @<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<< @>>> @>>> PICTURE sub show_tracks_section { no warnings; #$^A = $text->{format_top}; my @tracks = grep{ ref $_ } @_; # HACK! undef should not be passed map { formline $format_picture, $_->n, $_->name, $_->rw eq $_->rec_status ? undef : $_->rw, $_->rec_status_display, placeholder($_->source_status), placeholder($_->destination), placeholder($_->vol_level), placeholder($_->pan_level), ($_->is_comment ? 'C' : undef) } @tracks; my $output = $^A; $^A = ""; #$output .= show_tracks_extra_info(); $output; } sub show_tracks { my @array_refs = @_; my @list = $text->{format_top}; for( @array_refs ){ my ($mix,$bus) = splice @$_, 0, 2; push @list, Audio::Nama::Bus::settings_line($mix, $bus), show_tracks_section(@$_), } @list } sub showlist { package Audio::Nama; my @list = grep{ ! $_->hide } Audio::Nama::all_tracks(); my $section = [undef,undef,@list]; my ($screen_lines, $columns); if( $text->{term} ) { ($screen_lines, $columns) = $text->{term}->get_screen_size(); } return $section if scalar @list <= $screen_lines - 5 or ! $screen_lines; my @sections; push @sections, [undef,undef, map $tn{$_},qw(Main Mixdown)]; push @sections, [$tn{Main},$bn{Main},map $tn{$_},$bn{Main}->tracks ]; if( $mode->mastering ){ push @sections, [undef,undef, map $tn{$_},$bn{Mastering}->tracks] } elsif($this_bus ne 'Main'){ push @sections, [$tn{$this_bus},$bn{$this_bus}, map $tn{$_}, $this_bus, $bn{$this_bus}->tracks] } @sections } #### Some Text Commands sub t_load_project { package Audio::Nama; return if $this_engine->started() and Audio::Nama::ChainSetup::really_recording(); my $name = shift; my %args = @_; pager("input name: $name\n"); $name = sanitize($name); throw("Project $name does not exist\n"), return unless -d join_path(project_root(), $name) or $args{create}; stop_transport() if $this_engine->started(); save_state(); load_project( name => $name, %args ); pager("loaded project: $project->{name}\n") unless $args{create}; {no warnings 'uninitialized'; logpkg(__FILE__,__LINE__,'debug',"load hook: $config->{execute_on_project_load}"); } Audio::Nama::nama_cmd($config->{execute_on_project_load}); } sub sanitize { my $name = shift; my $newname = remove_spaces($name); $newname =~ s(/$)(); # remove trailing slash $newname; } sub t_create_project { package Audio::Nama; my $name = shift; t_load_project($name, create => 1); pager("created project: $project->{name}\n"); } sub mixdown { pager_newline("Enabling mixdown to file") if ! $quiet; $tn{Mixdown}->set(rw => REC); $tn{Main}->set(rw => MON); } sub mixplay { pager_newline("Setting mixdown playback mode.") if ! $quiet; $tn{Mixdown}->set(rw => PLAY); $tn{Main}->set(rw => OFF); } sub mixoff { pager_newline("Leaving mixdown mode.") if ! $quiet; $tn{Mixdown}->set(rw => OFF); $tn{Main}->set(rw => MON); } sub remove_fade { my $i = shift; my $fade = $Audio::Nama::Fade::by_index{$i} or throw("fade index $i not found. Aborting."), return 1; pager("removing fade $i from track " .$fade->track ."\n"); $fade->remove; } sub import_audio { my ($track, $path, $frequency) = @_; $track->import_audio($path, $frequency); # check that track is audible $track->set(rw => PLAY); } sub destroy_current_wav { carp($this_track->name.": must be set to PLAY."), return unless $this_track->play; $this_track->current_version or throw($this_track->name, ": No current version (track set to OFF?) Skipping."), return; my $wav = $this_track->full_path; my $reply = $text->{term}->readline("delete WAV file $wav? [n] "); #my $reply = chr($text->{term}->read_key()); if ( $reply =~ /y/i ){ # remove version comments, if any delete $this_track->{version_comment}{$this_track->current_version}; pager("Unlinking.\n"); unlink $wav or warn "couldn't unlink $wav: $!\n"; restart_wav_memoize(); } $text->{term}->remove_history($text->{term}->where_history); $this_track->set(version => 0); # reset $this_track->set(version => $this_track->current_version); 1; } sub pan_check { my ($track, $new_position) = @_; my $current = $track->pan_o->params->[0]; $track->set(old_pan_level => $current) unless defined $track->old_pan_level; update_effect( $track->pan, # id 0, # parameter $new_position, # value ); } sub remove_track_cmd { my ($track) = @_; # avoid having ownerless SlaveTracks. Audio::Nama::ChainSetup::remove_temporary_tracks(); $quiet or pager( "Removing track /$track->name/. All WAV files will be kept. Other data will be lost."); remove_submix_helper_tracks($track->name); $track->remove; $this_track = $tn{Main}; 1 } sub unity { my ($track, $save_level) = @_; if ($save_level){ $track->set(old_vol_level => fxn($track->vol)->params->[0]); } update_effect( $track->vol, 0, $config->{unity_level}->{fxn($track->vol)->type} ); } sub vol_back { my $track = shift; my $old = $track->old_vol_level; if (defined $old){ update_effect( $track->vol, # id 0, # parameter $old, # value ); $track->set(old_vol_level => undef); } } sub pan_back { my $track = shift; my $old = $track->old_pan_level; if (defined $old){ update_effect( $track->pan, # id 0, # parameter $old, # value ); $track->set(old_pan_level => undef); } } sub get_sample_rate { pager("project $project->{name}: audio engine sample rate is ",$project->{sample_rate} ); $project->{sample_rate} } sub set_sample_rate { my ($srate) = @_; my @allowable = qw{ 96000 88200 64000 48000 44100 32000 24000 22050 16000 11025 8000 }; my %allowable = map{$_ => 1} @allowable; if ( $allowable{$srate} ){ $project->{sample_rate} = $srate; pager("project $project->{name}: setting audio engine sample rate to $srate Hz for future runs." ); $srate } else { get_sample_rate(); pager qq(The value "$srate" is not an allowable sample rate.); pager("Use one of: @allowable"); } } sub list_buses { Audio::Nama::pager(map{ $_->list } Audio::Nama::Bus::all()) }Audio-Nama-1.216/lib/Audio/Nama/Jack.pm0000644000175000017500000002372413544212613016376 0ustar jrothjroth# ------- Jack port connect routines ------- package Audio::Nama; use Modern::Perl; use File::Slurp; no warnings 'uninitialized'; # general functions sub update_jack_client_list { state $warn_count; #logsub("&update_jack_client_list"); # cache current JACK status # skip if Ecasound is busy return if $this_engine->started(); if( $jack->{jackd_running} = process_is_running('jackd') ){ # reset our clients data $jack->{clients} = {}; $jack->{use_jacks} ? jacks_get_port_latency() : parse_port_latency(); parse_ports_list(); my ($bufsize) = qx(jack_bufsize); ($jack->{periodsize}) = $bufsize =~ /(\d+)/; my ($sample_rate) = qx(jack_samplerate); chomp $sample_rate; $project->{name} and $sample_rate != $project->{sample_rate} and ($warn_count == 1 or $warn_count % 8 == 0) # warn less often and Audio::Nama::throw(qq( JACK audio daemon sample rate is $sample_rate but sample rate for project "$project->{name}" is $project->{sample_rate}. Please fix this problem before continuing (maybe restart jackd with --rate $project->{sample_rate}?))), print prompt(); $warn_count++; } else { } } sub client_port { my $name = shift; $name =~ /(.+?):([^:]+)$/; =comment $name =~ / (?.+?) # anything, non-greedy : # a colon (?[^:]+$) # non-colon stuff to end /x; @+{qw(client port)} =cut $1, $2 } sub jack_client_array { # returns array of ports if client and direction exist my ($name, $direction) = @_; $jack->{clients}->{$name}{$direction} // [] } sub jacks_get_port_latency { logsub('&jacks_get_port_latency'); delete $jack->{clients}; my $jc; $jc = jacks::JsClient->new("watch latency", undef, $jacks::JackNullOption, 0); my $plist = $jc->getPortNames("."); for (my $i = 0; $i < $plist->length(); $i++) { my $pname = $plist->get($i); my ($client_name,$port_name) = client_port($pname); logpkg(__FILE__,__LINE__,'debug',qq(client: $client_name, port: $port_name)); my $port = $jc->getPort($pname); #my @connections = $jc->getAllConnections($client_name, $port_name); #say for @connections; my $platency = $port->getLatencyRange($jacks::JackPlaybackLatency); my $pmin = $platency->min(); my $pmax = $platency->max(); logpkg(__FILE__,__LINE__,'debug',"$pname: playback Latency [ $pmin $pmax ]"); $jack->{clients}->{$client_name}->{$port_name}->{latency}->{playback}->{min} = $pmin; $jack->{clients}->{$client_name}->{$port_name}->{latency}->{playback}->{max} = $pmax; my $clatency = $port->getLatencyRange($jacks::JackCaptureLatency); my $cmin = $clatency->min(); my $cmax = $clatency->max(); logpkg(__FILE__,__LINE__,'debug',"$pname: capture Latency [ $cmin $cmax ]"); $jack->{clients}->{$client_name}->{$port_name}->{latency}->{capture}->{min} = $cmin; $jack->{clients}->{$client_name}->{$port_name}->{latency}->{capture}->{max} = $cmax; } } sub parse_port_connections { my $j = shift || qx(jack_lsp -c 2> /dev/null); return unless $j; # initialize $jack->{connections} = {}; # convert to single lines $j =~ s/\n\s+/ /sg; my @lines = split "\n",$j; #say for @ports; for (@lines){ my ($port, @connections) = split " ", $_; #say "$port @connections"; $jack->{connections}->{$port} = \@connections; } } sub jack_port_to_nama { my $jack_port = shift; grep{ /$config->{ecasound_jack_client_name}/ and $jack->{is_own_port}->{$_} } @{ $jack->{connections}->{$jack_port} }; } sub parse_port_latency { # default to use output of jack_lsp -l my $j = shift || qx(jack_lsp -l 2> /dev/null); logpkg(__FILE__,__LINE__,'debug', "latency input $j"); state $port_latency_re = qr( # ecasound:in_1 (?[^:]+) # non-colon : # colon (?\S+?) # non-space \s+ # port latency = 2048 frames # DEPRECATED \Qport latency = \E \d+ # don't capture \Q frames\E \s+ # port playback latency = [ 0 2048 ] frames \Qport playback latency = [ \E (?\d+) \s+ (?\d+) \Q ] frames\E \s+ # port capture latency = [ 0 2048 ] frames \Qport capture latency = [ \E (?\d+) \s+ (?\d+) \Q ] frames\E )x; # convert to single lines $j =~ s/\n\s+/ /sg; my @ports = split "\n",$j; map { /$port_latency_re/; #logpkg(__FILE__,__LINE__,'debug', Dumper %+); logpkg(__FILE__,__LINE__,'debug', "client: ",$+{client}); logpkg(__FILE__,__LINE__,'debug', "port: ",$+{port}); logpkg(__FILE__,__LINE__,'debug', "capture min: ", $+{capture_min}); logpkg(__FILE__,__LINE__,'debug', "capture max: ",$+{capture_max}); logpkg(__FILE__,__LINE__,'debug', "playback min: ",$+{playback_min}); logpkg(__FILE__,__LINE__,'debug', "playback max: ",$+{playback_max}); $jack->{clients}->{$+{client}}->{$+{port}}->{latency}->{capture}->{min} = $+{capture_min}; $jack->{clients}->{$+{client}}->{$+{port}}->{latency}->{capture}->{max} = $+{capture_max}; $jack->{clients}->{$+{client}}->{$+{port}}->{latency}->{playback}->{min} = $+{playback_min}; $jack->{clients}->{$+{client}}->{$+{port}}->{latency}->{playback}->{max} = $+{playback_max}; } @ports; } sub parse_ports_list { # default to output of jack_lsp -p logsub("&parse_ports_list"); my $j = shift || qx(jack_lsp -tp 2> /dev/null); logpkg(__FILE__,__LINE__,'debug', "input: $j"); # convert to single lines $j =~ s/\n\s+/ /sg; # system:capture_1 alsa_pcm:capture_1 properties: output,physical,terminal, #fluidsynth:left properties: output, #fluidsynth:right properties: output, map{ my ($direction) = /properties: (input|output)/; s/properties:.+//; my @port_aliases = / \s* # zero or more spaces ([^:]+:[^:]+?) # non-colon string, colon, non-greedy non-colon string (?=[-+.\w]+:|\s+$) # zero-width port name or spaces to end-of-string /gx; map { s/ $//; # remove trailing space # make entries for 'system' and 'system:capture_1' push @{ $jack->{clients}->{$_}->{$direction} }, $_; my ($client, $port) = /(.+?):(.+)/; push @{ $jack->{clients}->{$client}->{$direction} }, $_; } @port_aliases; } grep{ ! /^jack:/i } # skip spurious jackd diagnostic messages grep{ ! /8 bit raw midi/ } split "\n",$j; } # connect jack ports via jack-plumbing or jack_connect sub jack_plumbing_conf { join_path( $ENV{HOME} , '.jack-plumbing' ) } sub initialize_jack_plumbing_conf { } { my $fh; my $jack_plumbing_code = sub { my ($port1, $port2) = @_; my $debug++; my $config_line = qq{(connect $port1 $port2)}; say $fh $config_line; # $fh in lexical scope logpkg(__FILE__,__LINE__,'debug', $config_line); }; my $jack_connect_code = sub { my ($port1, $port2) = @_; my $debug++; my $cmd = qq(jack_connect $port1 $port2); logpkg(__FILE__,__LINE__,'debug', $cmd); system($cmd) == 0 or die "system $cmd failed: $?"; }; sub connect_jack_ports_list { my @source_tracks = grep{ $_->source_type eq 'jack_ports_list' and $_->rec } Audio::Nama::ChainSetup::engine_tracks(); my @send_tracks = grep{ $_->send_type eq 'jack_ports_list' } Audio::Nama::ChainSetup::engine_tracks(); # we need JACK return if ! $jack->{jackd_running}; # We need tracks to configure return if ! @source_tracks and ! @send_tracks; sleeper(0.3); # extra time for ecasound engine to register JACK ports if( $config->{use_jack_plumbing} ) { open($fh, ">", jack_plumbing_conf()) or die("can't open ".jack_plumbing_conf()." for write: $!"); make_connections($jack_plumbing_code, \@source_tracks, 'in' ); make_connections($jack_plumbing_code, \@send_tracks, 'out'); close $fh; # run jack-plumbing start_jack_plumbing(); sleeper(3); # time for jack-plumbing to launch and poll kill_jack_plumbing(); } else { make_connections($jack_connect_code, \@source_tracks, 'in' ); make_connections($jack_connect_code, \@send_tracks, 'out'); } } } sub quote { $_[0] =~ /^"/ ? $_[0] : qq("$_[0]")} sub make_connections { my ($code, $tracks, $direction) = @_; my $ports_list = $direction eq 'in' ? 'source_id' : 'send_id'; map{ my $track = $_; my $name = $track->name; my $ecasound_port = $config->{ecasound_jack_client_name}.":$name\_$direction\_"; my $file = join_path(project_root(), $track->$ports_list); throw($track->name, ": JACK ports file $file not found. No sources connected."), return if ! -e -r $file; my $line_number = 0; my @lines = read_file($file); for my $external_port (@lines){ # $external_port is the source port name chomp $external_port; logpkg(__FILE__,__LINE__,'debug', "port file $file, line $line_number, port $external_port"); # setup shell command if(! $jack->{clients}->{$external_port}){ throw($track->name, qq(: port "$external_port" not found. Skipping.)); next } # ecasound port index my $index = $track->width == 1 ? 1 : $line_number % $track->width + 1; my @ports = map{quote($_)} $external_port, $ecasound_port.$index; $code->( $direction eq 'in' ? @ports : reverse @ports ); $line_number++; }; } @$tracks } sub kill_jack_plumbing { qx(killall jack-plumbing >/dev/null 2>&1) unless $config->{opts}->{A} or $config->{opts}->{J}; } sub start_jack_plumbing { if ( $config->{use_jack_plumbing} # not disabled in namarc and ! ($config->{opts}->{J} or $config->{opts}->{A}) # we are not testing ){ system('jack-plumbing >/dev/null 2>&1 &') == 0 or die "can't run jack-plumbing: $?" } } sub port_mapping { my $jack_port = shift; my $own_port; #..... $own_port } sub register_other_ports { return unless $jack->{jackd_running}; $jack->{is_other_port} = { map{ chomp; $_ => 1 } qx(jack_lsp) } } sub register_own_ports { # distinct from other Nama instances return unless $jack->{jackd_running}; $jack->{is_own_port} = { map{chomp; $_ => 1} grep{ ! $jack->{is_other_port}->{$_} } grep{ /^$config->{ecasound_jack_client_name}/ } qx(jack_lsp) } } 1; __END__ Audio-Nama-1.216/lib/Audio/Nama/WavModify.pm0000644000175000017500000000134413544212613017425 0ustar jrothjrothpackage Audio::Nama::WavModify; use Role::Tiny; use Modern::Perl; use Audio::Nama::Globals qw(PLAY); sub normalize { my $track = shift; if ($track->rec_status ne PLAY){ Audio::Nama::throw($track->name, ": You must set track to PLAY before normalizing, skipping.\n"); return; } # track version will exist if PLAY status my $cmd = 'ecanormalize '; $cmd .= $track->full_path; Audio::Nama::pager("executing: $cmd\n"); system $cmd; } sub fixdc { my $track = shift; if ($track->rec_status ne PLAY){ Audio::Nama::throw($track->name, ": You must set track to PLAY before fixing dc level, skipping.\n"); return; } my $cmd = 'ecafixdc '; $cmd .= $track->full_path; Audio::Nama::pager("executing: $cmd\n"); system $cmd; } 1;Audio-Nama-1.216/lib/Audio/Nama/Midi.pm0000644000175000017500000001232213544212613016400 0ustar jrothjroth# ------------- MIDI routines ----------- package Audio::Nama; use Modern::Perl; #use Audio::Nama::Log qw(logpkg); use Carp; { my ($pid, $sel); my @handles = my ($fh_midi_write, $fh_midi_read, $fh_midi_error) = map{ IO::Handle->new() } 1..3; map{ $_->autoflush(1) } @handles; sub start_midish_process { logsub('&start_midish_process'); my $executable = qx(which midish); chomp $executable; $executable or say("Midish not found!"), return; $pid = open3($fh_midi_write, $fh_midi_read, $fh_midi_error,"$executable -v") or warn "Midish failed to start!"; $sel = IO::Select->new(); $sel->add($fh_midi_read); $sel->add($fh_midi_error); midish_cmd( qq(print "Midish is ready.") ); write_aux_midi_commands(); midish_cmd( q(exec ").$file->aux_midi_commands.q(") ); $pid } sub midish_cmd { my $command = shift; logsub('&midish'); return unless $config->{use_midi}; print $fh_midi_write "$command\n"; #say "applied midish command: $command"; $project->{midi_history} //=[]; push @{ $project->{midi_history} },$command; my $length = 2**16; sleeper(0.05); my @result; foreach my $h ($sel->can_read) { my $buf = ''; if ($h eq $fh_midi_error) { sysread($fh_midi_error,$buf,$length); if($buf){print "MIDISH ERR-> $buf\n"} } else { sysread($fh_midi_read,$buf,$length); if($buf){push @result, grep{ !/\+ready/ } split "\n", $buf} } } join "\n", @result; } sub close_midish { save_midish(); say "reaping midish"; kill_and_reap($pid); } sub save_midish { my $fname = $file->midi_store; midish_cmd( qq); } sub reconfigure_midi { add_midi_track($config->{midi_record_buffer}, n => 999, hide => 1) if not $tn{$config->{midi_record_buffer}} and $this_track->current_midi and $this_track->rec; my $midi_rec = $tn{$config->{midi_record_buffer}}; # mute all my @all = $bn{Midi}->track_o; $_->mute for @all; # same as setting OFF for MidiTracks # unmute audible my @audible = grep{ $_->play } @all; $_->unmute for @audible; # unset filters do { $_->select_track; midish_cmd("fdel ".$_->name) } for @all; # set filters for PLAY and MON tracks do { $_->select_track; midish_cmd(join ' ', 'rnew', $_->source_id, $_->send_id) } for @audible; my ($rec) = my @rec = $en{midish}->rec_tracks; # maybe we're done? return unless @rec; throw("more than one midi REC track ", join " ", map{$_->name} @rec), return if @rec > 1; # mute the actual track since we'll record using the special-purpose track $rec->mute; $midi_rec->select_track; # use routing of target track on $midi_rec track my $cmd = 'rnew'; $cmd = join ' ', $cmd, $rec->source_id, $rec->send_id; midish_cmd($cmd); } sub start_midi_transport { my $start_command = $en{midish}->rec_tracks ? 'r' : 'p'; midish_cmd($start_command); $setup->{midish_running}++; } sub stop_midi_transport { return unless midish_running(); midish_cmd('s'); delete $setup->{midish_running}; } sub midi_rec_cleanup { my ($track) = $en{midish}->rec_tracks; # midish allows one recording track defined $track or return; my $length = midish_cmd('print [mend]'); $length > 0 or return; my $version = $track->current_version; $track->set_version($version); push @{$track->midi_versions}, $version; $track->set(rw => PLAY); my $cmd = join ' ', 'chdup', $config->{midi_record_buffer}, $track->source_id, $track->midi_version; say "cmd: $cmd"; midish_cmd($cmd); midish_cmd("clr $config->{midi_record_buffer} $length"); $track->unmute(); save_midish(); } } sub write_aux_midi_commands { write_file($file->aux_midi_commands, get_data_section('aux_midi_commands')) unless -e $file->aux_midi_commands } sub add_midi_track { my ($name, @args) = @_; my $track = Audio::Nama::add_track( $name, class => 'Audio::Nama::MidiTrack', group => 'Midi', source_id => 'midi', source_type => 'midi', midi_versions => [], novol => 1, engine_group => $config->{midi_engine_name}, nopan => 1, @args, ); } sub user_midi_tracks { grep { $_->class =~ /Midi/ } grep { ! $_->hide } all_tracks() } =comment chdup aux_recorder dx7 piano tnew synth rnew nord nord # play the nord keyboard sound with the nord keyboard tnew piano rnew tr dx7 # route the tr keyboard to the dx7 synth sound engine tnew aux_recorder rnew nord nord radd tr dx7 # not sure if this works, must recheck my code r s let complete_length = [mend]; 2. clear the auxiliary track clr aux_recorder $complete_length =cut 1; __END__Audio-Nama-1.216/lib/Audio/Nama/Bunch.pm0000644000175000017500000000411513544212613016556 0ustar jrothjroth# ---------------------- Bunch ----------------- # # operate on a list of tracks package Audio::Nama; use Modern::Perl; sub is_bunch { my $name = shift; $bn{$name} or $project->{bunch}->{$name} } { my %set_stat = ( (map{ $_ => 'rw' } qw(rec play mon off) ), map{ $_ => 'rec_status' } qw(REC PLAY MON OFF ) ); sub bunch { my ($bunchname, @tracks) = @_; if (! $bunchname){ Audio::Nama::pager(json_out( $project->{bunch} )); } elsif (! @tracks){ $project->{bunch}->{$bunchname} and pager("bunch $bunchname: @{$project->{bunch}->{$bunchname}}\n") or throw("bunch $bunchname: does not exist.\n"); } elsif (my @mispelled = grep { ! $tn{$_} and ! $ti{$_}} @tracks){ Audio::Nama::throw("@mispelled: mispelled track(s), skipping.\n"); } else { $project->{bunch}->{$bunchname} = [ @tracks ]; } } sub add_to_bunch {} sub bunch_tracks { my $bunchy = shift; my @tracks; if ( my $bus = $bn{$bunchy}){ @tracks = $bus->tracks; } elsif ( $bunchy eq 'bus' ){ logpkg(__FILE__,__LINE__,'debug', "special bunch: bus"); @tracks = grep{ ! $bn{$_} } $bn{$this_bus}->tracks; } elsif ($bunchy =~ /\s/ # multiple identifiers or $tn{$bunchy} or $bunchy !~ /\D/ and $ti{$bunchy}){ logpkg(__FILE__,__LINE__,'debug', "multiple tracks found"); # verify all tracks are correctly named my @track_ids = split " ", $bunchy; my @illegal = grep{ ! track_from_name_or_index($_) } @track_ids; if ( @illegal ){ throw("Invalid track ids: @illegal. Skipping."); } else { @tracks = map{ $_->name} map{ track_from_name_or_index($_)} @track_ids; } } elsif ( my $method = $set_stat{$bunchy} ){ logpkg(__FILE__,__LINE__,'debug', "special bunch: $bunchy, method: $method"); $bunchy = uc $bunchy; @tracks = grep{$tn{$_}->$method eq $bunchy} $bn{$this_bus}->tracks } elsif ( $project->{bunch}->{$bunchy} and @tracks = @{$project->{bunch}->{$bunchy}} ) { logpkg(__FILE__,__LINE__,'debug', "bunch tracks: @tracks"); } else { throw("$bunchy: no matching bunch identifier found") } @tracks; } } sub track_from_name_or_index { /\D/ ? $tn{$_[0]} : $ti{$_[0]} } 1;Audio-Nama-1.216/lib/Audio/Nama/AnalyseLV2.pm0000644000175000017500000001774013544212613017447 0ustar jrothjroth# ----------------- Analyse LV2 Plugins ------------------ # contributed by S. Massy # package Audio::Nama::AnalyseLV2; use Audio::Nama::Log qw(logpkg); # Initialise our global variables: # Store the plugin info: use strict; my %plugin; my %scalepoints; # Path to utilities my $lv2info; my $lv2ls; # Various internals: my $currentport; my @contents; sub _analyse_lv2 { %plugin = (); # Some variables used here. my ($uri) = @_; my $linecount = my $match; $currentport = -1; unless (acquire_lv2($uri)) { $plugin{error} = "Plugin not found."; return \%plugin; } foreach my $line (@contents) { logpkg(__FILE__,__LINE__,'debug',"Parsing $line"); $linecount++; $plugin{general}{uri} = $line if ($linecount == 1); if ($line =~ /^(\t| )+Name\:(\t| )+(.*+)/ && $currentport == -1) { $plugin{general}{name} = $3; } if (($line =~ /^(\t| )+Class\:(\t| )+(.*+)/) && !($line =~ /(\:\/\/)/) ) { $plugin{general}{class} = $3; } if ($line =~ /^(\t| )+Author\:(\t| )+(.*+)/) { $plugin{general}{author} = $3; } if ($line =~ /^(\t| )+Has latency\:(\t| )+(.*+)/) { $plugin{general}{has_latency} = $3; } # Next we embark on port data collection. # ...fffirst acquire current port. if ($line =~ /^(\t| )+file\:\/\/.*\.ttl$/ && ($currentport == -1) ) { chomp($line); $line =~ s/(\t| )+file\:\/\///; $plugin{'general'}{'datafile'} = $line; logpkg(__FILE__,__LINE__,'debug',"datafile: $plugin{'general'}{'datafile'}\n"); } if ($line =~ /(\t| )+Port (\d+)\:$/) { $currentport = $2; logpkg(__FILE__,__LINE__,'debug',"Acquiring info for $currentport\n"); } # type if ($line =~ /lv2core#(.+)Port$/) { $match = $1; if ($match =~ /Input|Output/) { $plugin{$currentport}{iotype} = $match; logpkg(__FILE__,__LINE__,'debug',"IOTYPE $plugin{$currentport}{iotype}\n"); } else { if (exists($plugin{$currentport}{etype})) { $plugin{$currentport}{etype} .= " "; } $plugin{$currentport}{etype} .= $match; logpkg(__FILE__,__LINE__,'debug',"Acquired ETYPE $1 \n"); } } # A special case for events. if ($line =~ /http.+\#(.+)Event$/ ) { $match = $1; if ( exists($plugin{$currentport}{etype}) ) { $plugin{$currentport}{etype} .= ", "; } $plugin{$currentport}{etype} .= $match; } # Name if ($line =~ /(\t| )+Name\:(\t| )+(.+$)/ && ($currentport != -1)) { $plugin{$currentport}{name} = $3; logpkg(__FILE__,__LINE__,'debug',"Port name is $plugin{$currentport}{name}\n"); } # MINVAL/MAXVAL/DEFVAL if ($line =~ /(\t| )+Minimum\:(\t| )+(.+$)/) { $plugin{$currentport}{minval} = $3; logpkg(__FILE__,__LINE__,'debug',"Acquired minval $plugin{$currentport}{minval}\n"); } if ($line =~ /(\t| )+Maximum\:(\t| )+(.+$)/) { $plugin{$currentport}{maxval} = $3; } if ($line =~ /(\t| )+Default\:(\t| )+(.+$)/) { $plugin{$currentport}{defval} = $3; } # Properties if ($line =~ /extportinfo#(.+$)/) { if (exists($plugin{$currentport}{props})) { $plugin{$currentport}{props} .= ", "; } $plugin{$currentport}{props} .= $1; } if ($currentport != -1 && $line =~ /Scale Points\:/) { $plugin{$currentport}{scalepoints} = 0; } if ($line =~ /(\t+| +)+(-?\d+\.?\d*) = \"(.*)\"$/ && exists($plugin{$currentport}{scalepoints})) { $plugin{$currentport}{scalepoints}++; $scalepoints{$currentport}{$2} = $3; } } $plugin{general}{maxport} = $currentport; $currentport = -1; # We iterate over the ports to add the selector property. for ($currentport = 0; $currentport <= $plugin{general}{maxport}; $currentport++) { if (exists($plugin{$currentport}{scalepoints})) { if (exists($plugin{$currentport}{props})) { $plugin{$currentport}{props} .= ", "; } $plugin{$currentport}{props} .= $plugin{$currentport}{scalepoints} . "-way Selector"; } } # Gather info from datafile proc_datafile($plugin{'general'}{'datafile'}); return (\%plugin, \%scalepoints); } # end of sub crunch sub stripzeros { my ($value) = @_; $value =~ s/\.0+$|0+$//; return $value; } sub generateportinfo { my $portinfo; $portinfo .= "\"$plugin{$currentport}{'name'}"; # For units if (exists($plugin{$currentport}{'units'})) { $portinfo .= " (" . cunits($plugin{$currentport}{'units'}) . ")"; } $portinfo .= "\" "; $portinfo .= "$plugin{$currentport}{iotype}, "; $portinfo .= "$plugin{$currentport}{etype}"; $portinfo .= ", " . &stripzeros($plugin{$currentport}{minval}) if exists($plugin{$currentport}{minval}); $portinfo .= " to " . &stripzeros($plugin{$currentport}{maxval}) if exists($plugin{$currentport}{maxval}); $portinfo .= ", default " . &stripzeros($plugin{$currentport}{defval}) if (exists($plugin{$currentport}{defval}) && $plugin{$currentport}{defval} ne "nan"); $portinfo .= ", " . filterprops($plugin{$currentport}{props}) if (exists($plugin{$currentport}{props}) && filterprops($plugin{$currentport}{props}) ne ""); $portinfo .= "\n"; return $portinfo; } sub filterprops { # Try to limit output my ($props) = @_; # Cut HasStrictBounds is long, uuuuuuseless?, and not in ladspa $props =~ s/, hasStrictBounds|hasStrictBounds, |hasStrictBounds//; # Don't just leave a comma and space $props =~ s/^, $|^ +$//; logpkg(__FILE__,__LINE__,'debug',"props: $props\n"); return $props;; } sub print_lv2 { my @buffer; push @buffer, "Name: $plugin{general}{name}\n" if exists($plugin{general}{name}); push @buffer, "URI: $plugin{general}{uri}"; push @buffer, "Class: $plugin{general}{class}\n" if exists($plugin{general}{class}); push @buffer, "Author: $plugin{general}{author}\n" if exists($plugin{general}{author}); push @buffer, "Latency: $plugin{general}{has_latency}\n" if exists($plugin{general}{has_latency}); for ($currentport = 0; $currentport <= $plugin{general}{maxport}; $currentport++) { if ($currentport == 0) { push @buffer, "Ports: "; } else { push @buffer, "\t"; } push @buffer, generateportinfo(); } push @buffer, "\n"; return @buffer; } sub acquire_lv2 { my ($uri) = @_; @contents = `$lv2info $uri`; logpkg(__FILE__,__LINE__,'debug',"Acquiring contents for $uri\n"); # logpkg(__FILE__,__LINE__,'debug',"$contents[0]\n"; return 0 if ($contents[0] eq ""); return 1; } sub find_utils { my $output; $output = `which lv2info`; chomp($output); if ( $output =~ /^\/.+lv2info$/ ) { $lv2info = $output;; } else { return 0; } $output = `which lv2ls`; chomp($output); if ( $output =~ /^\/.+lv2ls$/ ) { $lv2ls = $output; } else { return 0; } return 1; } sub trymatch { my ($string) = @_; my @lv2lsoutput = `$lv2ls`; my @results; foreach my $uline (@lv2lsoutput) { chomp($uline); push(@results, ($uline)) if ($uline =~ /$string/i); } return @results; } sub print_lv2_scalepoints { my @buffer; if (keys(%scalepoints) > 0) { push @buffer, "Printing full information for ports with scale points in plugin...\n$plugin{general}{name}\n"; foreach my $port (sort {$a <=> $b} (keys(%scalepoints))) { $currentport = $port; push @buffer, "Port $currentport: " . generateportinfo(); foreach my $point ( sort {$a <=> $b} (keys(%{ $scalepoints{$currentport} })) ) { push @buffer, "\t $point \= $scalepoints{$currentport}{$point}\n"; } } } else { push @buffer, "Plugin $plugin{general}{name} does not have any port with scale points.\n\n"; } return @buffer; } sub analyse_lv2 { my ($uri) = @_; if ( find_utils() ) { return _analyse_lv2($uri); } else { $plugin{error} = "Utilities not found."; return \%plugin; } } sub lv2_help { my $uri = shift; find_utils(); analyse_lv2($uri); print_lv2(); } #print lv2_help('http://plugin.org.uk/swh-plugins/zm1'); #print lv2_help('urn:50m30n3:plugins:SO-404'); sub proc_datafile { my ($file) = @_; open(my $fh, "<", $file) || return 0; $currentport = -1; while (my $curline = <$fh>) { if ($curline =~ /lv2\:index +(\d+) *;$/ ) { $currentport = $1; } if ($curline =~ /ue\:unit +ue\:([a-zA-Z0-9_]+) *;$/ && ($currentport != -1)) { $plugin{$currentport}{'units'} = $1; } } close($fh); $currentport = -1; return 1; } sub cunits { (my $units) = @_; $units =~ s/pc/\%/ if $units =~ /pc/; return $units; } 1;Audio-Nama-1.216/lib/Audio/Nama/Mark.pm0000644000175000017500000001567213544212613016423 0ustar jrothjroth # ----------- Mark ------------ package Audio::Nama::Mark; our $VERSION = 1.0; use Carp; use warnings; no warnings qw(uninitialized); our @ISA; use vars qw($n %by_name @all); use Audio::Nama::Log qw(logpkg); use Audio::Nama::Globals qw(:all); use Audio::Nama::Object qw( name time active ); sub initialize { map{ $_->remove} Audio::Nama::Mark::all(); @all = (); %by_name = (); # return ref to Mark by name $by_name{Here} = bless {}, 'Audio::Nama::HereMark'; @Audio::Nama::marks_data = (); # for save/restore } sub next_id { # returns incremented 4-digit $project->{mark_sequence_counter} ||= '0000'; $project->{mark_sequence_counter}++ } sub new { my $class = shift; my %vals = @_; croak "undeclared field: @_" if grep{ ! $_is_field{$_} } keys %vals; # to support set_edit_points, we now allow marks to be overwritten # #croak "name already in use: $vals{name}\n" # if $by_name{$vals{name}}; # null name returns false my $self = bless { ## defaults ## active => 1, name => "", @_ }, $class; #print "self class: $class, self type: ", ref $self, $/; if ($self->name) { $by_name{ $self->name } = $self; } push @all, $self; $Audio::Nama::this_mark = $self; $self; } sub set_name { my $mark = shift; my $name = shift; pager("name: $name\n"); if ( defined $by_name{ $name } ){ carp "you attempted to assign to name already in use\n"; } else { $mark->set(name => $name); $by_name{ $name } = $mark; } } sub jump_here { my $mark = shift; Audio::Nama::set_position($mark->time); $Audio::Nama::this_mark = $mark; } sub shifted_time { # for marks within current edit my $mark = shift; return $mark->time unless $mode->{offset_run}; my $time = $mark->time - Audio::Nama::play_start_time(); $time > 0 ? $time : 0 } sub remove { my $mark = shift; Audio::Nama::throw('Fades depend on this mark. Remove failed.'), return if Audio::Nama::fade_uses_mark($mark->name); if ( $mark->name ) { delete $by_name{$mark->name}; } @all = grep { $_->time != $mark->time } @all; } sub next { my $mark = shift; Audio::Nama::next_mark(); } sub previous { my $mark = shift; Audio::Nama::previous_mark(); } # -- Class Methods sub all { sort { $a->{time} <=> $b->{time} }@all } sub loop_start { my @points = sort { $a <=> $b } grep{ $_ } map{ mark_time($_)} @{$setup->{loop_endpoints}}[0,1]; #print "points @points\n"; $points[0]; } sub loop_end { my @points =sort { $a <=> $b } grep{ $_ } map{ mark_time($_)} @{$setup->{loop_endpoints}}[0,1]; $points[1]; } sub time_from_tag { my $tag = shift; $tag or $tag = ''; #print "tag: $tag\n"; my $mark; if ($tag =~ /\./) { # we assume raw time if decimal #print "mark time: ", $tag, $/; return $tag; } elsif ($tag =~ /^\d+$/){ #print "mark index found\n"; $mark = $Audio::Nama::Mark::all[$tag]; } else { #print "mark name found\n"; $mark = $Audio::Nama::Mark::by_name{$tag}; } return undef if ! defined $mark; #print "mark time: ", $mark->time, $/; return $mark->time; } sub duration_from_tag { my $tag = shift; $tag or $tag = ''; #print "tag: $tag\n"; my $mark; if ($tag =~ /[\d.-]+/) { # we assume time #print "mark time: ", $tag, $/; return $tag; } else { #print "mark name found\n"; $mark = $Audio::Nama::Mark::by_name{$tag}; } return undef if ! defined $mark; #print "mark time: ", $mark->time, $/; return $mark->time; } sub mark_time { my $tag = shift; my $time = time_from_tag($tag); return unless defined $time; $time -= Audio::Nama::play_start_time() if $mode->{offset_run}; $time } # ---------- Mark and jump routines -------- { package Audio::Nama; use Modern::Perl; use Audio::Nama::Globals qw(:all); sub drop_mark { logsub("&drop_mark"); my $name = shift; my $here = ecasound_iam("getpos"); if( my $mark = $Audio::Nama::Mark::by_name{$name}){ pager("$name: a mark with this name exists already at: ", colonize($mark->time)); return } if( my ($mark) = grep { $_->time == $here} Audio::Nama::Mark::all()){ pager( q(This position is already marked by "),$mark->name,q(") ); return } my $mark = Audio::Nama::Mark->new( time => $here, name => $name); $ui->marker($mark); # for GUI } sub mark { # GUI_CODE logsub("&mark"); my $mark = shift; my $pos = $mark->time; if ($gui->{_markers_armed}){ $ui->destroy_marker($pos); $mark->remove; arm_mark_toggle(); # disarm } else{ set_position($pos); } } sub next_mark { logsub("&next_mark"); my $jumps = shift || 0; $jumps and $jumps--; my $here = ecasound_iam("cs-get-position"); my @marks = Audio::Nama::Mark::all(); for my $i ( 0..$#marks ){ if ($marks[$i]->time - $here > 0.001 ){ logpkg(__FILE__,__LINE__,'debug', "here: $here, future time: ", $marks[$i]->time); set_position($marks[$i+$jumps]->time); $this_mark = $marks[$i]; return; } } } sub previous_mark { logsub("&previous_mark"); my $jumps = shift || 0; $jumps and $jumps--; my $here = ecasound_iam("getpos"); my @marks = Audio::Nama::Mark::all(); for my $i ( reverse 0..$#marks ){ if ($marks[$i]->time < $here ){ set_position($marks[$i+$jumps]->time); $this_mark = $marks[$i]; return; } } } ## jump recording head position sub to_start { logsub("&to_start"); return if Audio::Nama::ChainSetup::really_recording(); set_position( 0 ); } sub to_end { logsub("&to_end"); # ten seconds shy of end return if Audio::Nama::ChainSetup::really_recording(); my $end = ecasound_iam('cs-get-length') - 10 ; set_position( $end); } sub jump { return if Audio::Nama::ChainSetup::really_recording(); my $delta = shift; logsub("&jump"); my $here = ecasound_iam('getpos'); logpkg(__FILE__,__LINE__,'debug', "delta: $delta, here: $here, unit: $gui->{_seek_unit}"); my $new_pos = $here + $delta * $gui->{_seek_unit}; if ( $setup->{audio_length} ) { $new_pos = $new_pos < $setup->{audio_length} ? $new_pos : $setup->{audio_length} - 10 } set_position( $new_pos ); } sub set_position { fade_around(\&_set_position, @_) } sub _set_position { logsub("&set_position"); return if Audio::Nama::ChainSetup::really_recording(); # don't allow seek while recording my $seconds = shift; my $coderef = sub{ ecasound_iam("setpos $seconds") }; $jack->{jackd_running} ? Audio::Nama::stop_do_start( $coderef, $jack->{seek_delay} ) : $coderef->(); update_clock_display(); } sub forward { my $delta = shift; my $here = ecasound_iam('getpos'); my $new = $here + $delta; set_position( $new ); } sub rewind { my $delta = shift; forward( -$delta ); } sub jump_forward { my $multiplier = shift; forward( $multiplier * $text->{hotkey_playback_jumpsize}) } sub jump_backward { jump_forward( - shift()) } } # end package { package Audio::Nama::HereMark; our @ISA = 'Audio::Nama::Mark'; our $last_time; sub name { 'Here' } sub time { Audio::Nama::ecasound_iam('cs-connected') ? ($last_time = Audio::Nama::ecasound_iam('getpos')) : $last_time } } { package Audio::Nama::ClipMark; use Modern::Perl; our @ISA = 'Audio::Nama::Mark'; } 1; __END__Audio-Nama-1.216/lib/Audio/Nama/Terminal.pm0000644000175000017500000002331313544212613017273 0ustar jrothjroth# ----------- Terminal related subroutines --------- package Audio::Nama; use Modern::Perl; no warnings 'uninitialized'; use Carp; use Audio::Nama::Globals qw(:singletons $this_bus $this_track); use Audio::Nama::Log qw(logpkg logsub); use Data::Dumper::Concise; use List::MoreUtils qw(first_index); sub initialize_prompt { $text->{term}->stuff_char(10); # necessary to respond to Ctrl-C at first prompt &{$text->{term_attribs}->{'callback_read_char'}}(); set_current_bus(); print prompt(); $text->{term_attribs}->{already_prompted} = 0; } sub initialize_terminal { $text->{term} = new Term::ReadLine("Ecasound/Nama"); $text->{term_attribs} = $text->{term}->Attribs; $text->{term_attribs}->{attempted_completion_function} = \&complete; $text->{term_attribs}->{already_prompted} = 1; ($text->{screen_lines}, $text->{screen_columns}) = $text->{term}->get_screen_size(); logpkg(__FILE__,__LINE__,'debug', "screensize is $text->{screen_lines} lines x $text->{screen_columns} columns"); detect_spacebar(); revise_prompt(); # handle Control-C from terminal #$SIG{INT} = \&cleanup_exit; # responds in a more timely way than abovce $project->{events}->{sigint} = AE::signal('INT', \&cleanup_exit); $SIG{USR1} = sub { git_snapshot() }; } sub setup_hotkeys { say "\nHotkeys on!"; destroy_readline(); setup_termkey(); 1 } sub list_hotkeys { my $hots = dclone($config->{hotkeys}); my %hots = %$hots; $hots{'='} = 'Enter numeric value'; $hots{ 'mN' } = 'Change step size to 10 raised to the Nth power'; $hots{ '#' } = 'Engage hotkey mode (must be typed in column 1)'; pager("Hotkeys\n",Dumper \%hots) } sub setup_termkey { $project->{events}->{termkey} = AnyEvent::TermKey->new( term => \*STDIN, on_key => sub { my $key = shift; my $key_string = $key->termkey->format_key( $key, FORMAT_VIM ); logpkg(__FILE__,__LINE__,'debug',"got key: $key_string"); # remove angle brackets around multi-character # sequences, e.g. -> PageUp $key_string =~ s/[<>]//g if length $key_string > 1; exit_hotkey_mode(), cleanup_exit() if $key->type_is_unicode and $key->utf8 eq "C" and $key->modifiers & KEYMOD_CTRL; # execute callback if we have one keystroke # and it has an "instant" mapping my $suppress_status; $key_string =~ s/ /Space/; # to suit our mapping file if ( my $command = $config->{hotkeys}->{$key_string} and ! length $text->{hotkey_buffer}) { $suppress_status++ if $key_string eq 'Escape' or $key_string eq 'Space'; try { eval "$command()" } catch { throw( qq(cannot execute subroutine "$command" for key "$key_string": $_") ) } } # otherwise assemble keystrokes and check # them against the grammar else { $key_string =~ s/Space/ /; # back to the character $text->{hotkey_buffer} .= $key_string; print $key_string if length $key_string == 1; # push $text->{hotkey_object_buffer}, $key; $text->{hotkey_parser}->command($text->{hotkey_buffer}) and reset_hotkey_buffers(); } print( "\x1b[$text->{screen_lines};0H", # go to screen bottom line, column 0 "\x1b[2K", # erase line hotkey_status_bar(), ) if $text->{hotkey_buffer} eq undef and ! $suppress_status; }, ); } sub hotkey_status_bar { my $name = "[".$this_track->name."]"; return "$name has no selected effect" unless $this_track->op; join " ", $name, "Stepsize: ",$this_track->stepsize, fxn($this_track->op)->fxname, parameter_info($this_track->op, $this_track->param - 1); ; } sub reset_hotkey_buffers { $text->{hotkey_buffer} = ""; $text->{hotkey_object_buffer} = []; } sub exit_hotkey_mode { teardown_hotkeys(); initialize_terminal(); initialize_prompt(); }; sub teardown_hotkeys { $project->{events}->{termkey}->termkey->stop(), delete $project->{events}->{termkey} if $project->{events}->{termkey} } sub destroy_readline { $text->{term}->rl_deprep_terminal() if $text->{term}; delete $text->{term}; delete $project->{events}->{stdin}; } sub setup_hotkey_grammar { $text->{hotkey_grammar} = get_data_section('hotkey_grammar'); $text->{hotkey_parser} = Parse::RecDescent->new($text->{hotkey_grammar}) or croak "Bad grammar!\n"; } sub end_of_list_sound { system( $config->{hotkey_beep} ) } sub previous_track { end_of_list_sound(), return if $this_track->n == 1; do{ $this_track = $ti{$this_track->n - 1} } until ! $this_track->hide; } sub next_track { end_of_list_sound(), return if ! $ti{ $this_track->n + 1 }; do{ $this_track = $ti{$this_track->n + 1} } until ! $this_track->hide; } sub previous_effect { my $op = $this_track->op; my $pos = $this_track->pos; end_of_list_sound(), return if $pos == 0; $pos--; set_current_op($this_track->ops->[$pos]); } sub next_effect { my $op = $this_track->op; my $pos = $this_track->pos; end_of_list_sound(),return if $pos == scalar @{ $this_track->ops } - 1; $pos++; set_current_op($this_track->ops->[$pos]); } sub previous_param { my $param = $this_track->param; $param > 1 ? set_current_param($this_track->param - 1) : end_of_list_sound() } sub next_param { my $param = $this_track->param; $param < scalar @{ fxn($this_track->op)->params } ? $project->{current_param}->{$this_track->op}++ : end_of_list_sound() } {my $override; sub revise_prompt { logsub('&revise_prompt'); # hack to allow suppressing prompt $override = ($_[0] eq "default" ? undef : $_[0]) if defined $_[0]; $text->{term}->callback_handler_install($override//prompt(), \&process_line) if $text->{term} } } sub prompt { logsub('&prompt'); join ' ', 'nama', git_branch_display(), bus_track_display() ," ('h' for help)> " } sub detect_spacebar { # create a STDIN watcher to intervene when space # received in column one $project->{events}->{stdin} = AE::io(*STDIN, 0, sub { &{$text->{term_attribs}->{'callback_read_char'}}(); my $buffer = $text->{term_attribs}->{line_buffer}; my $trigger = ' '; if ( $config->{press_space_to_start} and ($buffer eq $trigger) and ! ($mode->song or $mode->live) ) { toggle_transport(); # reset command line, read next char $text->{term_attribs}->{line_buffer} = q(); $text->{term_attribs}->{point} = 0; $text->{term_attribs}->{end} = 0; $text->{term}->stuff_char(10); &{$text->{term_attribs}->{'callback_read_char'}}(); } elsif ( $text->{term_attribs}->{line_buffer} eq "#" ){ setup_hotkeys(); } }); } sub throw { logsub("&throw"); pager_newline(@_) } sub pagers { &pager_newline(join "",@_) } # pass arguments along sub pager_newline { # Add a newline if necessary to each line # push them onto the output buffer # print them to the screen my @lines = @_; for (@lines){ $_ .= "\n" if ! /\n$/ } push @{$text->{output_buffer}}, @lines; print(@lines); } sub paging_allowed { # The pager interferes with GUI and testing # so do not use the pager in these conditions # or if use_pager config variable is not set. $config->{use_pager} and ! $config->{opts}->{T} } sub pager { # push array onto output buffer, add two newlines # and print on terminal or view in pager # as appropriate logsub("&pager"); my @output = @_; @output or return; chomp $output[-1]; $output[-1] .= "\n\n"; push @{$text->{output_buffer}}, @output; page_or_print(@output); 1 } sub init_output_buffer { $text->{output_buffer} //= [] }; sub linecount { my @output = @_; my $linecount = 0; for (@output){ $linecount += $_ =~ tr(\n)(\n) } $linecount } sub page_or_print { my (@output) = @_; @output = map{"$_\n"} map{ split "\n"} @output; return unless scalar @output; print(@output), return if !paging_allowed() or scalar(@output) <= $text->{screen_lines} - 2; write_to_temp_file_and_view(@output) } sub write_to_temp_file_and_view { my @output = @_; my $fh = File::Temp->new(); my $fname = $fh->filename; print $fh @output; file_pager($fname); } sub file_pager { # given a filename, run the pager on it logsub("&file_pager"); my $fname = shift; if (! -e $fname or ! -r $fname ){ carp "file not found or not readable: $fname\n" ; return; } my $pager = $ENV{PAGER} || "/usr/bin/less"; $pager =~ /less/ and $pager .= qq( -M -i -PM"q=quit pager, /=search, PgUp/PgDown=scroll (line %lt/%L)"); my $cmd = qq($pager $fname); system $cmd; } 1; # command line processing routines sub get_ecasound_iam_keywords { my %reserved = map{ $_,1 } qw( forward fw getpos h help rewind quit q rw s setpos start stop t ? ); %{$text->{iam}} = map{$_,1 } grep{ ! $reserved{$_} } split /[\s,]/, ecasound_iam('int-cmd-list'); } sub load_keywords { my @keywords = keys %{$text->{commands}}; # complete hyphenated forms as well my %hyphenated = map{my $h = $_; $h =~ s/_/-/g; $h => $_ }grep{ /_/ } @keywords; $text->{hyphenated_commands} = \%hyphenated; push @keywords, keys %hyphenated; push @keywords, grep{$_} map{split " ", $text->{commands}->{$_}->{short}} @keywords; push @keywords, keys %{$text->{iam}}; push @keywords, keys %{$fx_cache->{partial_label_to_full}}; push @keywords, keys %{$text->{midi_cmd}} if $config->{use_midi}; push @keywords, "Audio::Nama::"; @{$text->{keywords}} = @keywords } sub complete { my ($string, $line, $start, $end) = @_; #print join $/, $string, $line, $start, $end, $/; my $term = $text->{term}; return $term->completion_matches($string,\&keyword); }; sub keyword { state $i; my ($string, $state) = @_; return unless $text; if($state) { $i++; } else { # first call $i = 0; } for (; $i<=$#{$text->{keywords}}; $i++) { return $text->{keywords}->[$i] if $text->{keywords}->[$i] =~ /^\Q$string/; }; return undef; }; 1; __END__Audio-Nama-1.216/lib/Audio/Nama/Custom.pm0000644000175000017500000000245313544212613016774 0ustar jrothjroth# ---------------- User Customization --------------- package Audio::Nama; use Modern::Perl; sub setup_user_customization { my $filename = $file->user_customization(); # effect aliases from .namarc for( keys %{$config->{alias}->{effect}} ) { my $longform = $config->{alias}->{effect}->{$_}; if(effect_index($longform)) { $fx_cache->{partial_label_to_full}->{$_} = $fx_cache->{partial_label_to_full}->{$longform} } else { throw("$longform: effect not found, cannot create shortcut") } } return unless -r $filename; say("reading user customization file $filename"); my %custom; unless (%custom = do $filename) { throw("couldn't parse $filename: $@\n") if $@; return; } logpkg(__FILE__,__LINE__,'debug','customization :', sub{Dumper \%custom }); my $prompt; { no warnings 'redefine'; *prompt = $custom{prompt} if $custom{prompt}; } my @commands = keys %{ $custom{commands} }; for my $cmd(@commands){ #my $coderef = gen_coderef($cmd,$custom{commands}{$cmd}) or next; $text->{user_command}->{$cmd} = $custom{commands}{$cmd}; } $config->{alias} = $custom{aliases}; } sub gen_coderef { my ($cmd,$code) = @_; my $coderef = eval "sub{ use feature ':5.10'; no warnings 'uninitialized'; $code }"; throw("couldn't parse command $cmd: $@"), return if $@; $coderef } 1;Audio-Nama-1.216/lib/Audio/Nama/TrackWaveform.pm0000644000175000017500000000063113544212613020271 0ustar jrothjrothpackage Audio::Nama::TrackWaveform; use Audio::Nama::Globals qw($project $config $gui %ti); use Modern::Perl; use Role::Tiny; use Try::Tiny; sub waveform { my $self = shift; Audio::Nama::Waveform->new( project => $self->project, wav => $self->current_wav, start => $self->region_start_time, end => $self->region_end_time, track => $self, ); } 1 # obligatory __END__Audio-Nama-1.216/lib/Audio/Nama/EcasoundCleanup.pm0000644000175000017500000001227413544212613020575 0ustar jrothjroth# ----------- Ecasound cleanup (post-recording) ----------- package Audio::Nama::EcasoundCleanup; use Role::Tiny; use Modern::Perl; sub cleanup { my $self = shift; Audio::Nama::rec_cleanup(); } package Audio::Nama; use Modern::Perl; use Cwd; use Audio::Nama::Globals qw(:all); sub rec_cleanup { logsub("&rec_cleanup"); logpkg(__FILE__,__LINE__,'debug',"transport still running, can't cleanup"), return if $this_engine->running; if( my (@files) = new_files_were_recorded() ) { if( my @rec_tracks = Audio::Nama::ChainSetup::engine_wav_out_tracks() ) { $project->{playback_position} = 0; $setup->{_last_rec_tracks} = \@rec_tracks; pager(join " ", "Files recorded for these tracks:", map{ $_->current_wav } @rec_tracks); } if( grep /Mixdown/, @files) { mixdown_postprocessing() ; mixplay(); } post_rec_configure() } } sub mixdown_postprocessing { logsub("&mixdown_postprocessing"); nama_cmd('mixplay'); my ($oldfile) = $tn{Mixdown}->full_path =~ m{([^/]+)$}; $oldfile = join_path('.wav',$oldfile); my $tag_name = join '-', $project->{name}, current_branch(); my $version = $tn{Mixdown}->playback_version; # simplify the tagname basename # # untitled-master -> untitled # untitled-premix-branch -> untitled-premix $tag_name =~ s/-branch$//; $tag_name =~ s/-master$//; $tag_name .= "_$version"; delete_existing_mixdown_tag_and_convenience_encodings($tag_name); # create symlink in project_dir() my $was_in = getcwd; chdir project_dir() or die "couldn't chdir: $!"; my $newfile = "$tag_name.wav"; logpkg(__FILE__,__LINE__,'debug',"symlinking oldfile: $oldfile, newfile: $newfile"); symlink $oldfile, $newfile or throw("symlink didn't work: $!"); tag_mixdown_commit($tag_name, $newfile, $oldfile) if $config->{use_git}; my $sha = git_sha(); # possibly undef my $encoding = $config->{mixdown_encodings}; my $comment; if ($sha or $encoding){ $comment .= "tagged " if $sha; $comment .= "and " if $sha and $encoding; $comment .= "encoded " if $encoding; $comment .= "as $tag_name "; $comment .= "(commit $sha)" if $sha; } $tn{Mixdown}->add_system_version_comment($version, $comment); pager_newline($comment); encode_mixdown_file($oldfile,$tag_name); chdir $was_in; } sub tag_mixdown_commit { logsub('&tag_mixdown_commit'); my ($name, $newfile, $mixdownfile) = @_; logpkg(__FILE__,__LINE__,'debug',"tag_mixdown_commit: @_"); my ($sym) = $newfile =~ m([^/]+$); my ($mix) = $mixdownfile =~ m([^/]+$); # we want to tag the normal playback state local $quiet = 1; mixoff(); my $msg = "State for $sym ($mix)"; git_snapshot($msg); git('tag', $name, '-m', $mix); } sub delete_existing_mixdown_tag_and_convenience_encodings { logsub('&delete_existing_mixdown_tag_and_convenience_encodings'); my $name = shift; logpkg(__FILE__,__LINE__,'debug',"name: $name"); git('tag', '-d', $name); foreach( qw(mp3 ogg wav) ){ my $file = join_path(project_dir(),"$name.$_"); unlink $file if -e $file; } } sub encode_mixdown_file { state $shell_encode_command = { mp3 => q(lame -h --ta "$artist" --ty $year --tt "$title" $input_file $output_file), ogg => q(oggenc -o $output_file -a "$artist" -t "$title" -d "$date" $input_file) }; my($mixdownfile, $tag_name, @formats) = @_; @formats or @formats = split " ", $config->{mixdown_encodings}; logpkg(__FILE__,__LINE__,'debug',"formats: @formats"); my $artist = $project->{artist} || qx(whoami); my $title = $project->{name}; my $date = qx(date); chomp($date, $artist); my ($year) = $date =~ /(\d{4})$/; my $input_file = $mixdownfile; for my $format( @formats ){ my $output_file = join_path(project_dir(),"$tag_name.$format"); logpkg(__FILE__,__LINE__,'debug',"artist $artist, title $title, date $date, year $year, input file $input_file, output file $output_file"); my $cmd = eval qq(qq($shell_encode_command->{$format})); logpkg(__FILE__,__LINE__,'debug',"Mixdown encoding command:\n$cmd"); system $cmd; } } sub adjust_offset_recordings { for( Audio::Nama::ChainSetup::engine_wav_out_tracks()){ no warnings 'uninitialized'; if (my $mark = $setup->{offset_run}->{mark}){ $_->set(playat => $mark); logpkg(__FILE__,__LINE__,'debug',$_->name, ": offsetting to $mark"); } } } sub post_rec_configure { $ui->global_version_buttons(); # recreate adjust_offset_recordings(); # toggle recorded tracks to PLAY for auditioning map{ $_->set(rw => PLAY) } @{$setup->{_last_rec_tracks}}; undef $mode->{offset_run} if ! defined $this_edit; $ui->refresh(); request_setup(); reconfigure_engine(); } sub new_files_were_recorded { return unless my @files = Audio::Nama::ChainSetup::really_recording(); logpkg(__FILE__,__LINE__,'debug',join $/, "intended recordings:", @files); my @recorded = grep { my ($name, $version) = /([^\/]+)_(\d+).wav$/; if (-e ) { if (-s > 44100) { # 0.5s x 16 bits x 44100/s logpkg(__FILE__,__LINE__,'debug',"File size >44100 bytes: $_"); $tn{$name}->set(version => $version) if $tn{$name}; $ui->update_version_button($tn{$name}->n, $version); 1; } else { unlink $_; 0 } } } @files; if(@recorded){ restart_wav_memoize(); pagers("recorded:", @recorded); } map{ _get_wav_info($_) } @recorded; @recorded } 1; __END__Audio-Nama-1.216/lib/Audio/Nama/Help.pm0000644000175000017500000005762513544212613016425 0ustar jrothjroth# -------------------- Help ---------------------- package Audio::Nama; use Modern::Perl; { no warnings 'uninitialized'; sub helpline { my $cmd = shift; my $out = "Command: $cmd\n"; $out .= "Shortcuts: $text->{commands}->{$cmd}->{short}\n" if $text->{commands}->{$cmd}->{short}; $out .= "Category: $text->{commands}->{$cmd}->{type}\n"; my $what = munge_help($text->{commands}->{$cmd}->{what}); $out .= "Description: $what\n"; $out .= "Usage: $cmd "; if ( $text->{commands}->{$cmd}->{parameters} && $text->{commands}->{$cmd}->{parameters} ne 'none' ){ $out .= $text->{commands}->{$cmd}->{parameters} } $out .= "\n"; my $example = $text->{commands}->{$cmd}->{example}; $example = munge_help($example); #$example =~ s/!n/\n/g; if ($example){ $out .= "Example: "; if ($example =~ /\n/s){ $example = "\n$example"; # add leading newline $example =~ s(\n)(\n )g; # indent } $out .= $example; $out .= "\n"; } ($/, ucfirst $out, $/); } sub munge_help { my $text = shift; $text =~ s/(^\s*)!(\s*#)/$1 $2/mg; $text =~ s/(^\s*!)/#/mg; $text } } sub helptopic { my $i = shift; my $key = $help->{index}->{$i}; format_help_topic($i, $key); } sub format_help_topic { my ($index, $key) = @_; my $title = $help->{display}->[$index]; my @output; push @output, "\n-- $help->{title}->[$index] --\n\n"; push @output, $help->{topic}->{$key}, $/; @output } sub help { my $name = shift; $name =~ s/-/_/g; # help indices require underscores chomp $name; #print "seeking help for argument: $name\n"; $text->{iam}->{$name} and pager(<{topic}->{$name}){ @output = helptopic($name); } elsif ( $name =~ /^(\d+)$/ ){ @output = helptopic($name) } else { my %helped = (); my @help = (); if ( $text->{commands}->{$name} ){ push @help, helpline($name); $helped{$name}++ } map { my $cmd = $_ ; if ($cmd =~ /$name/ ) { push @help, helpline($cmd) unless $helped{$cmd} or $cmd =~ /-/; # skip hyphenated command forms # which lack full help $helped{$cmd}++ ; } no warnings 'uninitialized'; if ( ! $helped{$cmd} and grep{ /$name/ } split " ", $text->{commands}->{$cmd}->{short}) { push @help, helpline($cmd) } } keys %{$text->{commands}}; if ( @help ){ push @output, qq("$name" matches the following commands:\n\n), @help; } } if (@output){ map{ s/_/-/g } @output; Audio::Nama::pager( @output ); } else { throw("$name: no help found.\n"); } } sub help_effect { my ($input, $id, $no_match, @output); $id = $input = shift; push @output, "\n"; # e.g. help tap_reverb # help 2142 # help var_chipmunk # preset # convert digits to LADSPA label if ($id !~ /\D/){ $id = $fx_cache->{ladspa_id_to_label}->{$id} or $no_match++ } # convert ladspa_label to el:ladspa_label # convert preset_name to pn:preset_name if ($fx_cache->{full_label_to_index}->{$id}){} # we are ready elsif ( $fx_cache->{partial_label_to_full}->{$id} ) { $id = $fx_cache->{partial_label_to_full}->{$id} } else { $no_match++ } # one-line help for Ecasound and chain operators, controllers and presets if ($id !~ /^(lv2|el):/) { push @output, grep{ /$id/ } @{$fx_cache->{user_help}}; } # full help for LADSPA/LV2 plugins elsif ( $id =~ /el:/ ) { @output = $fx_cache->{ladspa_help}->{$id} } elsif ( $id =~ /elv2:/) { @output = $fx_cache->{lv2_help}->{$id} } if( $no_match ){ throw("No effects were found matching: $input\n\n"); } else { Audio::Nama::pager(@output) } } sub find_effect { my @keys = @_; #print "keys: @keys\n"; #my @output; my @matches = grep{ my $_help = $_; my $didnt_match; map{ $_help =~ /\Q$_\E/i or $didnt_match++ } @keys; ! $didnt_match; # select if no cases of non-matching } grep{$_} @{$fx_cache->{user_help}}; if ( @matches ){ Audio::Nama::pager( $text->{wrap}->paragraphs(@matches) , "\n" ); } else { throw(join " ", "No effects were found matching:",@keys,"\n\n") } } sub parse_midi_help { my $h = get_data_section("midi_help"); my @lines = split $/, $h; my $j; my $cmd; my %acc; for (@lines) { /(^\w+)/ ? ($cmd = $1, $acc{$cmd} = [], $j++) : (push @{$acc{$cmd}}, $_ ); } $help->{midish} = \%acc; return } ## Initialization @{ $help->{arr_topic} } = qw( project track_basics track_status wav_versions transport track_fader effect_info effect_manipulation marks time_shifting track_io inserts fades group bus mixdown prompt normalization track_caching effect_chains effect_profiles advanced_transport version_control diagnostics edits advanced ) ; my $i; my @display_index = map{ $help->{index}->{++$i} = $_; # integer => topic key s/_/ /g; my $name = ucfirst $_; $help->{title}->[$i] = $name; $name = join " ",$i, $name; $help->{display}->[$i] = $name; } @{ $help->{arr_topic} }; sub pad { my ($text, $len) = @_; my $pad = ' ' x ( $len - length $text); $text.$pad } my @twocolumn = map{ pad($display_index[$_], 22).$display_index[$_+13].$/ } 0..12; %{ $help->{topic} } = ( help => < - show help for help - show help for commands matching // help - info on a LADSPA id help - list commands under HELP project => <<'PROJECT', load-project, load - load an existing project, e.g 'load ' create-project, create - create a new project (usually under $HOME/nama/project-name/) list-projects, lp - list all projects undo - go back in time to the state before the last command redo - reapply the step rolled back by undo exit, quit - exit program, saving state PROJECT track_basics => <<'TRACKBASICS', add-track, add - Create a new track Example: add sax - Add a new track 'sax', mono by default. - Sax is selected, and receives next commands. source 3 - Use soundcard channel 3 as input rec - Arm for recording an audio file (e.g. sax_1.wav) start - Start engine, begin recording stop - Stop engine, close file and queue for playback can be used at the prompt to start and stop engine Example: add piano; source synth; stereo; rec This line of commands prepares to record track 'piano' in stereo width from JACK client 'synth'. Use of semicolons allow several commands in one line of input. import-audio, import - Import a .wav file, resampling if necessary remove-track - Remove effects, parameters and GUI for current track TRACKBASICS track_status => <<'TRACKSTATUS', Prepare conditions for next engine run rec - REC: record and monitor audio source mon - MON: monitor audio source play - PLAY: queue .wav file for playback off - OFF: omit track from audio network show-tracks,show - show status, all tracks show-track,sh - show status of current track, including effects, versions, etc. chains - show the audio network configuration TRACKSTATUS wav_versions => <<'WAV_VERSIONS', list-version, lver - list version numbers of audio files in the current track - (this information also appears in the show-track listing) set-version, version, n - select a version number for the current track A track can record multiple audio files ('takes'). The audio filenames follow the trackname so that recording a track named 'piano' will produce files with names with names like piano_1.wav, piano_2.wav. Audio files recorded by Nama are considered immutable resources and stored outside of version control. One version can be selected as streaming source when when the track is set to PLAY. The version number is zero by default, which means select the most recent (highest numbered) take. The bus-version command will set default version number for all tracks belonging to the current bus. WAV_VERSIONS transport => < <<'TRACKFADER', Track volume/pan fader can be used to change settings for the current track. pan, p - get/set pan position pan-back, pb - restore pan after pr/pl/pc pan-center, pc - set pan center pan-left, pl - pan track fully left pan-right, pr - pan track fully right volume, vol - set or adjust track volume (in dB) Examples: vol 0 (set level to 0 dB or unity gain) vol -20 (set level to -20 dB) vol - 3 (reduce vol by 3 dB) unity - set volume gain to unity mute, c, cut - mute track volume unmute, nomute, uncut, C - restore muted volume solo - mute all tracks but current track all, nosolo - return to pre-solo status TRACKFADER effect_info => <<'EFFECT_INFO', ladspa-register, lrg - list LADSPA effects preset-register, prg - list Ecasound presets ctrl-register, crg - list Ecasound controllers find-effect, fe - list available effects matching arguments example: find-effect reverb help-effect, he - full information about an effect example: help-effect 1209 (information about LADSPA plugin 1209) example: help-effect valve (information about LADSPA plugin valve) EFFECT_INFO effect_manipulation => <<'EFFECT_DO', add-effect, afx - add an effect to the current track Example: afx ea 50 add-controller, acl - add an Ecasound controller to the parameter of an effect insert-effect, ifx - insert an effect before another effect modify-effect, mfx - set, increment or decrement effect parameter remove-effect, rfx - remove an effect or controller append-effect, apfx - add effect to the end of current track effect list bypass-effects, bypass, bye - suspend current track effects except vol/pan restore-effects, restore, ref - restore track effects EFFECT_DO marks => < <<'TIME_SHIFTING', Regions and time shifting set-region, srg - specify a track region using times or mark names add-region, arg - define a region creating an auxiliary track remove-region, rrg - remove auxiliary track or region definition shift-track, shift - set playback delay for track/region unshift-track, unshift - eliminate playback delay for track/region TIME_SHIFTING track_io => <<'TRACKIO', Track inputs and outputs are set by source and send commands, which take similar arguments. source, src, r - set track source - with no arguments returns current signal source ---------------------------------------------------------- for this input use this command ---------------------------------------------------------- * soundcard channel 3 source 3 * soundcard channels 3,4 source 3; stereo * JACK client source fluidsynth * JACK port source fluidsynth:left * JACK port with spaces source "MPlayer [20120]:out_0" * unconnected JACK port source manual note: the port for mono track 'piano' would be ecasound:piano_in_1 XX * A list of JACK ports source drum.ports (ports list from drums.ports) source ports (ports list from .ports) * from another track (after effects processing) source track Strings * from a bus (raw output) source bus Strings * from another track (before effects processing) source loop sax_in ----------------------------------------------------------- send, out, m, aux - Create an auxiliary send Same arguments as 'source'. One send is allowed per track. Sends are not necessary to set up as most tracks are part of a bus that routes the audio output. TRACKIO inserts => <<'INSERTS', Use inserts to pass an audio stream through an external effect such as a JACK client or an analog fx box hooked to your soundcard. add-insert, ain - add an insert to current track remove-insert, rin - remove an insert from current track set-insert-wetness, wet - set/query insert wetness example: wet 99 (99% wet, 1% dry) INSERTS fades => < < < < < At the command prompt, you can enter several types of commands: Type Example ------------------------------------------------------------ Nama commands load somesong Ecasound commands cs-is-valid Shell expressions ! ls Perl code eval 2*3 # prints '6' Many commands in Nama operate on the currently selected track or 'current track'. track name or number to a command sets the current track before the command executes. For example, to mute the volume for a track called 'sax', you could say 'sax mute', or even something like '4 mute'. Using the track number can be convenient when executing commands on multiple tracks as 'for 4 5 6; unmute' PROMPT normalization => <<'NORMAL', Normalization ecanormalize, normalize, norm - run ecanormalize on current track version ecafixdc, fixdc - run ecafixdc on current track version autofix-tracks, autofix - fixdc and normalize selected versions of all PLAY NORMAL track_caching => <<'CACHING', Track caching (freezing) - render effects to a new .wav file to fix a result and save cpu cache - cache the output of a track or bus as a .wav file uncache - restore effects and settings prior to cache operation CACHING effect_chains => <<'EFFECT_CHAINS', Effect chains are presets that can consist of multiple effects. They are a convenient way to save by name sequences of effects with the parameters that you commonly use. You make an effect chain by saving effects from the current track. find-effect-chains, fec - find all effect chains (filtering on key/value pairs, if supplied) find-user-effect-chains,fuec - find all user-defined effect chains, filtering as above new-effect-chain, nec - define a new effect chain overwrite-effect-chain, oec - as above, but overwite existing definition add-effect-chain, aec - add an effect chain to the current track delete-effect-chain, dec - delete an effect chain definition EFFECT_CHAINS effect_profiles => <<'EFFECT_PROFILES', Effect profiles (effect chains for a group of tracks) An effect profile is a group of effect chains, saved with the tracks that configure them. It is a convenient way to share parts of a project among other projects. new-effect-profile, nep - define a new effect profile apply-effect-profile, aep - apply an effect profile (current effects are bypassed) overlay-effect-profile, oep - apply an effect profile, adding to current effects delete-effect-profile, dep - delete an effect profile definition EFFECT_PROFILES advanced_transport => <<'ADVANCED_TRANSPORT', loop-enable, loop - loop playback between two points example: loop 5.0 200.0 (positions in seconds) example: loop start end (mark names or numbers) loop-disable, noloop, nl - disable looping preview - start engine with WAV recording disabled (for mic check, etc.) Release with 'arm'. doodle - Like preview, with WAV playback also disabled Release with 'arm'. ADVANCED_TRANSPORT version_control => <<'VCS', ** Version control Nama uses git to save project state as a series of commits, a new commit after each command. It is easy to tag a commit as a way of documenting developments in a projects. save - label current autosave state as get - checkout project state tagged with branch - switch to branch and load state list-branches, lbr - list branches and tags new-branch, nbr - create a new branch starting at the current commit or a specified commit, e.g. 'nbr ' tag - tag current commit with a name and optional message ** note that can be a branch, tag, commit id. VCS diagnostics => < < <<'ADVANCED', set-track - directly set current track parameters destroy-current-wav - unlink current track's selected WAV version. ADVANCED ); # print values %{$help->{topic}}; $help->{screen} = < - show help for help - show help for all commands matching // help - list commands under topic below help yml - browse the YAML command source help is available for the following topics: HELP join '',@twocolumn; 1;Audio-Nama-1.216/lib/Audio/Nama/Project.pm0000644000175000017500000002221413544212613017125 0ustar jrothjroth# --------- Project related subroutines --------- { package Audio::Nama::Project; use Modern::Perl; use Carp; sub hello { my $self = shift; say "hello $self: ",Audio::Nama::Dumper $Audio::Nama::project} } { package Audio::Nama; use Modern::Perl; use Carp; use File::Slurp; # this sub caches the symlink-resolved form of the # project root directory sub project_root { state %proot; $proot{$config->{root_dir}} ||= resolve_path($config->{root_dir}) } sub config_file { $config->{opts}->{f} ? $config->{opts}->{f} : ".namarc" } { # OPTIMIZATION my %wdir; sub this_wav_dir { $config->{opts}->{p} and return $config->{root_dir}; # cwd $project->{name} and $wdir{$project->{name}} ||= resolve_path( join_path( project_root(), $project->{name}, q(.wav) ) ); } } sub waveform_dir { join_path(project_dir(), q(.waveform) ) } sub project_dir { $config->{opts}->{p} and return $config->{root_dir}; # cwd $project->{name} and join_path( project_root(), $project->{name}) } # we prepend a slash sub bus_track_display { my ($busname, $trackname) = ($this_bus, $this_track && $this_track->name || ''); ($busname eq "Main" ? "": "$busname/" ). $trackname } sub list_projects { my $projects = join "\n", sort map{ my ($vol, $dir, $lastdir) = File::Spec->splitpath($_); $lastdir } File::Find::Rule ->directory() ->maxdepth(1) ->extras( { follow => 1} ) ->in( project_root()); pager($projects); } sub initialize_project_data { logsub("&initialize_project_data"); -d Audio::Nama::waveform_dir() or mkdir Audio::Nama::waveform_dir(); $ui->destroy_widgets(); $ui->project_label_configure( -text => uc $project->{name}, -background => 'lightyellow', ); $gui->{tracks} = {}; $gui->{fx} = {}; $gui->{_markers_armed} = 0; map{ $_->initialize() } qw( Audio::Nama::Edit Audio::Nama::Fade Audio::Nama::Mark Audio::Nama::Bus Audio::Nama::Track Audio::Nama::Insert Audio::Nama::Effect Audio::Nama::EffectChain ); # $is_armed = 0; $setup->{_old_snapshot} = ""; $mode->{mastering} = 0; $project->{track_comments} = {}; $project->{track_version_comments} = {}; $project->{repo} = undef; $project->{artist} = undef; $project->{bunch} = {}; $project->{sample_rate} = $config->{sample_rate}; create_system_buses(); $this_bus = 'Main'; $setup->{wav_info} = {}; clear_offset_run_vars(); $mode->{offset_run} = 0; $this_edit = undef; { no warnings 'uninitialized'; $mode->{preview}++ if $config->{initial_mode} eq 'preview'; $mode->{doodle}++ if $config->{initial_mode} eq 'doodle'; } Audio::Nama::ChainSetup::initialize(); reset_hotkey_buffers(); reset_command_buffer(); $this_engine->reset_ecasound_selections_cache(); } sub load_project { logsub("&load_project"); my %args = @_; logpkg(__FILE__,__LINE__,'debug', sub{json_out \%args}); $project->{name} = $args{name} if $args{name}; $config->{opts}->{c} and $args{create}++; if (! $project->{name} or $project->{name} and ! -d project_dir() and ! $args{create}) { no warnings 'uninitialized'; Audio::Nama::pager_newline(qq(Project "$project->{name}" not found. Loading project "untitled".)); $project->{name} = "untitled", $args{create}++, } if ( ! -d project_dir() ) { map{create_dir($_)} project_dir(), this_wav_dir(), waveform_dir() if $args{create}; } # we used to check each project dir for customized .namarc # read_config( global_config() ); teardown_engine(); trigger_rec_cleanup_hooks(); initialize_project_data(); remove_riff_header_stubs(); cache_wav_info(); restart_wav_memoize(); initialize_project_git_repository(); restore_state($args{settings}) unless $config->{opts}->{M} ; $config->{opts}->{M} = 0; # enable initialize_mixer(); dig_ruins(); # in case there are audio files, but no tracks present git_commit("initialize new project") if $config->{use_git}; # possible null if Text mode #$ui->global_version_buttons(); #$ui->refresh_group; logpkg(__FILE__,__LINE__,'debug', "project_root: ", project_root()); logpkg(__FILE__,__LINE__,'debug', "this_wav_dir: ", this_wav_dir()); logpkg(__FILE__,__LINE__,'debug', "project_dir: ", project_dir()); 1; } sub restore_state { my $name = shift; if( ! $name or $name =~ /.json$/ or ! $config->{use_git}) { restore_state_from_file($name) } else { restore_state_from_vcs($name) } } sub initialize_mixer { return if $tn{Main}; Audio::Nama::SimpleTrack->new( group => 'Null', name => 'Main', send_type => 'soundcard', send_id => 1, width => 2, rw => MON, source_type => 'bus', source_id => 'Main', ); my $mixdown = Audio::Nama::MixDownTrack->new( group => 'Mixdown', name => 'Mixdown', width => 2, rw => OFF, source_type => 'track', source_id => 'Main', send_type => 'soundcard', send_id => 1, ); $ui->create_master_and_mix_tracks(); } sub dig_ruins { logsub("&dig_ruins"); return if user_tracks_present(); logpkg(__FILE__,__LINE__,'debug', "looking for audio files"); my $d = this_wav_dir(); opendir my $wav, $d or carp "couldn't open directory $d: $!"; # remove version numbers my @wavs = grep{s/(_\d+)?\.wav$//i} readdir $wav; closedir $wav if $wav; my %wavs; map{ $wavs{$_}++ } @wavs; @wavs = keys %wavs; logpkg(__FILE__,__LINE__,'debug', "tracks found: @wavs"); map{add_track($_)}@wavs; } sub remove_riff_header_stubs { # 44 byte stubs left by a recording chainsetup that is # connected by not started logsub("&remove_riff_header_stubs"); logpkg(__FILE__,__LINE__,'debug', "this wav dir: ", this_wav_dir()); return unless this_wav_dir(); my @wavs = File::Find::Rule ->name( qr/\.wav$/i ) ->file() ->size(44) ->extras( { follow => 1} ) ->in( this_wav_dir() ) if -d this_wav_dir(); logpkg(__FILE__,__LINE__,'debug', join $/, @wavs); map { unlink $_ } @wavs; } sub create_system_buses { logsub("&create_system_buses"); my $buses = q( Main Audio::Nama::SubBus send_type track send_id Main # master fader track Mixdown Audio::Nama::Bus # mixdown track Mastering Audio::Nama::Bus # mastering network Insert Audio::Nama::Bus # auxiliary tracks for inserts Cooked Audio::Nama::Bus # for track caching Temp Audio::Nama::Bus # temp tracks while generating setup Null Audio::Nama::Bus # unrouted for Main Midi Audio::Nama::MidiBus send_type null send_id null # all MIDI tracks Aux Audio::Nama::SubBus send_type null # routed only from track source_* and send_* fields ); ($buses) = strip_comments($buses); # need initial parentheses $buses =~ s/\A\s+//; # remove initial newline and whitespace $buses =~ s/\s+\z//; # remove terminating newline and whitespace my @buses = split "\n", $buses; for my $bus (@buses) { my ($name, $class, @args) = split " ",$bus; $class->new(name => $name, @args); } } ## project templates sub new_project_template { my ($template_name, $template_description) = @_; my @tracks = all_tracks(); # skip if project is empty throw("No user tracks found, aborting.\n", "Cannot create template from an empty project."), return if ! user_tracks_present(); # save current project status to temp state file my $previous_state = '_previous_state.json'; save_state($previous_state); # edit current project into a template # No tracks are recorded, so we'll remove # - version (still called 'active') # - track caching # - region start/end points # Also # - unmute all tracks # - throw away any pan caching map{ my $track = $_; $track->unmute; map{ $track->set($_ => undef) } qw( version old_pan_level region_start region_end ); } @tracks; # Throw away command history $text->{term}->SetHistory(); # Buses needn't set version info either map{$_->set(version => undef)} values %bn; # create template directory if necessary mkdir join_path(project_root(), "templates"); # save to template name save_state( join_path(project_root(), "templates", "$template_name.json")); # add description, but where? # recall temp name load_project( # restore_state() doesn't do the whole job name => $project->{name}, settings => $previous_state, ); # remove temp state file unlink join_path( project_dir(), $previous_state) ; } sub use_project_template { my $name = shift; my @tracks = Audio::Nama::Track::all(); # skip if project isn't empty throw("User tracks found, aborting. Use templates in an empty project."), return if scalar @tracks > 2; # load template load_project( name => $project->{name}, settings => join_path(project_root(),"templates","$name.json"), ); save_state(); } sub list_project_templates { my @templates= map{ /(.+?)\.json$/; $1} read_dir(join_path(project_root(), "templates")); pager(join "\n","Templates:",@templates); } sub remove_project_template { map{my $name = $_; pager("$name: removing template"); $name .= ".yml" unless $name =~ /\.yml$/; unlink join_path( project_root(), "templates", $name); } @_; } } # end package :: 1; __END__Audio-Nama-1.216/lib/Audio/Nama/Waveform.pm0000644000175000017500000001072413544212613017310 0ustar jrothjrothpackage Audio::Nama::Waveform; use Audio::Nama::Globals qw($project $config $gui %ti); use Audio::Nama::Util qw(join_path); use Modern::Perl; use Try::Tiny; use vars qw(%by_name); use Audio::Nama::Object qw(wav track project start end); # * objects of this class represent a waveform display # * each object is associated with an audio file # * object will find or generate PNG for the audio # * will display waveform # + if shift, correctly position PNG # + if region, trim the PNG existing for the track # the $track->waveform method will create a new object of this class # we will memoize since it remains constant between reconfigures # keyed to the # + name of the WAV file # + name of project # + start and end times # the get_png() method will find or generate the appropriate PNG # files are of the form # sax_1.wav.1200x200-10.png # where the numbers correspond to width and height in pixels of the audio # waveform image, and the x-scaling in pixels per second (default 10) sub new { my $class = shift; my %args = @_; bless \%args, $class } sub generate_waveform { my $self = shift; my ($width, $height, $pixels_per_second) = @_; $pixels_per_second //= $config->{waveform_pixels_per_second}; $height //= $config->{waveform_height}; $width //= int( $self->track->wav_length * $pixels_per_second); my $name = waveform_name($self->track->full_path, $width, $height, $pixels_per_second); my $cmd = join ' ', 'waveform', "-b #c2d6d6 -c #0080ff -W $width -H $height", $self->full_path, $name; say $cmd; system($cmd); $name; } # utility subroutine sub waveform_name { my($path, $width, $height, $pixels, $start, $end) = @_; "$path." . $width . 'x' . "$height-$pixels" . region_def($start,$end) . ".png" } sub find_waveform { my $self = shift; my $match = shift() // '*'; my @files = File::Find::Rule->file() ->name( $self->wav . ".$match.png" ) ->in( Audio::Nama::this_wav_dir() ); @files; } sub get_waveform { my $self = shift; my ($waveform) = $self->find_waveform; $waveform or $self->generate_waveform; } sub display { my $self = shift; my ($waveform) = $self->get_waveform; my $widget = $gui->{ww}->Photo(-format => 'png', -file => $waveform); $gui->{waveform}{$self->track->name} = []; # unused? $gui->{wwcanvas}->createImage( 0, $self->y_offset_multiplier * $config->{waveform_height}, -anchor => 'nw', -tags => ['waveform', $self->track->name], -image => $widget); my ($width, $height) = Audio::Nama::wh($gui->{ww}); my $name_x = $width - 150; my $name_y = $config->{waveform_height} * $self->y_offset_multiplier + 20; $gui->{wwcanvas}->createText( $name_x, $name_y, -font => 'lucidasanstypewriter-bold-14', -text => uc($self->track->name) . ' - '.$self->track->current_wav); } sub width { my $self = shift; my ($waveform) = $self->get_waveform; my ($width, $height, $pixels_per_second) = $waveform =~ /(\d+)x(\d+)-(\d+)/ or Audio::Nama::throw("cannot parse waveform filename: $waveform"); $width } sub height { my $self = shift; my ($waveform) = $self->get_waveform; my ($width, $height, $pixels_per_second) = $waveform =~ /(\d+)x(\d+)-(\d+)/ or Audio::Nama::throw("cannot parse waveform filename: $waveform"); $height } sub pixels_per_second { my $self = shift; my ($waveform) = $self->get_waveform; my ($width, $height, $pixels_per_second) = $waveform =~ /(\d+)x(\d+)-(\d+)/ or Audio::Nama::throw("cannot parse waveform filename: $waveform"); $pixels_per_second } sub y_offset_multiplier { my $self = shift; my $before_me; for (2 .. $self->track->n - 1){ $before_me++ if $ti{$_} and $ti{$_}->play; } $before_me } 1 # obligatory __END__ =comment Usage: waveform [options] source_audio [ouput.png] -W, --width WIDTH Width (in pixels) of generated waveform image -- Default 1800. -H, --height HEIGHT Height (in pixels) of generated waveform image -- Default 280. -c, --color COLOR Color (hex code) to draw the waveform. Can also pass 'transparent' to cut it out of the background -- Default #00ccff. -b, --background COLOR Background color (hex code) to draw waveform on -- Default #666666. -m, --method METHOD Wave analyzation method (can be 'peak' or 'rms') -- Default 'peak'. -q, --quiet Don't print anything out when generating waveform -F, --force Force generationg of waveform if file exists -h, --help Display this screen =cutAudio-Nama-1.216/lib/Audio/Nama/TrackRegion.pm0000644000175000017500000000410713544212613017730 0ustar jrothjroth{ package Audio::Nama::TrackRegion; use Role::Tiny; use Modern::Perl; use Audio::Nama::Globals qw(:all); use Carp; # these behaviors are associated with WAV playback sub is_region { defined $_[0]->{region_start} } sub region_start_time { my $track = shift; return unless $track->is_region; #return if $track->rec_status ne PLAY; carp $track->name, ": expected PLAY status" if $track->rec_status ne PLAY; Audio::Nama::Mark::time_from_tag( $track->region_start ) } sub region_end_time { my $track = shift; return unless $track->is_region; #return if $track->rec_status ne PLAY; #carp $track->name, ": expected PLAY status" if $track->rec_status ne PLAY; no warnings 'uninitialized'; if ( $track->region_end eq 'END' ){ return $track->wav_length; } else { Audio::Nama::Mark::time_from_tag( $track->region_end ) } } sub playat_time { my $track = shift; carp $track->name, ": expected PLAY status" if $track->rec_status ne PLAY; #return if $track->rec_status ne PLAY; Audio::Nama::Mark::time_from_tag( $track->playat ) } # the following methods adjust # region start and playat values during edit mode sub shifted_length { my $track = shift; my $setup_length; if ($track->region_start){ $setup_length = $track->shifted_region_end_time - $track->shifted_region_start_time } else { $setup_length = $track->wav_length; } no warnings 'uninitialized'; $setup_length += $track->shifted_playat_time; } sub shifted_region_start_time { my $track = shift; return $track->region_start_time unless $mode->{offset_run}; Audio::Nama::new_region_start(Audio::Nama::edit_vars($track)); } sub shifted_playat_time { my $track = shift; return $track->playat_time unless $mode->{offset_run}; Audio::Nama::new_playat(Audio::Nama::edit_vars($track)); } sub shifted_region_end_time { my $track = shift; return $track->region_end_time unless $mode->{offset_run}; Audio::Nama::new_region_end(Audio::Nama::edit_vars($track)); } sub region_is_out_of_bounds { return unless $mode->{offset_run}; my $track = shift; Audio::Nama::case(Audio::Nama::edit_vars($track)) =~ /out_of_bounds/ } } 1Audio-Nama-1.216/lib/Audio/Nama/Config.pm0000644000175000017500000001315513544212613016730 0ustar jrothjroth# ------ Config subroutines ------ # To create a new config var: # # add the mapping, e.g. "$mix_to_disk_format $config->{mix_to_disk_format}" # (without quotes) to file var_config # these subs are in the main namespace package Audio::Nama; use Modern::Perl; no warnings 'uninitialized'; use Audio::Nama::Globals qw(:all); # exclusive to this module our ( %subst, # substitutions ); ## configuration file sub global_config { # return text of config file, in the following order # or priority: # # 1. the file designated by the -f command line argument # 2. .namarc in the current project directory, i.e. ~/nama/untitled/.namarc # 3. .namarc in the home directory, i.e. ~/.namarc # 4. .namarc in the project root directory, i.e. ~/nama/.namarc if( $config->{opts}->{f} ){ pager_newline("reading config file $config->{opts}->{f}\n"); return read_file($config->{opts}->{f}); } my @search_path = (project_dir(), $ENV{HOME}, project_root() ); my $c = 0; map{ if (-d $_) { my $config_path = join_path($_, config_file()); if( -f $config_path or -l $config_path){ say "Found config file: $config_path"; my $yml = read_file($config_path); return $yml; } } } ( @search_path) } # sub global_config { # read_file( join_path($ENV{HOME}, config_file())); # } sub read_config { # read and process the configuration file # # use the embedded default file if none other is present logsub("&read_config"); my $config_file = shift; my $yml = $config_file // get_data_section("default_namarc"); strip_all( $yml ); my %cfg = %{ yaml_in($yml) }; logpkg(__FILE__,__LINE__,'debug', "config file:", Dumper \%cfg); *subst = \%{$cfg{abbreviations}}; # alias walk_tree(\%cfg); walk_tree(\%cfg); # second pass completes substitutions assign( data => \%cfg, vars => [ config_vars() ], class => 'Audio::Nama', var_map => 1, ); $config->{root_dir} = $config->{opts}->{d} if $config->{opts}->{d}; $config->{root_dir} = expand_tilde($config->{root_dir}); $config->{sample_rate} = $cfg{abbreviations}{frequency}; $config->{hotkeys}->{' '} = $config->{hotkeys}->{Space}; $config->{use_git} and ! git_executable_found() and say("Config file requests Git version control, but the git executable could not be found. Please check that the git executable directory is included in your shell's \$PATH variable (currently $ENV{PATH}). Falling back to the file paradigm. :-( Note that the command nama> save initial_mix creates initial_mix.json, not a tagged commit. nama> get initial_mix loads initial_mix.json"); $config->{use_git} = $config->{use_git} && git_executable_found() ? 1 : 0; } sub git_executable_found { qx(which git) } sub walk_tree { #logsub("&walk_tree"); my $ref = shift; map { substitute($ref, $_) } grep {$_ ne q(abbreviations)} keys %{ $ref }; } sub substitute{ my ($parent, $key) = @_; my $val = $parent->{$key}; #logpkg(__FILE__,__LINE__,'debug', qq(key: $key val: $val\n) ); ref $val and walk_tree($val) or map{$parent->{$key} =~ s/$_/$subst{$_}/} keys %subst; } sub first_run { return if $config->{opts}->{f}; my $config_file = '.namarc'; my $config_path = "$ENV{HOME}/$config_file"; logpkg(__FILE__,__LINE__,'debug', "config path: $config_path" ); if ( ! -e $config_path and ! -l $config_path ) { # check for missing components my $missing; my @a = `which analyseplugin`; @a or print( <; chomp $reply; print("Goodbye.\n"), exit unless $reply =~ /y/i; } print <; sleep 1; print <; chomp $reply; my $default_config; if ($reply !~ /n/i){ # write project root path into default namarc $default_config = get_data_section("default_namarc"); $default_config =~ s/^project_root.*$/project_root: $ENV{HOME}\/nama/m; # create path nama/untitled/.wav # # this creates directories for # - project root # - project name 'untitled', the default project, and # - project untitled's hidden directory for holding WAV files my $default_project_root = join_path($ENV{HOME}, 'nama'); mkpath( join_path($default_project_root, qw(untitled .wav)) ); $config->{root_dir} = $default_project_root; # needed for $file->user_customization() to resolve in next line write_file($file->user_customization(), get_data_section('custom_pl')); } else { print <{current_op}->{$_[0]->name} //= $_[0]->{ops}->[-1] } sub param { $project->{current_param}->{$_[0]->op} //= 1 } sub stepsize { $project->{current_stepsize}->{$_[0]->op}->[$_[0]->param] //= 0.01 # TODO use hint if available } sub pos { my $track = shift; first_index{$_ eq $track->op} @{$track->ops}; } sub user_ops_o { my $track = shift; map{ fxn($_) } $track->user_ops(); } sub channel_ops { my $track = shift; grep{ $_->is_channel_op } $track->ops_o; } sub audio_ops { my $track = shift; grep{ ! $_->is_channel_op and ! $_->is_controller } $track->ops_o; } sub ops_ecasound_order { my $track = shift; $track->channel_ops, $track->audio_ops } sub ecasound_dynamic_apply_list { # audio ops and their controllers my $track = shift; grep{ ! $_->is_channel_op } $track->ops_o; } sub ops_o { my $track = shift; map{ Audio::Nama::fxn($_) } @{ $track->ops } } sub apply_ops { my $track = shift; $_->apply_op for $track->ecasound_dynamic_apply_list; } sub user_ops { my $track = shift; my @skip = grep {fxn($_)} # must exist map { $track->{$_} } qw(vol pan fader latency_op ); # make a dictionary of ops to exclude # that includes utility ops and their controllers my %skip; map{ $skip{$_}++ } @skip, Audio::Nama::expanded_ops_list(@skip); grep{ ! $skip{$_} } @{ $track->{ops} || [] }; } sub first_effect_of_type { my $track = shift; my $type = shift; for my $op ( @{$track->ops} ){ my $FX = fxn($op); return $FX if $FX->type =~ /$type/ # Plate matches el:Plate } } sub effect_id_by_name { my $track = shift; my $ident = shift; for my $FX ($track->user_ops_o) { return $FX->id if $FX->name eq $ident } } sub vol_level { my $self = shift; try { $self->vol_o->params->[0] } } sub pan_level { my $self = shift; try { $self->pan_o->params->[0] } } sub vol_o { my $self = shift; fxn($self->vol) } sub pan_o { my $self = shift; fxn($self->pan) } sub mute { my $track = shift; my $nofade = shift; # do nothing if track is already muted return if defined $track->old_vol_level(); # do nothing if track has no volume operator my $vol = $track->vol_o; return unless $vol; # store vol level for unmute $track->set(old_vol_level => $vol->params->[0]); $nofade ? $vol->_modify_effect(1, $vol->mute_level) : $vol->fadeout } sub unmute { my $track = shift; my $nofade = shift; # do nothing if we are not muted return if ! defined $track->old_vol_level; $nofade ? $track->vol_o->_modify_effect(1, $track->old_vol_level) : $track->vol_o->fadein($track->old_vol_level); $track->set(old_vol_level => undef); } sub get_inserts { my $track = shift; grep{ $_->{track} eq $track->name} values %Audio::Nama::Insert::by_index; } 1;Audio-Nama-1.216/lib/Audio/Nama/Graph.pm0000644000175000017500000002145213544212613016563 0ustar jrothjrothpackage Audio::Nama::Graph; use Modern::Perl; use Carp; use Graph; use Audio::Nama::Util qw(input_node output_node); use Audio::Nama::Log qw(logsub logpkg); use Audio::Nama::Globals qw(:trackrw); use vars qw(%reserved); # this dispatch table also identifies labels reserved # for signal sources and sinks. *reserved = \%Audio::Nama::IO::io_class; sub add_path_for_rec { # connect input source to file my($g,$track) = @_; logsub("&add_path_for_rec: track ".$track->name); # Case 1: Regular track if( $track->source_type !~ /track|bus|loop/ and ! $track->is_mixing) { # create temporary track for rec_file chain # (it may be possible to avoid creating a # temporary track by providing the data as # graph edge attributes) logpkg(__FILE__,__LINE__,'debug',"rec file link for ".$track->name); my $name = $track->name . '_rec_file'; my $anon = Audio::Nama::SlaveTrack->new( target => $track->name, rw => OFF, group => 'Temp', hide => 1, novol => 1, nopan => 1, name => $name); # connect writepath: source --> temptrackname --> wav_out $g->add_path(input_node($track->source_type), $name, 'wav_out'); $g->set_vertex_attributes($name, { # set chain_id to R3 (if original track is 3) chain_id => 'R'.$track->n, # do not perform mono-to-stereo copy, # (override IO class default) mono_to_stereo => '', }); } # Case 2: Mix track elsif ($track->source_type =~ /bus|track/ or $track->is_mixing) { my $name = $track->name . '_rec_file'; my $anon = Audio::Nama::SlaveTrack->new( target => $track->name, rw => OFF, group => 'Temp', hide => 1, novol => 1, nopan => 1, name => $name); my @edge = ($track->name, 'wav_out'); $g->add_path(@edge); # set chain_id same as original track $g->set_edge_attributes(@edge, { chain_id => $track->n, }); } } sub add_path_for_aux_send { my ($g, $track) = @_; add_path_for_send($g, map{ $track->$_ } qw(name send_type send_id) ); } sub add_path_for_send { my ($g, $name, $send_type, $send_id) = @_; logsub("&add_path_for_aux_send: track ".$name); logpkg(__FILE__,__LINE__,'debug',"args: graph: $g, name: $name, send_type, $send_type, send_id: $send_id"); # for track 'sax', send_type 'track' send_id 'vocal' # my @path; if ( $send_type eq 'track'){ @path = ($name, $send_id) } else { # for track 'sax', send_type 'jack_client', create route as # sax -> sax_aux_send -> jack_client_out my $nameof = $name . '_aux_send'; my $anon = Audio::Nama::SlaveTrack->new( target => $name, rw => OFF, group => 'Temp', hide => 1, name => $nameof); @path= ($name, $nameof, output_node($send_type)); } logpkg(__FILE__,__LINE__,'debug',"adding path ", join '-',@path); $g->add_path(@path); } { my %seen; sub expand_graph { my $g = shift; %seen = (); for ($g->edges){ my($a,$b) = @{$_}; logpkg(__FILE__,__LINE__,'debug',"$a-$b: processing..."); logpkg(__FILE__,__LINE__,'debug',"$a-$b: already seen") if $seen{"$a-$b"}; next if $seen{"$a-$b"}; # case 1: both nodes are tracks: default insertion logic if ( is_a_track($a) and is_a_track($b) ){ logpkg(__FILE__,__LINE__,'debug',"processing track-track edge: $a-$b"); add_loop($g,$a,$b) } # case 2: fan out from track: use near side loop elsif ( is_a_track($a) and $g->successors($a) > 1 ) { logpkg(__FILE__,__LINE__,'debug',"fan_out from track $a"); add_near_side_loop($g,$a,$b,out_loop($a));} # case 3: fan in to track: use far side loop elsif ( is_a_track($b) and $g->predecessors($b) > 1 ) { logpkg(__FILE__,__LINE__,'debug',"fan in to track $b"); add_far_side_loop($g,$a,$b,in_loop($b));} else { logpkg(__FILE__,__LINE__,'debug',"$a-$b: no action taken") } } } sub add_inserts { my $g = shift; map{ my $i = $Audio::Nama::tn{$_}->prefader_insert; $Audio::Nama::Insert::by_index{$i}->add_paths($g, $_) if $i; $i = $Audio::Nama::tn{$_}->postfader_insert; $Audio::Nama::Insert::by_index{$i}->add_paths($g, $_) if $i; } grep{ $Audio::Nama::tn{$_} } $g->vertices; } sub add_loop { my ($g,$a,$b) = @_; logpkg(__FILE__,__LINE__,'debug',"adding loop"); my $fan_out = $g->successors($a); logpkg(__FILE__,__LINE__,'debug',"$a: fan_out $fan_out"); my $fan_in = $g->predecessors($b); logpkg(__FILE__,__LINE__,'debug',"$b: fan_in $fan_in"); if ($fan_out > 1){ add_near_side_loop($g,$a,$b, out_loop($a)) } elsif ($fan_in > 1){ add_far_side_loop($g,$a,$b, in_loop($b)) } elsif ($fan_in == 1 and $fan_out == 1){ # we expect a single user track to feed to Main_in # as multiple user tracks do $b eq 'Main' ? add_far_side_loop($g,$a,$b,in_loop($b)) # otherwise default to near_side ( *_out ) loops : add_near_side_loop($g,$a,$b,out_loop($a)); } else {croak "unexpected fan"}; } sub add_near_side_loop { # a - b # a - c # a - d # # converts to # # a_out - b # a_out - c # a_out - d # a - a_out # we deal with all edges departing from $a, the left node. # I call it a-x below, but it is actually a-$_ where $_ # is an alias to each of the successor node. # # 1. start with a - x # # 2. delete a - x # # 3. add a - a_out # # 4. add a_out - x # # 5. Add a_out attributes for track name and # other info need to generate correct chain_ids # # 6. Copy any attributes of edge a - x to a_out - x. # # No multiedge handling needed because with our # current topology, we never have a track # with, for example, multiple edges to a soundcard. # # Send buses create new tracks to provide connections. my ($g, $a, $b, $loop) = @_; logpkg(__FILE__,__LINE__,'debug',"$a-$b: insert near side loop"); # we will insert loop _after_ processing successor # edges so $a-$loop will not be picked up # in successors list. # We will assign chain_ids to loop-to-loop edges # looking like J7a, J7b,... # # To make this possible, we store the following # information in the left vertex of # the edge: # # n: track index, j: alphabetical counter $g->set_vertex_attributes($loop,{ n => $Audio::Nama::tn{$a}->n, j => 'a', track => $Audio::Nama::tn{$a}->name}); map{ my $attr = $g->get_edge_attributes($a,$_); logpkg(__FILE__,__LINE__,'debug',"deleting edge: $a-$_"); $g->delete_edge($a,$_); $g->add_edge($loop, $_); $g->set_edge_attributes($loop,$_, $attr) if $attr; $seen{"$a-$_"}++; } $g->successors($a); $g->add_edge($a,$loop); } sub add_far_side_loop { my ($g, $a, $b, $loop) = @_; logpkg(__FILE__,__LINE__,'debug',"$a-$b: insert far side loop"); $g->set_vertex_attributes($loop,{ n => $Audio::Nama::tn{$a}->n, j => 'a', track => $Audio::Nama::tn{$a}->name}); map{ my $attr = $g->get_edge_attributes($_,$b); logpkg(__FILE__,__LINE__,'debug',"deleting edge: $_-$b"); $g->delete_edge($_,$b); $g->add_edge($_,$loop); $g->set_edge_attributes($_,$loop, $attr) if $attr; $seen{"$_-$b"}++; } $g->predecessors($b); $g->add_edge($loop,$b); } } sub in_loop{ "$_[0]_in" } sub out_loop{ "$_[0]_out" } sub is_a_track{ $Audio::Nama::tn{$_[0]} } # most reliable sub is_terminal { $reserved{$_[0]} or is_port($_[0]) } sub is_port { $_[0] =~ /^[^:]+:[^:]+$/ } sub is_a_loop{ my $name = shift; return if $reserved{$name}; if (my($root, $suffix) = $name =~ /^(.+?)_(in|out|insert_p.+)$/){ return ($root, $suffix); } } sub inputless_tracks { my $g = shift; (grep{ is_a_track($_) and $g->is_source_vertex($_) } $g->vertices) } sub remove_out_of_bounds_tracks { my $g = shift; my @names = $g->successors('wav_in'); # PLAY status tracks map{ remove_tracks($g, $_) } grep{ Audio::Nama::edit_case(Audio::Nama::edit_vars($Audio::Nama::tn{$_})) =~ /out_of_bounds/ } @names; } sub recursively_remove_inputless_tracks { my $g = shift; # make multiple passes if necessary while(my @i = inputless_tracks($g)){ remove_tracks($g, @i); } } sub outputless_tracks { my $g = shift; (grep{ is_a_track($_) and $g->is_sink_vertex($_) } $g->vertices) } sub recursively_remove_outputless_tracks { my $g = shift; while(my @i = outputless_tracks($g)){ remove_tracks($g, @i); } } sub remove_tracks { my ($g, @names) = @_; map{ $g->delete_edges(map{@$_} $g->edges_from($_)); $g->delete_edges(map{@$_} $g->edges_to($_)); $g->delete_vertex($_); } @names; } # for latency-related graph transformations sub remove_branch { my ($g, $v) = @_; my @p = $g->predecessors($v); $g->delete_vertex($v) if $g->is_sink_vertex($v); remove_branch($g, $_) for @p; } sub remove_isolated_vertices { my $g = shift; map{ $g->delete_vertex($_) } grep{ $g->is_isolated_vertex($_) } $g->vertices(); } sub simplify_send_routing { my $g = shift; for( grep { is_a_track($_) } $g->vertices ){ my $aux = "$_\_aux_send"; my @successors; if( $g->has_edge($_, $aux) and @successors = $g->successors($_) and scalar @successors == 1 ){ my ($output) = $g->successors($aux); $g->delete_path($_, $aux, $output); $g->add_edge($_, $output); } } } 1; __END__Audio-Nama-1.216/lib/Audio/Nama/Persistence.pm0000644000175000017500000002442413544212613020010 0ustar jrothjroth# ---------- Persistent State Support ------------- package Audio::Nama; use File::Copy; use Modern::Perl; no warnings 'uninitialized'; sub save_state { logsub("&save_state"); my $filename = shift; if ($filename) { # remove extension if present $filename =~ s/\.json//; # append filename if warranted $filename = $filename =~ m{/} ? $filename # as-is if input contains slashes : join_path(project_dir(),$filename) } my $path = $filename || $file->state_store(); $project->{nama_version} = $VERSION; # store playback position, if possible $project->{playback_position} = ecasound_iam("getpos") if $this_engine->valid_setup(); # some stuff get saved independently of our state file logpkg(__FILE__,__LINE__,'debug', "saving palette"); $ui->save_palette; # do nothing more if only Main and Mixdown #user_tracks_present() or throw("No user tracks, skipping..."), return; logpkg(__FILE__,__LINE__,'debug',"Saving state as ", $path); save_system_state($path); save_global_effect_chains(); save_midish(); # store alsa settings if ( $config->{opts}->{a} ) { my $filename = $filename; $filename =~ s/\.yml$//; pager("storing ALSA settings\n"); pager(qx(alsactl -f $filename.alsa store)) } } sub initialize_marshalling_arrays { @tracks_data = (); # zero based, iterate over these to restore @bus_data = (); # @marks_data = (); @fade_data = (); @inserts_data = (); @effects_data = (); @edit_data = (); @project_effect_chain_data = (); @global_effect_chain_data = (); $text->{command_history} = {}; } sub save_system_state { my $path = shift; my $output_format = shift; sync_effect_parameters(); # in case a controller has made a change # we sync read-only parameters, too, but I think that is # harmless initialize_marshalling_arrays(); # prepare tracks for storage $this_track_name = $this_track->name if $this_track; logpkg(__FILE__,__LINE__,'debug', "copying tracks data"); map { push @tracks_data, $_->as_hash } all_tracks(); # print "found ", scalar @tracks_data, "tracks\n"; # delete obsolete fields map { my $t = $_; map{ delete $t->{$_} } qw(ch_r ch_m source_select send_select jack_source jack_send); } @tracks_data; logpkg(__FILE__,__LINE__,'debug', "copying bus data"); @bus_data = map{ $_->as_hash } sort { $a->name cmp $b->name} Audio::Nama::Bus::all(); my $by_n = sub { $a->{n} <=> $b->{n} }; # prepare inserts data for storage logpkg(__FILE__,__LINE__,'debug', "copying inserts data"); @inserts_data = sort $by_n map{ $_->as_hash } values %Audio::Nama::Insert::by_index; # prepare marks data for storage (new Mark objects) logpkg(__FILE__,__LINE__,'debug', "copying marks data"); @marks_data = sort {$a->{time} <=> $b->{time} } map{ $_->as_hash } Audio::Nama::Mark::all(); @effects_data = sort { $a->{id} cmp $b->{id} } map{ $_->as_hash } values %Audio::Nama::Effect::by_id; @fade_data = sort $by_n map{ $_->as_hash } values %Audio::Nama::Fade::by_index; @edit_data = sort $by_n map{ $_->as_hash } values %Audio::Nama::Edit::by_index; @project_effect_chain_data = sort $by_n map { $_->as_hash } Audio::Nama::EffectChain::find(project => 1); # save history -- 50 entries, maximum my @history; @history = $text->{term}->GetHistory if $text->{term}; my %seen; $text->{command_history} = []; map { push @{$text->{command_history}}, $_ unless $seen{$_}; $seen{$_}++ } @history; my $max = scalar @{$text->{command_history}}; $max = 50 if $max > 50; @{$text->{command_history}} = @{$text->{command_history}}[-$max..-1]; logpkg(__FILE__,__LINE__,'debug', "serializing"); my @formats = $output_format || $config->serialize_formats; map{ my $format = $_ ; serialize( file => $path, format => $format, vars => \@tracked_vars, class => 'Audio::Nama', ); } @formats; serialize( file => $file->untracked_state_store, format => 'json', vars => \@persistent_vars, class => 'Audio::Nama', ); "$path.json"; } { my %is_legal_suffix = ( json => 'json', yml => 'yaml', pl => 'perl', bin => 'storable', yaml => 'yaml', # we allow formats as well perl => 'perl', storable => 'storable', ); sub get_newest { # choose the newest # my ($path, $format) = @_; # simply return the file # if filename matches exactly, # and we know the format return($path, $format) if -f $path and $is_legal_suffix{$format}; my ($dir, $name) = $path =~ m!^(.*?)([^/]+)$!; # otherwise we glob, sort and filter directory entries my @sorted = sort{ $a->[1] <=> $b->[1] } grep{ $is_legal_suffix{$_->[2]} } map { my ($suffix) = m/^$path(?:\.(\w+))?$/; [$_, -M $_, $suffix] } glob("$path*"); logpkg(__FILE__,__LINE__,'debug', sub{json_out \@sorted}); ($sorted[0]->[0], $sorted[0]->[2]); } } { my %decode = ( json => \&json_in, yaml => sub { my $yaml = shift; # remove empty key hash lines to satisfy YAML::Tiny $yaml = join $/, grep{ ! /^\s*:/ } split $/, $yaml; $yaml = quote_yaml_scalars( $yaml ); yaml_in($yaml); }, perl => sub {my $perl_source = shift; eval $perl_source}, storable => sub { my $bin = shift; thaw( $bin) }, ); # allow dispatch by either file format or suffix @decode{qw(yml pl bin)} = @decode{qw(yaml perl storable)}; sub decode { my ($source, $suffix) = @_; $decode{$suffix} or die qq(key $suffix: expecting one of).join q(,),keys %decode; $decode{$suffix}->($source); } } sub restore_state_from_file { logsub("&restore_state_from_file"); my $filename = shift; $filename =~ s/\.json$//; $filename = join_path(project_dir(), $filename) if $filename and not $filename =~ m(/); $filename ||= $file->state_store(); my ($ref, $path, $source, $suffix); # get state file, newest if more than one # with same name, differing extensions # i.e. State.json and State.yml initialize_marshalling_arrays(); # restore from default filenames ( $path, $suffix ) = get_newest($file->untracked_state_store); if ($path) { $source = read_file($path); $ref = decode($source, $suffix); assign( data => $ref, vars => \@persistent_vars, class => 'Audio::Nama'); assign_singletons( { data => $ref }); } ( $path, $suffix ) = get_newest($filename); if ($path) { $source = read_file($path); $ref = decode($source, $suffix); assign( data => $ref, vars => \@tracked_vars, class => 'Audio::Nama'); # perform assignments for singleton # hash entries (such as $fx->{ applied}); # that that assign() misses assign_singletons({ data => $ref }); } restore_global_effect_chains(); ####### Backward Compatibility ######## if ( $project->{nama_version} < 1.214 ) { } # restore effects, no change to track objects needed map { my %args = %$_; my $class = delete $args{class}; my $FX = $class->new(%args, restore => 1); } @effects_data; # restore user buses Audio::Nama::Bus::initialize(); map{ my $class = $_->{class}; $class->new( %$_ ) } @bus_data; create_system_buses(); # temporary turn on mastering mode to enable # recreating mastering tracksk #my $current_master_mode = $mode->{mastering}; #$mode->{mastering} = 1; # convert field "latency" to "latency_op" map{ $_->{latency_op} = delete $_->{latency} if $_->{latency} } @tracks_data; # restore tracks map{ my %args = %$_; my $class = $args{class} || "Audio::Nama::Track"; my $track = $class->new( %args, restore => 1 ); } @tracks_data; # restore inserts Audio::Nama::Insert::initialize(); map{ bless $_, $_->{class}; # bless directly, bypassing constructor $Audio::Nama::Insert::by_index{$_->{n}} = $_; } @inserts_data; # Restore GUI for user tracks map{ my $n = $_->{n}; # create gui $ui->track_gui($n) unless $n <= 2; } @tracks_data; $this_track = $tn{$this_track_name}, set_current_bus() if $this_track_name; #print "\n---\n", $main->dump; #print "\n---\n", map{$_->dump} Audio::Nama::audio_tracks();# exit; $ui->manifest; logpkg(__FILE__,__LINE__,'debug', sub{ join " ", map{ ref $_, $/ } all_tracks() }); # restore Alsa mixer settings if ( $config->{opts}->{a} ) { my $filename = $filename; $filename =~ s/\.yml$//; pager("restoring ALSA settings\n"); pager(qx(alsactl -f $filename.alsa restore)); } # text mode marks map { my %h = %$_; my $mark = Audio::Nama::Mark->new( %h ) ; } grep { (ref $_) =~ /HASH/ } @marks_data; $ui->restore_time_marks(); $ui->paint_mute_buttons; # track fades map{ my %h = %$_; my $fade = Audio::Nama::Fade->new( %h ) ; } @fade_data; # edits map{ my %h = %$_; my $edit = Audio::Nama::Edit->new( %h ) ; } @edit_data; # restore command history $text->{term}->SetHistory(@{$text->{command_history}}) if (ref $text->{command_history}) =~ /ARRAY/; ; # restore effect chains and profiles %Audio::Nama::EffectChain::by_index = (); #say "Project Effect Chain Data\n", json_out( \@project_effect_chain_data); map { my $fx_chain = Audio::Nama::EffectChain->new(%$_) } (@project_effect_chain_data, @global_effect_chain_data); my $fname = $file->midi_store; midish_cmd(qq); } sub convert_rw { my $h = shift; $h->{rw} = MON, return if $h->{rw} eq 'REC' and ($h->{rec_defeat} or $h->{is_mix_track}); $h->{rw} = PLAY, return if $h->{rw} eq 'MON'; } sub is_nonempty_hash { my $ref = shift; return if (ref $ref) !~ /HASH/; return (keys %$ref); } sub save_global_effect_chains { @global_effect_chain_data = map{ $_->as_hash } Audio::Nama::EffectChain::find(global => 1); # always save global effect chain data because it contains # incrementing counter map{ my $format = $_ ; serialize( file => $file->global_effect_chains, format => $format, vars => \@global_effect_chain_vars, class => 'Audio::Nama', ); } $config->serialize_formats; } sub restore_global_effect_chains { logsub("&restore_global_effect_chains"); my $path = $file->global_effect_chains; my ($resolved, $format) = get_newest($path); throw("$resolved: file not found"), return unless $resolved; my $source = read_file($resolved); throw("$resolved: empty file"), return unless $source; logpkg(__FILE__,__LINE__,'debug', "format: $format, source: \n",$source); my $ref = decode($source, $format); assign( data => $ref, vars => \@global_effect_chain_vars, class => 'Audio::Nama'); assign_singletons({ data => $ref }); } 1; __END__Audio-Nama-1.216/lib/Audio/Nama/EngineSetup.pm0000644000175000017500000000147313544212613017751 0ustar jrothjroth# ----------- Engine Setup and Teardown ----------- package Audio::Nama; use Modern::Perl; use Carp; sub reconfigure_engine { logsub("&reconfigure_engine"); my $force = shift; # skip if command line option is set return if ($config->{opts}->{R} or $config->{disable_auto_reconfigure}); update_jack_client_list(); Audio::Nama::Engine::sync_action('configure'); } sub request_setup { my ($package, $filename, $line) = caller(); logpkg(__FILE__,__LINE__,'debug',"reconfigure requested in file $filename:$line"); $setup->{changed}++ } sub generate_setup {Audio::Nama::Engine::sync_action('setup') } sub start_transport { logsub("&start_transport"); Audio::Nama::Engine::sync_action('start'); } sub stop_transport { logsub("&stop_transport"); Audio::Nama::Engine::sync_action('stop'); } 1; __END__Audio-Nama-1.216/lib/Audio/Nama/TrackIO.pm0000644000175000017500000002562513544212613017024 0ustar jrothjrothpackage Audio::Nama::TrackIO; use Role::Tiny; use Modern::Perl; use Audio::Nama::Globals qw(:all); use File::Slurp qw(write_file); use File::Copy; use Audio::Nama::Util qw(dest_string dest_type join_path freq); use Audio::Nama::Log qw(logpkg logsub); sub is_used { my $track = shift; # Track is used if: my $bus = $track->bus; # $track->send_type # It's sending its own signal or $track->{rw} eq REC # It's recording its own signal or $track->wantme # Another track needs my signal or ($bus and $bus->can('wantme') and $bus->wantme) # A bus needs my signal } sub rec_status { # logsub("&rec_status"); my $track = shift; my $bus = $track->bus; return OFF if 0 # ! ($track->engine_group eq $Audio::Nama::this_engine->name) or ! $track->is_used and ! ($mode->doodle and ! $mode->eager and $setup->{tracks_with_duplicate_inputs}->{$track->name} ); return $track->{rw} if $track->{rw} ne PLAY; my $v = $track->playback_version; { no warnings 'uninitialized'; logpkg(__FILE__,__LINE__,'debug', "track: $track->{name}, source: $track->{source_id}, playback version: $v"); } no warnings 'uninitialized'; return maybe_playback($track, $v) if $track->{rw} eq PLAY; } sub maybe_playback { # ordinary sub, not object method my ($track, $playback_version) = @_; return PLAY if $track->targets->{$playback_version} and ! $mode->doodle; return OFF; } sub rec_status_display { my $track = shift; my $rs = $track->rec_status; my $status; $status .= $rs; $status .= ' v'.$track->current_version if $rs eq REC; $status } ### object methods for text-based commands # Reasonable behavior whether 'source' and 'send' commands # are issued in JACK or ALSA mode. sub set_io { my $track = shift; my ($direction, $id, $type) = @_; # $direction: send | source # unless we are dealing with a simple query, # by the end of this routine we are going to assign # the following fields using the values in the # $type and $id variables: # # source_type # source_id # # -OR- # # send_type # send_id my $type_field = $direction."_type"; my $id_field = $direction."_id"; # respond to query if ( ! $id ){ return $track->$type_field ? $track->$id_field : undef } # set values, returning new setting $type ||= dest_type( $id ); if( $type eq 'track') {} elsif( $type eq 'soundcard'){} # no changes needed elsif( $type eq 'bus') {} # -ditto- #elsif( $type eq 'loop') {} # unused at present # don't allow user to set JACK I/O unless JACK server is running elsif( $type =~ /jack/ ){ Audio::Nama::throw("JACK server not running! " ,"Cannot set JACK client or port as track source."), return unless $jack->{jackd_running}; if( $type eq 'jack_manual'){ my $port_name = $track->jack_manual_port($direction); Audio::Nama::pagers($track->name, ": JACK $direction port is $port_name. Make connections manually."); $id = 'manual'; $id = $port_name; $type = 'jack_manual'; } elsif( $type eq 'jack_client'){ my $client_direction = $direction eq 'source' ? 'output' : 'input'; my $name = $track->name; my $width = scalar @{ Audio::Nama::jack_client_array($id, $client_direction) }; $width or Audio::Nama::pagers( qq(Track $name: $direction port for JACK client "$id" not found.)); $width or return; $width ne $track->width and Audio::Nama::pagers( "Track $name set to ", Audio::Nama::width($track->width), qq(, but JACK source "$id" is ), Audio::Nama::width($width), '.'); } elsif( $type eq 'jack_ports_list' ){ $id =~ /(\w+)\.ports/; my $ports_file_name = ($1 || $track->name) . '.ports'; $id = $ports_file_name; # warn if ports do not exist Audio::Nama::throw($track->name, qq(: ports file "$id" not found in ),Audio::Nama::project_root(),". Skipping."), return unless -e join_path( Audio::Nama::project_root(), $id ); # check if ports file parses } } $track->set($type_field => $type); $track->set($id_field => $id); } sub source { # command for setting, showing track source my $track = shift; my ($id, $type) = @_; $track->set_io( 'source', $id, $type); } sub send { # command for setting, showing track source my $track = shift; my ($id, $type) = @_; $track->set_io( 'send', $id, $type); } sub set_source { my $track = shift; my ($source, $type) = @_; my $old_source = $track->input_object_text; $track->set_io('source',$source, $type); my $new_source = $track->input_object_text;; my $object = $new_source; if ( $old_source eq $new_source ){ Audio::Nama::pagers($track->name, ": input unchanged, $object"); } else { Audio::Nama::pagers("Track ",$track->name, ": source set to $object"); } } sub set_version { my ($track, $n) = @_; my $name = $track->name; if ($n == 0){ Audio::Nama::pagers("$name: following bus default"); $track->set(version => $n) } elsif ( grep{ $n == $_ } @{$track->versions} ){ Audio::Nama::pagers("$name: anchoring version $n"); $track->set(version => $n) } else { Audio::Nama::throw("$name: version $n does not exist, skipping.\n") } } sub set_send { my $track = shift; my ($output, $type) = @_; my $old_send = $track->output_object_text; logpkg(__FILE__,__LINE__,'debug', "send was $old_send"); $track->send($output, $type); my $new_send = $track->output_object_text; logpkg(__FILE__,__LINE__,'debug', "send is now $new_send"); my $object = $track->output_object_text; if ( $old_send eq $new_send ){ Audio::Nama::pagers("Track ",$track->name, ": send unchanged, ", ( $object ? $object : 'off')); } else { Audio::Nama::pagers("Track ",$track->name, ": ", $object ? "$object is now a send target" : "send target is turned off."); } } { my %object_to_text = ( soundcard => 'soundcard channel', jack_client => 'JACK client', jack_manual => 'JACK manual port', jack_port => 'JACK manual port', loop => 'loop device', jack_ports_list => "JACK ports list", bus => "bus", midi => 'MIDI input channel', track => 'track', ); sub object_as_text { my ($track, $direction) = @_; # $direction: source | send my $type_field = $direction."_type"; my $id_field = $direction."_id"; { no warnings 'uninitialized'; my $text = $object_to_text{$track->$type_field}; $text .= ' '; $text .= $track->$id_field } } } sub input_object_text { # for text display my $track = shift; $track->object_as_text('source'); } sub output_object_text { # text for user display my $track = shift; $track->object_as_text('send'); } sub source_status { my $track = shift; no warnings 'uninitialized'; return $track->current_wav if $track->play; my $bus = $bn{$track->source_id}; return join " ", $bus->name, $bus->display_type if $track->source_type eq 'bus'; return "track ".$track->source_id if $track->source_type eq 'track'; return 'jack client '.$track->source_id if $track->source_type eq 'jack_client'; if($track->source_type eq 'soundcard') { my $ch = $track->source_id; my @channels; push @channels, $_ for $ch .. ($ch + $track->width - 1); return 'CH '. join '/', @channels } "type: $track->{source_type} id: $track->{source_id}" if $track->{source_id} =~ /\S/ or $track->{source_type} =~ /\S/; } sub destination { my $track = shift; return if $track->off; # display logic # always show the bus # except for tracks that belongs to the bus null. # in that case, show the specific source. # # for these mix tracks, we use the # track's own send_type/send_id my $out; $out .= (join " ", $track->group, $track->bus->display_type) unless $track->group =~ /^(Null)$/; my $send_id = $track->send_id; my $send_type = $track->send_type; return $out if ! $send_type; $out .= ', ' if $out; $out .= dest_string($send_type, $send_id, $track->width); $out } sub set_rec { my $track = shift; if (my $t = $track->target){ my $msg = $track->name; $msg .= qq( is an alias to track "$t"); $msg .= q( in project ") . $track->project . q(") if $track->project; $msg .= qq(.\n); $msg .= "Can't set a track alias to REC.\n"; Audio::Nama::throw($msg); return; } $track->set_rw(REC); } sub rw_set { my $track = shift; logsub("&rw_set"); my ($bus, $rw) = @_; $track->set_rec, return if $rw eq REC; $track->set_rw($rw); } sub set_rw { my ($track, $setting) = @_; #my $already = $track->rw eq $setting ? " already" : ""; $track->set(rw => $setting); my $status = $track->rec_status(); Audio::Nama::pagers("Track ",$track->name, " set to $setting", ($status ne $setting ? ", but current status is $status" : "")); } sub has_insert { $_[0]->prefader_insert or $_[0]->postfader_insert } sub prefader_insert { Audio::Nama::Insert::get_id($_[0],'pre') } sub postfader_insert { Audio::Nama::Insert::get_id($_[0],'post') } sub inserts { [ # return array ref map{ $Audio::Nama::Insert::by_index{$_} }grep{$_} map{ Audio::Nama::Insert::get_id($_[0],$_)} qw(pre post) ] } sub soundcard_channel { $_[0] // 1 } sub import_audio { my $track = shift; Audio::Nama::throw($track->name.": Cannot import audio to system track"), return if ! $track->is_user_track; my ($path, $frequency) = @_; $path = Audio::Nama::expand_tilde($path); my $version = $track->last + 1; if ( ! -r $path ){ Audio::Nama::throw("$path: non-existent or unreadable file. No action.\n"); return; } my ($depth,$width,$freq) = split ',', Audio::Nama::wav_format($path); Audio::Nama::pager_newline("format: ", Audio::Nama::wav_format($path)); $frequency ||= $freq; if ( ! $frequency ){ Audio::Nama::throw("Cannot detect sample rate of $path. Skipping.", "Maybe 'import_audio ' will help."); return } my $desired_frequency = freq( $config->{raw_to_disk_format} ); my $destination = join_path(Audio::Nama::this_wav_dir(),$track->name."_$version.wav"); if ( $frequency == $desired_frequency and $path =~ /.wav$/i){ Audio::Nama::pager_newline("copying $path to $destination"); copy($path, $destination) or die "copy failed: $!"; } else { my $format = Audio::Nama::signal_format($config->{raw_to_disk_format}, $width); Audio::Nama::pager_newline("importing $path as $destination, converting to $format"); Audio::Nama::teardown_engine(); my $ecs = qq(-f:$format -i:resample-hq,$frequency,"$path" -o:$destination); my $path = join_path(Audio::Nama::project_dir()."convert.ecs"); write_file($path, $ecs); Audio::Nama::load_ecs($path) or Audio::Nama::throw("$path: load failed, aborting"), return; Audio::Nama::ecasound_iam('start'); Audio::Nama::sleeper(0.2); sleep 1 while $this_engine->running(); } Audio::Nama::restart_wav_memoize() if $config->{opts}->{R}; # usually handled by reconfigure_engine() } sub port_name { $_[0]->target || $_[0]->name } sub jack_manual_port { my ($track, $direction) = @_; $track->port_name . ($direction =~ /source|input/ ? '_in' : '_out'); } sub wantme { my $track = shift; no warnings 'uninitialized'; my @wantme = grep{ $_->name ne $track->name and $_->source_type eq 'track' and $_->source_id eq $track->name and ($_->rec or $_->mon) } Audio::Nama::all_tracks(); @wantme } 1; Audio-Nama-1.216/lib/Audio/Nama/Bus.pm0000644000175000017500000002400013544212613016243 0ustar jrothjroth# ------------ Bus -------------------- { package Audio::Nama::Bus; use Modern::Perl; use Carp; use Audio::Nama::Log qw(logsub logpkg); use Audio::Nama::Globals qw(:trackrw $setup); our @ISA = qw( Audio::Nama::Object ); # share the following variables with subclasses our $VERSION = 1.0; our (%by_name); use Audio::Nama::Object qw( name rw version send_type send_id engine_group class ); sub initialize { %by_name = (); }; sub new { my $class = shift; my %vals = @_; my @undeclared = grep{ ! $_is_field{$_} } keys %vals; croak "undeclared field: @undeclared" if @undeclared; if (! $vals{name}){ Audio::Nama::throw("missing bus name"); return } if ( $by_name{$vals{name}} ){ #Audio::Nama::throw("$vals{name}: bus name already exists. Skipping.") unless $Audio::Nama::quiet; return; } my $bus = bless { class => $class, # for serialization, may be overridden rw => MON, # for group control @_ }, $class; $by_name{$bus->name} = $bus; } sub group { $_[0]->name } sub tracks { # returns list of track names in bus my $bus = shift; map{ $_->name } $bus->track_o; } sub track_o { my $bus = shift; grep{ $_->group eq $bus->name } Audio::Nama::all_tracks(); } sub last { #logpkg(__FILE__,__LINE__,'debug', "group: @_"); my $bus = shift; my $max = 0; map{ my $track = $_; my $last; $last = $track->last || 0; #print "track: ", $track->name, ", last: $last\n"; $max = $last if $last > $max; } $bus->track_o; $max; } sub remove { Audio::Nama::throw($_[0]->name, " is system bus. No can remove.") } sub tracks_on { my $bus = shift; for ( $bus->track_o ) { my $old = $setup->{bus}->{oldrw}->{$_->name}; $_->set( rw => $old) if $old; delete $setup->{bus}->{oldrw}->{$_->name }; } } sub tracks_off { my $bus = shift; return if not grep { $_->rw ne OFF } $bus->track_o; for ( $bus->track_o ) { delete $setup->{bus}->{oldrw}->{$_->name }; next if $_->rw eq OFF; $setup->{bus}->{oldrw}->{$_->name } = $_->rw; $_->set( rw => OFF ); } } ## class methods # all buses that have mutable state, and therefore reason to # save or display that state sub all { values %by_name } sub overall_last { my $max = 0; map{ my $last = $_->last; $max = $last if $last > $max } all(); $max; } sub settings_line { my ($mix,$bus) = @_; my $nothing = '-' x 77 . "\n"; #return if $maybe_mix->name eq 'Main' or $maybe_mix->group eq 'Mastering'; return unless defined $mix; my ($bustype) = $bus->class =~ /(\w+)$/; my $line = join " ", $bustype ,$bus->name; $line .= " Version setting".$bus->version if $bus->version; #$line .= "feeds", $line .= join " "," Mix track",$mix->name,"is",$mix->rec_status; $line = "------[$line]"; $line .= '-' x (77 - length $line); $line .= "\n"; $line } sub trackslist { my $bus = shift; my $mix = $Audio::Nama::tn{$bus->name}; # XX this will work only for mix tracks with the same name as their bus my @list = ($mix,$bus); push @list, map{$Audio::Nama::tn{$_}} ($mix->name, $bus->tracks); \@list; } sub apply {} # base class does no routing of its own sub display_type { my ($type) = $_[0]->class =~ /([^:]+)$/; $type = lc $type; $type =~ s/sub//; $type } sub list { my $self = shift; my @fields = qw( name rw version send_type send_id class ); # version # engine_group my $list = join "", map{"$_: ".$self->$_."\n"} grep {$self->$_} @fields; } ### subclasses { package Audio::Nama::SubBus; # with magic for Main bus use Modern::Perl; use Carp; our @ISA = 'Audio::Nama::Bus'; use Audio::Nama::Log qw(logsub logpkg); use Audio::Nama::Util qw(input_node); use Audio::Nama::Globals qw(:trackrw %tn); # connect source --> member_track --> mix_track sub apply { no warnings 'uninitialized'; my ($bus, $g) = @_; logpkg(__FILE__,__LINE__,'debug', "bus ". $bus->name. ": applying routes"); logpkg(__FILE__,__LINE__,'debug', "Bus destination is type: $bus->{send_type}, id: $bus->{send_id}"); my @wantme = $bus->wantme; logpkg(__FILE__,__LINE__,'debug', "bus ". $bus->name. " consumed by ".$_->name) for @wantme; map{ my $member = $_; # connect member track input paths logpkg(__FILE__,__LINE__,'debug', "track ".$_->name); my @path = $member->input_path; $g->add_path(@path) if @path; logpkg(__FILE__,__LINE__,'debug',"input path: @path") if scalar @path; # connect member track outputs to target for (@wantme) { my $mix_track = $_; Audio::Nama::Graph::add_path_for_send($g, $member->name, 'track', $mix_track->name) } # add paths for recording Audio::Nama::Graph::add_path_for_rec($g,$_) if $_->rec and ! $Audio::Nama::mode->preview and ! $Audio::Nama::mode->doodle; } grep {$_->rec_status ne OFF} $bus->track_o; } sub remove { my $bus = shift; # all tracks returned to Main group map{$_->set(group => 'Main') } $bus->track_o; my $mix_track = $Audio::Nama::tn{$bus->name}; # remove mix track unless it has some WAV files $mix_track->remove if defined $mix_track and not scalar @{ $mix_track->versions }; # remove bus from index delete $Audio::Nama::bn{$bus->name}; } sub wantme { my $bus = shift; no warnings 'uninitialized'; my @wantme = grep{ $_->{rw} =~ /REC|MON/ and $_->source_type eq 'bus' and $_->source_id eq $bus->name and $_->is_used} Audio::Nama::all_tracks(); @wantme } } { package Audio::Nama::SendBusRaw; use Modern::Perl; use Carp; our @ISA = 'Audio::Nama::Bus'; use Audio::Nama::Log qw(logsub logpkg); sub apply { my $bus = shift; map{ my @input_path = $_->input_path; $Audio::Nama::g->add_edge(@input_path); $Audio::Nama::g->set_edge_attributes( @input_path, { width => $Audio::Nama::tn{$_->target}->width }); my @edge = ($_->name, Audio::Nama::output_node($bus->send_type)); $Audio::Nama::g->add_edge(@edge); $Audio::Nama::g->set_edge_attributes( @edge, { send_id => $bus->send_id, width => 2 }); # force to stereo } grep{ $_->input_path } $bus->track_o; } sub remove { my $bus = shift; # delete all tracks map{$_->remove } $bus->track_o; # remove bus delete $by_name{$bus->name}; } } { package Audio::Nama::SendBusCooked; use Audio::Nama::Log qw(logsub logpkg); use Modern::Perl; use Carp; our @ISA = 'Audio::Nama::SendBusRaw'; # graphic routing: target -> slave -> bus_send_type sub apply { my $bus = shift; my $g = shift; map{ my @edge = ($_->name, Audio::Nama::output_node($bus->send_type)); $g->add_path( $_->target, @edge); $g->set_edge_attributes( @edge, { send_type => $bus->send_type, send_id => $bus->send_id, width => 2}) } $bus->track_o; } } { package Audio::Nama::MidiBus; use Modern::Perl; use Carp; our @ISA = 'Audio::Nama::Bus'; use Audio::Nama::Log qw(logsub logpkg); use Audio::Nama::Util qw(input_node); use Audio::Nama::Globals qw(:trackrw); sub apply { my ($bus) = @_; logpkg(__FILE__,__LINE__,'debug', "bus ". $bus->name. ": applying routes"); logpkg(__FILE__,__LINE__,'debug', "Bus destination is type: $bus->{send_type}, id: $bus->{send_id}"); # } sub remove { } # We never remove the Midi bus } # ---------- Bus routines -------- { package Audio::Nama; use Modern::Perl; use Carp; use Audio::Nama::Util qw(dest_type); our ( $this_track, $this_bus, %tn, %bn, ); sub set_current_bus { my $track = shift || ($this_track ||= $tn{Main}); return unless $track; # needed for test environment # The current sequence changes when the user touches a # track that belongs to another sequence. $this_sequence = $bn{$track->group} if (ref $bn{$track->group}) =~ /Sequence/; my $bus_name = $track->name =~ /Main|Mixdown/ ? 'Main' : $track->is_mixing() ? $track->name : $track->group; select_bus($bus_name); } sub select_bus { my $name = shift; my $bus = $bn{$name} or return; $this_bus = $name; $this_bus_o = $bus; } sub add_bus { # creates named bus if necessary # creates named mix track if necessary # sets mix track to MON with bus as source my ($name, @args) = @_; # don't create bus if such named already exists Audio::Nama::SubBus->new( name => $name, send_type => 'track', send_id => $name, ) unless $bn{$name}; my $bus = $bn{$name}; # modify bus and track settings to activate bus $bus->set(rw => MON); @args = ( rw => MON, source_type => 'bus', source_id => $name, @args ); $tn{$name} and pager_newline( qq($name: setting as mix track for bus "$name")); my $track = $tn{$name}// add_track($name, width => 2); $track->set( @args ); } sub add_submix { my ($name, $dest_id, $bus_type) = @_; my $dest_type = dest_type( $dest_id ); # dest_type: soundcard | jack_client | loop | jack_port | jack_multi logpkg(__FILE__,__LINE__,'debug',"name: $name, dest_type: $dest_type, dest_id: $dest_id"); if ($bn{$name} and (ref $bn{$name}) !~ /SendBus/){ Audio::Nama::throw($name,": bus name already in use. Aborting."), return; } if ($bn{$name}){ Audio::Nama::pager_newline( qq(monitor bus "$name" already exists. Updating with new tracks.) ); } else { my @args = ( name => $name, send_type => $dest_type, send_id => $dest_id, ); my $class = $bus_type eq 'cooked' ? 'Audio::Nama::SendBusCooked' : 'Audio::Nama::SendBusRaw'; my $bus = $class->new( @args ); $bus or carp("can't create bus!\n"), return; } map{ Audio::Nama::EarTrack->new( name => "$name\_$_", # BusName_TrackName rw => MON, target => $_, group => $name, width => 2, hide => 1, ) } $bn{Main}->tracks; } sub update_submix { my $name = shift; add_submix( $name, $bn{$name}->send_id), "dummy", } sub remove_submix_helper_tracks { my $name = shift; #say "got name: $name"; my @submixes = submixes(); #say "got submixes:", Dumper \@submixes; for my $sm ( @submixes ){ my $to_remove = join '_', $sm->name, $name; #say "to_remove: $to_remove"; local $quiet; $quiet++; for my $name ($sm->tracks) { $tn{$name}->remove, last if $name eq $to_remove } } } sub submixes { grep { (ref $_) =~ /SendBusCooked/ } values %Audio::Nama::Bus::by_name } } } 1; __END__Audio-Nama-1.216/lib/Audio/Nama/TrackComment.pm0000644000175000017500000000427413544212613020114 0ustar jrothjrothpackage Audio::Nama::TrackComment; use Role::Tiny; use Modern::Perl; use Audio::Nama::Globals qw($project); sub is_comment { my $self = shift; $Audio::Nama::project->{track_comments}->{$self->name} } sub is_version_comment { my $self = shift; my $version = shift; { no warnings 'uninitialized'; my $comments = $project->{track_version_comments}->{$self->name}->{$version}; $comments and $comments->{user} } } sub set_comment { my ($track, $comment) = @_; $project->{track_comments}->{$track->name} = $comment } sub comment { my ($track) = @_; $project->{track_comments}->{$track->name} } sub version_comment { my ($track, $v) = @_; return unless $project->{track_version_comments}->{$track->name}{$v}; my $text = $project->{track_version_comments}->{$track->name}{$v}{user}; $text .= " " if $text; my $system = $project->{track_version_comments}->{$track->name}{$v}{system}; $text .= "* $system" if $system; $track->name." version $v: $text\n" if $text; } sub show_version_comments { my ($track, @v) = @_; return unless @v; Audio::Nama::pager(map{ $track->version_comment($_) } @v); } sub add_version_comment { my ($track,$v,$text) = @_; $track->targets->{$v} or Audio::Nama::throw("$v: no such version"), return; $project->{track_version_comments}->{$track->name}{$v}{user} = $text; $track->version_comment($v); } sub add_system_version_comment { my ($track,$v,$text) = @_; $track->targets->{$v} or Audio::Nama::throw("$v: no such version"), return; $project->{track_version_comments}{$track->name}{$v}{system} = $text; $track->system_version_comment($v); } sub remove_version_comment { my ($track,$v) = @_; $track->targets->{$v} or Audio::Nama::throw("$v: no such version"), return; delete $project->{track_version_comments}{$track->name}{$v}{user}; $track->version_comment($v) || "$v: [comment deleted]\n"; } sub remove_system_version_comment { my ($track,$v) = @_; delete $project->{track_version_comments}{$track->name}{$v}{system} if $project->{track_version_comments}{$track->name}{$v} } sub system_version_comment { my ($track, $v) = @_; return unless $project->{track_version_comments}->{$track->name}{$v}; $project->{track_version_comments}->{$track->name}{$v}{system}; } 1;Audio-Nama-1.216/lib/Audio/Nama/Insert.pm0000644000175000017500000002417013544212613016766 0ustar jrothjroth{ package Audio::Nama::Insert; use Modern::Perl; use Carp; no warnings qw(uninitialized redefine); our $VERSION = 0.1; our %by_index; use Audio::Nama::Log qw(logpkg); use Audio::Nama::Log qw(logpkg); use Audio::Nama::Globals qw($jack $setup $config :trackrw); use Audio::Nama::Object qw( n class send_type send_id return_type return_id wet_track dry_track tracks track wetness wet_vol dry_vol ); use Audio::Nama::Util qw(input_node output_node dest_type); initialize(); sub initialize { %by_index = () } sub idx { # return first free index my $n = 0; while (++$n){ return $n if not $by_index{$n} } } sub wet_name { my $self = shift; join('-', $self->track, $self->n, 'wet'); } sub dry_name { my $self = shift; join('-', $self->track, $self->n, 'dry'); } sub new { my $class = shift; my %vals = @_; my @undeclared = grep{ ! $_is_field{$_} } keys %vals; croak "undeclared field: @undeclared" if @undeclared; $vals{n} ||= idx(); my $self = bless { class => $class, # for restore wetness => 100, %vals, }, $class; my $name = $vals{track}; # this is the wet return track my $wet = Audio::Nama::SlaveTrack->new( name => $self->wet_name, target => $name, group => 'Insert', rw => MON, # don't hide wet track if used for hosting effects hide => ! $self->is_local_effects_host, ); my $dry = Audio::Nama::SlaveTrack->new( name => $self->dry_name, target => $name, group => 'Insert', hide => 1, rw => MON); map{ Audio::Nama::remove_effect($_)} $wet->vol, $wet->pan, $dry->vol, $dry->pan; map{ my $track = $_; map{ delete $track->{$_} } qw(vol pan) } $wet, $dry; $self->{dry_vol} = Audio::Nama::add_effect({ track => $dry, type => 'ea', params => [0] }); $self->{wet_vol} = Audio::Nama::add_effect({ track => $wet, type => 'ea', params => [100], }); # synchronize effects with wetness setting $self->set_wetness($self->{wetness}); $by_index{$self->n} = $self; } # method name for track field holding insert sub type { (ref $_[0]) =~ /Pre/ ? 'prefader_insert' : 'postfader_insert' } #sub remove {} # subroutine # sub add_insert { my ($track, $type, $send_id, $return_id) = @_; local $Audio::Nama::this_track; # $type : prefader_insert | postfader_insert Audio::Nama::pager("\n",$track->name , ": adding $type\n"); my $name = $track->name; # the input fields will be ignored, since the track will get input # via the loop device track_insert my $class = $type =~ /pre/ ? 'Audio::Nama::PreFaderInsert' : 'Audio::Nama::PostFaderInsert'; # remove an existing insert of specified type, if present $track->$type and $by_index{$track->$type}->remove; my $i = $class->new( track => $track->name, send_type => Audio::Nama::dest_type($send_id), send_id => $send_id, return_type => Audio::Nama::dest_type($return_id), return_id => $return_id, ); if (! $i->{return_id}){ $i->{return_type} = $i->{send_type}; $i->{return_id} = $i->{send_id} if $i->{return_type} eq 'jack_client'; $i->{return_id} = $i->{send_id} + 2 if $i->{return_type} eq 'soundcard'; # TODO adjust to suit track channel width? } } sub get_id { # get Insert index for track # optionally specify whether we are looking for # prefader or postfader insert # my ($track, $prepost) = @_; my @inserts = $track->get_inserts; my ($prefader) = (map{$_->n} grep{$_->class =~ /pre/i} @inserts); my ($postfader) = (map{$_->n} grep{$_->class =~ /post/i} @inserts); my %id = ( pre => $prefader, post => $postfader); $prepost = $id{pre} ? 'pre' : 'post' if (! $prepost and ! $id{pre} != ! $id{post} ); $id{$prepost};; } sub is_local_effects_host { ! $_[0]->send_id } sub set_wetness { my ($self, $p) = @_; $self->{wetness} = $p; Audio::Nama::modify_effect($self->wet_vol, 1, undef, $p); Audio::Nama::sleeper(0.1); Audio::Nama::modify_effect($self->dry_vol, 1, undef, 100 - $p); } sub is_via_soundcard { my $self = shift; for (qw(source send)){ my $type = "$_\_type"; my $id = "$_\_id"; return 0 unless is_channel($self->$id) or $self->$type eq 'soundcard' or is_jack_soundcard($self->$id) } sub is_channel { $_[0] =~ /^\d+$/ } sub is_jack_soundcard { $_[0] =~ /^system/ } } sub soundcard_delay { my $track_name = shift; my ($insert) = grep{ $_->wet_name eq $track_name } values %by_index; my $delta = 0; $delta = $config->{soundcard_loopback_delay} if defined $insert and $insert->is_via_soundcard; Audio::Nama::Lat->new($delta,$delta) } } { package Audio::Nama::PostFaderInsert; use Modern::Perl; use Carp; our @ISA = qw(Audio::Nama::Insert); use Audio::Nama::Util qw(input_node output_node dest_type); use Audio::Nama::Log qw(logpkg); sub add_paths { # Since this routine will be called after expand_graph, # we can be sure that every track vertex will connect to # to a single edge, either loop or an output my ($self, $g, $name) = @_; no warnings qw(uninitialized); Audio::Nama::logpkg(__FILE__,__LINE__,'debug', "add_insert for track: $name"); my $t = $Audio::Nama::tn{$name}; Audio::Nama::logpkg(__FILE__,__LINE__,'debug', "insert structure: ", sub{$self->dump}); my ($successor) = $g->successors($name); # successor will be either a loop, device or JACK port # i.e. can accept multiple signals $g->delete_edge($name, $successor); my $loop = "$name\_insert_post"; my $wet = $Audio::Nama::tn{$self->wet_name}; my $dry = $Audio::Nama::tn{$self->dry_name}; Audio::Nama::logpkg(__FILE__,__LINE__,'debug', "found wet: ", $wet->name, " dry: ",$dry->name); # if no insert target, our insert will # a parallel effects host with wet/dry dry branches # --- track ---insert_post--+--- wet ---+-- successor # | | # +--- dry ---+ # otherwise a conventional wet path with send and receive arms # --- track ---insert_post--+-- wet-send wet-return ---+-- successor # | | # +-------------- dry ----------+ if ( $self->is_local_effects_host ) { $g->add_path($name, $loop, $wet->name, $successor); } else { # wet send path (no extra track): track -> loop -> output my @edge = ($loop, output_node($self->{send_type})); Audio::Nama::logpkg(__FILE__,__LINE__,'debug', "edge: @edge"); $g->add_path( $name, @edge); $g->set_vertex_attributes($loop, {n => $t->n}); $g->set_edge_attributes(@edge, { send_id => $self->{send_id}, width => 2, }); # wet return path: input -> wet_track (slave) -> successor # we override the input with the insert's return source $g->set_vertex_attributes($wet->name, { width => 2, # default for cooked mono_to_stereo => '', # override source_type => $self->{return_type}, source_id => $self->{return_id}, }); $g->add_path(input_node($self->{return_type}), $wet->name, $successor); } # connect dry track to graph $g->add_path($loop, $dry->name, $successor); } sub remove { my $self = shift; $Audio::Nama::tn{ $self->wet_name }->remove; $Audio::Nama::tn{ $self->dry_name }->remove; delete $Audio::Nama::Insert::by_index{$self->n}; } } { package Audio::Nama::PreFaderInsert; use Modern::Perl; use Carp; our @ISA = qw(Audio::Nama::Insert); use Audio::Nama::Util qw(input_node output_node dest_type); use Audio::Nama::Log qw(logpkg); use Audio::Nama::Globals qw(:trackrw); # --- source ---------- wet_send_track wet_return_track -+-- insert_pre -- track # | # --- source ------------------ dry track ----------------+ sub new { my ($class, %args) = @_; my $self = $class->SUPER::new(%args); my $wet_send = Audio::Nama::SlaveTrack->new( name => $self->wet_send_name, target => $self->track, group => 'Insert', hide => 1, rw => REC ); if ($wet_send->width == 1){ Audio::Nama::add_effect({ track => $wet_send, type => 'chcopy', params => [1,2] }); } map{ Audio::Nama::remove_effect($_)} $wet_send->vol, $wet_send->pan; map{ my $track = $_; map{ delete $track->{$_} } qw(vol pan) } $wet_send; $self } sub wet_send_name { my $self = shift; join('-', $self->track, $self->n, 'wet-send'); } sub add_paths { my ($self, $g, $name) = @_; no warnings qw(uninitialized); Audio::Nama::logpkg(__FILE__,__LINE__,'debug', "add_insert for track: $name"); my $t = $Audio::Nama::tn{$name}; Audio::Nama::logpkg(__FILE__,__LINE__,'debug', "insert structure:", sub{$self->dump}); # get track source my ($predecessor) = $g->predecessors($name); # delete source connection to track $g->delete_edge($predecessor, $name); my $loop = "$name\_insert_pre"; my $wet = $Audio::Nama::tn{$self->wet_name}; my $dry = $Audio::Nama::tn{$self->dry_name}; my $wet_send = $Audio::Nama::tn{$self->wet_send_name}; Audio::Nama::logpkg(__FILE__,__LINE__,'debug', "found wet: ", $wet->name, " dry: ",$dry->name); #pre: wet send path: wet_send_name (slave) -> output my @edge = ($self->wet_send_name, output_node($self->send_type)); $g->add_path($predecessor, @edge); Audio::Nama::logpkg(__FILE__,__LINE__,'debug', "edge: @edge"); $g->set_vertex_attributes($self->wet_send_name, { send_id => $self->{send_id}, send_type => $self->{send_type}, mono_to_stereo => '', # disable for prefader send path }); #pre: wet return path: input -> wet_track (slave) -> loop # we override the input with the insert's return source $g->set_vertex_attributes($wet->name, { width => $t->width, mono_to_stereo => '', # override source_type => $self->{return_type}, source_id => $self->{return_id}, }); $g->set_vertex_attributes($dry->name, { mono_to_stereo => '', # override }); $g->add_path(input_node($self->{return_type}), $wet->name, $loop); # connect dry track to graph # # post: dry path: loop -> dry -> successor # pre: dry path: predecessor -> dry -> loop $g->add_path($predecessor, $dry->name, $loop, $name); } sub remove { my $self = shift; $Audio::Nama::tn{ $self->wet_send_name }->remove; $Audio::Nama::tn{ $self->dry_name }->remove; $Audio::Nama::tn{ $self->wet_name }->remove; delete $Audio::Nama::Insert::by_index{$self->n}; } } 1;Audio-Nama-1.216/lib/Audio/Nama/Regions.pm0000644000175000017500000000205713544212613017130 0ustar jrothjroth# ------------ Set region for current track ---------- package Audio::Nama; use Modern::Perl; use Carp; sub set_region { my ($beg, $end) = @_; $this_track->set(region_start => $beg); $this_track->set(region_end => $end); show_region(); } sub new_region { my ($beg, $end, $name) = @_; $name ||= new_region_name(); add_track_alias($name, $this_track->name); set_region($beg,$end); } sub new_region_name { my $name = $this_track->name . '_region_'; my $i; map{ my ($j) = /_(\d+)$/; $i = $j if $j > $i; } grep{/$name/} keys %Audio::Nama::Track::by_name; $name . ++$i } sub remove_region { if (! $this_track->region_start){ throw($this_track->name, ": no region is defined. Skipping."); return; } elsif ($this_track->target ){ pager($this_track->name, ": looks like a region... removing."); $this_track->remove; } else { undefine_region() } } sub undefine_region { $this_track->set(region_start => undef ); $this_track->set(region_end => undef ); pager($this_track->name, ": Region definition removed. Full track will play.\n"); } 1; __END__Audio-Nama-1.216/lib/Audio/Nama/Git.pm0000644000175000017500000001334513544212613016247 0ustar jrothjroth# ---------- Git Support ---------- package Audio::Nama; use Modern::Perl; sub git { return if is_test_script(); $config->{use_git} or warn("@_: git command, but git is not enabled. You may want to set use_git: 1 in .namarc"), return; logpkg(__FILE__,__LINE__,'debug',"VCS command: git @_"); $project->{repo}->run(@_) } sub initialize_project_git_repository { logsub('&initialize_project_git_repository'); confess("no project dir") if ! project_dir(); return unless $config->{use_git} and not is_test_script(); pager("Creating git repository in ", join_path( project_dir(), '.git' )) if ! -d join_path( project_dir(), '.git' ); Git::Repository->run( init => project_dir()); $project->{repo} = Git::Repository->new( work_tree => project_dir() ); write_file($file->git_state_store, "{}\n") if ! -e $file->git_state_store; git( add => $file->git_state_store ); write_file($file->midi_store, ""), git( add => $file->midi_store ) if ! -e $file->midi_store; git( commit => '--quiet', '--message', "initialize project"); } sub git_tag_exists { logsub('&git_tag_exists'); my $tag = shift; grep { $tag eq $_ } git( 'tag','--list'); } # on command "get foo", Nama opens a branch name 'foo-branch', # or returns to HEAD of existing branch 'foo-branch' sub tag_branch { "$_[0]-branch" } sub restore_state_from_vcs { logsub("&restore_state_from_vcs"); my $name = shift; # tag or branch # checkout branch if matching branch exists if (git_branch_exists($name)){ pager_newline( qq($name: branch exists. Checking out branch $name.) ); git_checkout($name); } # checkout branch diverging at tag if matching that tag elsif ( git_tag_exists($name) ){ my $tag = $name; my $branch = tag_branch($tag); if (git_branch_exists($branch)){ pager_newline( qq(tag $tag: matching branch exists. Checking out $branch.) ); git_checkout($branch); } else { pager_newline( "Creating and checking out branch $branch from tag $tag"); git_create_branch($branch, $tag); } } else { throw("$name: tag doesn't exist. Cannot checkout."), return } restore_state_from_file(); } sub git_snapshot { logsub("&git_snapshot"); my $commit_message = shift() || ""; return unless $config->{use_git} and ! $config->{opts}->{R}; save_state(); reset_command_buffer(), return unless state_changed(); git_commit($commit_message); } sub reset_command_buffer { $project->{command_buffer} = [] } sub git_commit { logsub("&git_commit"); my $commit_message = shift; no warnings 'uninitialized'; use utf8; scalar @{$project->{command_buffer}} and $commit_message .= join "\n", undef, (map{ $_->{command} } @{$project->{command_buffer}}), # context for first command "* track: $project->{command_buffer}->[0]->{context}->{track}", "* bus: $project->{command_buffer}->[0]->{context}->{bus}", "* op: $project->{command_buffer}->[0]->{context}->{op}", git( add => $file->git_state_store ); git( commit => '--quiet', '--message', $commit_message); reset_command_buffer(); } sub git_checkout { logsub("&git_checkout"); my ($branchname, @args) = @_; return unless $config->{use_git}; my $exist_message = git_branch_exists($branchname) ? undef : "$branchname: branch does not exist."; my $dirty_tree_msg = !! state_changed() ? "You have changes to working files. You cannot switch branches until you commit these changes, or throw them away." : undef; my $conjunction = ($dirty_tree_msg and $exist_message) ? "And by the way, " : undef; throw( $dirty_tree_msg, $conjunction, $exist_message, "No action taken."), return if $dirty_tree_msg or $exist_message; git(checkout => $branchname, @args); } sub git_create_branch { logsub("&git_create_branch"); my ($branchname, $branchfrom) = @_; return unless $config->{use_git}; # create new branch my @args; my $from_target; $from_target = "from $branchfrom" if $branchfrom; push @args, $branchname; push(@args, $branchfrom) if $branchfrom; pager("Creating branch $branchname $from_target"); git(checkout => '-b', @args) } sub state_changed { logsub("&state_changed"); return unless $config->{use_git}; git("diff"); } sub git_branch_exists { logsub("&git_branch_exists"); return unless $config->{use_git}; my $branchname = shift; grep{ $_ eq $branchname } map{ s/^\s+//; s/^\* //; $_} git("branch"); } sub current_branch { logsub("¤t_branch"); return unless $project->{repo}; my ($b) = map{ /\* (\S+)/ } grep{ /\*/ } split "\n", git('branch'); $b } sub git_sha { my $commit = shift || 'HEAD'; my ($sha) = git(show => $commit) =~ /commit ([0-9a-f]{10})/; $sha } sub git_branch_display { logsub("&git_branch_display"); my $display = $Audio::Nama::project->{name}; return $display unless $config->{use_git}; my $cb = current_branch(); $display .= ":$cb" if $cb and $cb ne 'master'; $display } sub list_branches { pager_newline( "---Branches--- (asterisk marks current branch)", $project->{repo}->run('branch'), "", "-----Tags-----", $project->{repo}->run('tag','--list') ); } sub autosave { logsub("&autosave"); $this_engine->started() ? return : git_snapshot(); } sub redo { if ($project->{redo}){ git('cherry-pick',$project->{redo}); load_project(name => $project->{name}); delete $project->{redo}; } else {throw("nothing to redo")} 1 } sub undo { pager("removing last commit"); local $quiet = 1; # get the commit id my $show = git(qw/show HEAD/); my ($commit) = $show =~ /commit ([a-z0-9]{10})/; # blow it away git(qw/reset --hard HEAD^/); load_project( name => $project->{name}); # remember it $project->{redo} = $commit; } sub show_head_commit { my $show = git(qw/show HEAD/); my ($commit) = $show =~ /commit ([a-z0-9]{10})/; my (undef,$msg) = split "\n\n",$show; pager_newline("commit: $commit",$msg); } 1Audio-Nama-1.216/lib/Audio/Nama/Sequence.pm0000644000175000017500000001231613544212613017271 0ustar jrothjrothpackage Audio::Nama::Sequence; use Modern::Perl; use Carp; use Audio::Nama::Assign qw(json_out); use Audio::Nama::Log qw(logsub logpkg); use Audio::Nama::Effect qw(fxn modify_effect); use Audio::Nama::Object qw( items clip_counter ); use Audio::Nama::Globals qw(:trackrw); our @ISA = 'Audio::Nama::SubBus'; our $VERSION = 1.0; use SUPER; our %by_name; # alias to %Audio::Nama::Bus::by_name *by_name = \%Audio::Nama::Bus::by_name; sub new { my ($class,%args) = @_; # take out args we will process my $items = delete $args{items}; my $counter = delete $args{clip_counter}; #logpkg(__FILE__,__LINE__,'debug', "items: ",map{json_out($_->as_hash)}map{$Audio::Nama::tn{$_}}@$items) if $items; $items //= []; @_ = ($class, %args); my $self = super(); logpkg(__FILE__,__LINE__,'debug',"new object: ", json_out($self->as_hash)); logpkg(__FILE__,__LINE__,'debug', "items: ",json_out($items)); $self->{clip_counter} = $counter; $self->{items} = $items; $Audio::Nama::this_sequence = $self; $self; } sub clip { my ($self, $index) = @_; return 0 if $index <= 0; $Audio::Nama::tn{$self->{items}->[$index - 1]} } sub rw { my $self = shift; $Audio::Nama::mode->{offset_run} ? OFF : $self->{rw} } # perl indexes arrays at zero, for nama users we number items from one sub insert_item { my $self = shift; my ($item, $index) = @_; $self->append_item($item), return if $index == @{$self->{items}} + 1; $self->verify_item($index) or die "$index: sequence index out of range"; splice @{$self->{items}}, $index - 1,0, $item->name } sub verify_item { my ($self, $index) = @_; $index >= 1 and $index <= scalar @{$self->items} } sub delete_item { my $self = shift; my $index = shift; $self->verify_item($index) or die "$index: sequence index out of range"; my $trackname = splice(@{$self->{items}}, $index - 1, 1); $Audio::Nama::tn{$trackname} and $Audio::Nama::tn{$trackname}->remove; } sub append_item { my $self = shift; my $item = shift; push( @{$self->{items}}, $item->name ); } sub item { my $self = shift; my $index = shift; return 0 if $index <= 0; $Audio::Nama::tn{$self->{items}->[$index - 1]}; } sub list_output { my $self = shift; my $i; join "\n","Sequence $self->{name} clips:", map { join " ", ++$i, $Audio::Nama::tn{$_}->n, $_, sprintf("%.3f %.3f", $Audio::Nama::tn{$_}->duration, $Audio::Nama::tn{$_}->endpoint), } @{$self->items} } sub remove { my $sequence = shift; # delete all clips map{$Audio::Nama::tn{$_}->remove } $by_name{$sequence->name}->tracks; # delete clip array delete $sequence->{items}; my $mix_track = $Audio::Nama::tn{$sequence->name}; if ( defined $mix_track ){ $mix_track->set(rw => OFF); # remove mix track unless it has some WAV files $mix_track->remove unless scalar @{ $mix_track->versions }; } # remove sequence from index delete $by_name{$sequence->name}; } sub new_clip { my ($self, $track, %args) = @_; # $track can be object or name my $markpair = delete $args{region}; logpkg(__FILE__,__LINE__,'debug',json_out($self->as_hash), json_out($track->as_hash)); ref $track or $track = $Audio::Nama::tn{$track} or die("$track: track not found."); my %region_args = ( region_start => $markpair && $markpair->[0]->name || $track->region_start, region_end => $markpair && $markpair->[1]->name || $track->region_end ); my $clip = Audio::Nama::Clip->new( target => $track->basename, name => $self->unique_clip_name($track->name, $track->playback_version), rw => PLAY, group => $self->name, version => $track->playback_version, hide => 1, %region_args, %args ); modify_effect( $clip->vol, 1, undef, fxn($track->vol)->params->[0]); modify_effect( $clip->pan, 1, undef, fxn($track->pan)->params->[0]); $clip } sub new_spacer { my( $self, %args ) = @_; my $position = delete $args{position}; my $spacer = Audio::Nama::Spacer->new( duration => $args{duration}, name => $self->unique_spacer_name(), rw => OFF, group => $self->name, ); $self->insert_item( $spacer, $position || ( scalar @{ $self->{items} } + 1 )) } sub unique_clip_name { my ($self, $trackname, $version) = @_; join '-', $self->name , ++$self->{clip_counter}, $trackname, 'v'.$version; } sub unique_spacer_name { my $self = shift; join '-', $self->name, ++$self->{clip_counter}, 'spacer'; } package Audio::Nama; sub new_sequence { my %args = @_; my $name = $args{name}; my @tracks = defined $args{tracks} ? @{ $args{tracks} } : (); my $group = $args{group} || 'Main'; my $mix_track = $tn{$name} || add_track($name, group => $group); $mix_track->set( rw => MON, source_type => 'bus', source_id => $name, ); my $sequence = Audio::Nama::Sequence->new( name => $name, ); ; map{ $sequence->append_item($_) } map{ $sequence->new_clip($_)} @tracks; $this_sequence = $sequence; } sub compose_sequence { my ($sequence_name, $track, $markpairs) = @_; logpkg(__FILE__,__LINE__,'debug',"sequence_name: $sequence_name, track:", $track->name, ", markpairs: ",Audio::Nama::Dumper $markpairs); my $sequence = new_sequence( name => $sequence_name); logpkg(__FILE__,__LINE__,'debug',"sequence\n",Audio::Nama::Dumper $sequence); my @clips = map { $sequence->new_clip($track, region => $_) } @$markpairs; map{ $sequence->append_item($_) } @clips; $sequence } 1 __END__Audio-Nama-1.216/lib/Audio/Nama/StatusSnapshot.pm0000644000175000017500000000227613544212613020530 0ustar jrothjrothpackage Audio::Nama::StatusSnapshot; use Role::Tiny; use Modern::Perl; { package Audio::Nama; # these track fields will be inspected my @relevant_track_fields = qw( name n width group playat region_start region_end looping source_id source_type send_id send_type rec_status current_version ); sub status_snapshot { # # hashref output for detecting if we need to reconfigure engine # compared as YAML strings # %status_snaphot indicates Nama's internal # state. It consists of # - the values of selected global variables # - selected field values of each track my %snapshot = ( project => $project->{name}, mastering_mode => $mode->mastering, preview => $mode->{preview}, doodle => $mode->{doodle}, jack_running => $jack->{jackd_running}, tracks => [], ); map { push @{$snapshot{tracks}}, $_->snapshot(\@relevant_track_fields) } grep{ $_->rec_status ne OFF } grep { $_->group ne 'Temp' } Audio::Nama::all_tracks(); \%snapshot; } sub status_snapshot_string { my $json = json_out(status_snapshot()); # hack to avoid false diff due to string/numerical # representation $json =~ s/: "(\d+)"/: $1/g; $json } } 1;Audio-Nama-1.216/lib/Audio/Nama/TrackUtils.pm0000644000175000017500000001243113544212613017604 0ustar jrothjrothpackage Audio::Nama; use Modern::Perl; sub add_track { logsub("&add_track"); my ($name, @params) = @_; my %vals = (name => $name, @params); my $class = $vals{class} // 'Audio::Nama::Track'; { no warnings 'uninitialized'; logpkg(__FILE__,__LINE__,'debug', "name: $name, ch_r: $gui->{_chr}, ch_m: $gui->{_chm}"); } Audio::Nama::throw("$name: track name already in use. Skipping."), return if $tn{$name}; Audio::Nama::throw("$name: reserved track name. Skipping"), return if grep $name eq $_, @{$mastering->{track_names}}; # in order to increment serially Audio::Nama::ChainSetup::remove_temporary_tracks(); my $track = $class->new(%vals); return if ! $track; logpkg(__FILE__,__LINE__,'debug', "ref new track: ", ref $track); $track->source($gui->{_chr}) if $gui->{_chr}; # $track->send($gui->{_chm}) if $gui->{_chm}; my $bus = $bn{$track->group}; $bus->set(rw => MON) unless $track->target; # not if is alias # normal tracks set to config->new_track_rw # defaulting to MON # track aliases default to PLAY $track->set(rw => $track->{target} ? PLAY : $config->{new_track_rw} || MON ); $gui->{_track_name} = $gui->{_chm} = $gui->{_chr} = undef; set_current_bus(); logpkg(__FILE__,__LINE__,'debug', "Added new track!\n", sub{$track->dump}); $track; } # create read-only track pointing at WAV files of specified # name in current project sub add_track_alias { my ($name, $track) = @_; my $target; if ( $tn{$track} ){ $target = $track } elsif ( $ti{$track} ){ $target = $ti{$track}->name } add_track( $name, target => $target, width => $tn{$target}->width); } # create read-only track pointing at WAV files of specified # track name in a different project sub add_track_alias_project { my ($name, $track, $project_name) = @_; $project_name //= $Audio::Nama::project->{name}; my $dir = join_path(project_root(), $project_name, '.wav'); if ( -d $dir ){ if ( glob "$dir/$track*.wav"){ Audio::Nama::pager("Found target WAV files.\n"); my @params = ( target => $track, project => $project_name, ); add_track( $name, @params ); } else { Audio::Nama::throw("$project_name:$track - No WAV files found. Skipping.\n"), return; } } else { Audio::Nama::throw("$project_name: project does not exist. Skipping.\n"); return; } } # vol/pan requirements of mastering and mixdown tracks # called from Track_subs, Graphical_subs { my %volpan = ( Eq => {}, Low => {}, Mid => {}, High => {}, Boost => {vol => 1}, Mixdown => {}, ); sub need_vol_pan { # this routine used by # # + add_track() to determine whether a new track _will_ need vol/pan controls # + add_track_gui() to determine whether an existing track needs vol/pan my ($track_name, $type) = @_; # $type: vol | pan # Case 1: track already exists return 1 if $tn{$track_name} and $tn{$track_name}->$type; # Case 2: track not yet created if( $volpan{$track_name} ){ return($volpan{$track_name}{$type} ? 1 : 0 ) } return 1; } } # track width in words sub width { my $count = shift; return 'mono' if $count == 1; return 'stereo' if $count == 2; return "$count channels"; } sub add_volume_control { my $n = shift; return unless need_vol_pan($ti{$n}->name, "vol"); my $vol_id = Audio::Nama::Effect->new( chain => $n, type => $config->{volume_control_operator}, id => $ti{$n}->vol, # often undefined )->id; $ti{$n}->set(vol => $vol_id); # save the id for next time $vol_id; } sub add_pan_control { my $n = shift; return unless need_vol_pan($ti{$n}->name, "pan"); my $pan_id = Audio::Nama::Effect->new( chain => $n, type => 'epp', id => $ti{$n}->pan, # often undefined )->id; $ti{$n}->set(pan => $pan_id); # save the id for next time $pan_id; } sub rename_track { use Cwd; use File::Slurp; my ($oldname, $newname, $statefile, $dir) = @_; save_state(); my $old_dir = cwd(); chdir $dir; # rename audio files qx(rename 's/^$oldname(?=[_.])/$newname/' *.wav); # rename in State.json when candidate key # is part of the specified set and the value # exactly matches $oldname my $state = read_file($statefile); $state =~ s/ " # open quote (track| # one of specified fields name| group| source| send_id| target| current_edit| send_id| return_id| wet_track| dry_track| track| host_track) " # close quote \ # space : # colon \ # space "$oldname"/"$1" : "$newname"/gx; write_file($statefile, $state); my $msg = "Rename track $oldname -> $newname"; git_commit($msg); Audio::Nama::pager($msg); load_project(name => $Audio::Nama::project->{name}); } sub user_tracks_present { my $i = 0; $i++ for user_tracks(); $i } sub all_tracks { sort{$a->n <=> $b->n } values %Audio::Nama::Track::by_name } sub audio_tracks { grep { $_->class !~ /Midi/ } all_tracks() } sub rec_hookable_tracks { grep{ $_->group ne 'Temp' and $_->group ne 'Insert' } all_tracks() } sub user_tracks { grep { ! $_->is_system_track } all_tracks() } sub system_tracks { grep { $_->is_system_track } all_tracks() } sub this_op { $this_track and $this_track->op } sub this_op_o { $this_track and $this_track->op and fxn($this_track->op) } sub this_param { $this_track ? $this_track->param : ""} sub this_stepsize { $this_track ? $this_track->stepsize : ""} sub this_track_name { $this_track ? $this_track->name : "" }Audio-Nama-1.216/lib/Audio/Nama/TrackLatency.pm0000644000175000017500000000130413544212613020100 0ustar jrothjrothpackage Audio::Nama::TrackLatency; use Role::Tiny; use Modern::Perl; use Audio::Nama::Globals qw($setup); sub latency_offset { my $track = shift; no warnings 'uninitialized'; $setup->{latency}->{sibling}->{$track->name} - $setup->{latency}->{track}->{$track->name}->{total}; } sub capture_latency { my $track = shift; my $io = $track->input_object; return $io->capture_latency if ref $io; } sub playback_latency { my $track = shift; my $io = $track->input_object; return $io->playback_latency if ref $io; } sub sibling_latency { my $track = shift; $setup->{latency}->{sibling}->{$track->name} } sub sibling_count { my $track = shift; $setup->{latency}->{sibling_count}->{$track->name} } 1;Audio-Nama-1.216/lib/Audio/Nama/Assign.pm0000644000175000017500000003176013544212613016751 0ustar jrothjrothpackage Audio::Nama::Assign; use Modern::Perl; our $VERSION = 1.0; use 5.008; use feature 'state'; use strict; use warnings; no warnings q(uninitialized); use Carp qw(carp confess croak cluck); use YAML::Tiny; use File::Slurp; use File::HomeDir; use Audio::Nama::Log qw(logsub logpkg); use Storable qw(nstore retrieve); use JSON::XS; use Data::Dumper::Concise; require Exporter; our @ISA = qw(Exporter); our %EXPORT_TAGS = ( 'all' => [ qw( serialize assign assign_singletons yaml_in json_in json_out quote_yaml_scalars var_map config_vars ) ] ); our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); our @EXPORT = (); our $to_json = JSON::XS->new->utf8->allow_blessed->pretty->canonical(1) ; use Carp; {my $var_map = { qw( %devices $config->{devices} $alsa_playback_device $config->{alsa_playback_device} $alsa_capture_device $config->{alsa_capture_device} $soundcard_channels $config->{soundcard_channels} %abbreviations $config->{abbreviations} $mix_to_disk_format $config->{mix_to_disk_format} $raw_to_disk_format $config->{raw_to_disk_format} $cache_to_disk_format $config->{cache_to_disk_format} $mixer_out_format $config->{mixer_out_format} $sample_rate $config->{sample_rate} $ecasound_tcp_port $config->{engine_tcp_port} $ecasound_globals $config->{ecasound_globals} $ecasound_buffersize $config->{engine_buffersize} $realtime_profile $config->{realtime_profile} $jack_seek_delay $config->{engine_base_jack_seek_delay} $jack_transport_mode $config->{jack_transport_mode} $ecasound_jack_client_name $config->{ecasound_jack_client_name} $ecasound_engine_name $config->{ecasound_engine_name} $midi_engine_name $config->{midi_engine_name} $use_midi $config->{use_midi} $midi_record_buffer $config->{midi_record_buffer} $midi_default_input_channel $config->{midi_default_input_channel} $eq $mastering->{fx_eq} $low_pass $mastering->{fx_low_pass} $mid_pass $mastering->{fx_mid_pass} $high_pass $mastering->{fx_high_pass} $compressor $mastering->{fx_compressor} $spatialiser $mastering->{fx_spatialiser} $limiter $mastering->{fx_limiter} $project_root $config->{root_dir} $use_group_numbering $config->{use_group_numbering} $press_space_to_start_transport $config->{press_space_to_start} $execute_on_project_load $config->{execute_on_project_load} $initial_mode $config->{initial_mode} $quietly_remove_tracks $config->{quietly_remove_tracks} $use_jack_plumbing $config->{use_jack_plumbing} $use_playback_version_for_mixdown $config->{sync_mixdown_and_playback_version_numbers} $mixdown_encodings $config->{mixdown_encodings} $volume_control_operator $config->{volume_control_operator} $serialize_formats $config->{serialize_formats} $use_git $config->{use_git} $autosave $config->{autosave} $beep_command $config->{beep_command} $hotkey_beep $config->{hotkey_beep} $eager $mode->{eager} $waveform_height $config->{waveform_height} $alias $config->{alias} $hotkeys $config->{hotkeys} $new_track_rw $config->{new_track_rw} $hotkeys_always $config->{hotkeys_always} $use_pager $config->{use_pager} $use_placeholders $config->{use_placeholders} $edit_playback_end_margin $config->{edit_playback_end_margin} $edit_crossfade_time $config->{edit_crossfade_time} $default_fade_length $config->{engine_fade_default_length} $fade_time $config->{engine_fade_length_on_start_stop} %mute_level $config->{mute_level} %fade_out_level $config->{fade_out_level} $fade_resolution $config->{fade_resolution} %unity_level $config->{unity_level} $enforce_channel_bounds $config->{enforce_channel_bounds} $midi_input_dev $midi->{input_dev} $midi_output_dev $midi->{output_dev} $controller_ports $midi->{controller_ports} $midi_inputs $midi->{inputs} $osc_listener_port $config->{osc_listener_port} $osc_reply_port $config->{osc_reply_port} $remote_control_port $config->{remote_control_port} $engines $config->{engines} $default_waveform_height $config->{default_waveform_height} $loop_chain_channel_width $config->{loop_chain_channel_width} $waveform_pixels_per_second $config->{waveform_pixels_per_second} $display_waveform $config->{display_waveform} ) }; sub var_map { $var_map } # to allow outside access while keeping # working lexical sub config_vars { keys %$var_map } sub assign { # Usage: # assign ( # data => $ref, # vars => \@vars, # var_map => 1, # class => $class # ); logsub("&assign"); my %h = @_; # parameters appear in %h my $class; logpkg(__FILE__,__LINE__,'logcarp',"didn't expect scalar here") if ref $h{data} eq 'SCALAR'; logpkg(__FILE__,__LINE__,'logcarp',"didn't expect code here") if ref $h{data} eq 'CODE'; # print "data: $h{data}, ", ref $h{data}, $/; if ( ref $h{data} !~ /^(HASH|ARRAY|CODE|GLOB|HANDLE|FORMAT)$/){ # we guess object $class = ref $h{data}; logpkg(__FILE__,__LINE__,'debug',"I found an object of class $class"); } $class = $h{class}; $class .= "::" unless $class =~ /::$/; # SKIP_PREPROC my @vars = @{ $h{vars} }; my $ref = $h{data}; my $type = ref $ref; logpkg(__FILE__,__LINE__,'debug',<\w:\[\]{}]+)$/; $var = $var_map->{$var} if $h{var_map} and $var_map->{$var}; logpkg(__FILE__,__LINE__,'debug',"oldvar: $oldvar, newvar: $var") unless $oldvar eq $var; my ($sigil, $identifier) = $var =~ /([\$\%\@])(\S+)/; $sigil{$old_identifier} = $sigil; $ident{$old_identifier} = $identifier; } @vars; logpkg(__FILE__,__LINE__,'debug',sub{"SIGIL\n". json_out(\%sigil)}); #%ident = map{ @$_ } grep{ $_->[0] ne $_->[1] } map{ [$_, $ident{$_}] } keys %ident; my %ident2 = %ident; while ( my ($k,$v) = each %ident2) { delete $ident2{$k} if $k eq $v } logpkg(__FILE__,__LINE__,'debug',sub{"IDENT\n". json_out(\%ident2)}); #print join " ", "Variables:\n", @vars, $/ ; croak "expected hash" if ref $ref !~ /HASH/; my @keys = keys %{ $ref }; # identifiers, *no* sigils logpkg(__FILE__,__LINE__,'debug',sub{ join " ","found keys: ", keys %{ $ref },"\n---\n"}); map{ my $eval; my $key = $_; chomp $key; my $sigil = $sigil{$key}; my $full_class_path = $sigil . ($key =~/:\:/ ? '': $class) . $ident{$key}; # use the supplied class unless the variable name # contains \:\: logpkg(__FILE__,__LINE__,'debug',<{$key}; if (! ref $val or ref $val eq 'SCALAR') # scalar assignment { # extract value if ($val) { # if we have something, # dereference it if needed ref $val eq q(SCALAR) and $val = $$val; # quoting for non-numerical $val = qq("$val") unless $val =~ /^[\d\.,+\-e]+$/ } else { $val = q(undef) }; # or set as undefined $eval .= $val; # append to assignment } elsif ( ref $val eq 'ARRAY' or ref $val eq 'HASH') { if ($sigil eq '$') # assign reference { $eval .= q($val) ; } else # dereference and assign { $eval .= qq($sigil) ; $eval .= q({$val}) ; } } else { die "unsupported assignment: ".ref $val } logpkg(__FILE__,__LINE__,'debug',"eval string: $eval"); eval($eval); logpkg(__FILE__,__LINE__,'logcarp',"failed to eval $eval: $@") if $@; } # end if sigil{key} } @keys; 1; } } # assign_singletons() assigns hash key/value entries # rather than a top-level hash reference to avoid # clobbering singleton key/value pairs initialized # elsewhere. my @singleton_idents = map{ /^.(.+)/; $1 } # remove leading '$' sigil qw( $ui $mode $file $graph $setup $config $jack $fx $fx_cache $text $gui $midi $help $mastering $project ); sub assign_singletons { logsub('&assign_singletons'); my $ref = shift; my $data = $ref->{data} or die "expected data got undefined"; my $class = $ref->{class} // 'Audio::Nama'; $class .= '::'; # SKIP_PREPROC map { my $ident = $_; if( defined $data->{$ident}){ my $type = ref $data->{$ident}; $type eq 'HASH' or die "$ident: expect hash, got $type"; map{ my $key = $_; my $cmd = join '', '$', $class, $ident, '->{', $key, '}', ' = $data->{$ident}->{$key}'; logpkg(__FILE__,__LINE__,'debug',"eval: $cmd"); eval $cmd; logpkg(__FILE__,__LINE__,'logcarp',"error during eval: $@") if $@; } keys %{ $data->{$ident} } } } @singleton_idents; # list of "singleton" variables } our %suffix = ( storable => "bin", perl => "pl", json => "json", yaml => "yml", ); our %dispatch = ( storable => sub { my($ref, $path) = @_; nstore($ref, $path) }, perl => sub { my($ref, $path) = @_; write_file($path, Dumper $ref) }, yaml => sub { my($ref, $path) = @_; write_file($path, json_out($ref))}, json => sub { my($ref, $path) = @_; write_file($path, json_out($ref))}, ); sub serialize_and_write { my ($ref, $path, $format) = @_; $path .= ".$suffix{$format}" unless $path =~ /\.$suffix{$format}$/; $dispatch{$format}->($ref, $path) } { my $parse_re = # initialize only once qr/ ^ # beginning anchor ([\%\@\$]) # first character, sigil ([\w:]+) # identifier, possibly perl namespace (?:->\{(\w+)})? # optional hash key for new hash-singleton vars $ # end anchor /x; sub serialize { logsub("&serialize"); my %h = @_; my @vars = @{ $h{vars} }; my $class = $h{class}; my $file = $h{file}; my $format = $h{format} // 'perl'; # default to Data::Dumper::Concise $class //= "Audio::Nama"; $class =~ /::$/ or $class .= '::'; # SKIP_PREPROC logpkg(__FILE__,__LINE__,'debug',"file: $file, class: $class\nvariables...@vars"); # first we marshall data into %state my %state; map{ my ($sigil, $identifier, $key) = /$parse_re/; logpkg(__FILE__,__LINE__,'debug',"found sigil: $sigil, ident: $identifier, key: $key"); # note: for YAML::Reader/Writer all scalars must contain values, not references # more YAML adjustments # restore will break if a null field is not converted to '~' #my $value = q(\\) # directly assign scalar, but take hash/array references # $state{ident} = $scalar # $state{ident} = \%hash # $state{ident} = \@array # in case $key is provided # $state{ident}->{$key} = $singleton->{$key}; # my $value = ($sigil ne q($) ? q(\\) : q() ) . $sigil . ($identifier =~ /:/ ? '' : $class) . $identifier . ($key ? qq(->{$key}) : q()); logpkg(__FILE__,__LINE__,'debug',"value: $value"); my $eval_string = q($state{') . $identifier . q('}) . ($key ? qq(->{$key}) : q() ) . q( = ) . $value; if ($identifier){ logpkg(__FILE__,__LINE__,'debug',"attempting to eval $eval_string"); eval($eval_string); logpkg(__FILE__,__LINE__,'error', "eval failed ($@)") if $@; } } @vars; logpkg(__FILE__,__LINE__,'debug',sub{join $/,'\%state', Dumper \%state}); # YAML out for screen dumps return( json_out(\%state) ) unless $h{file}; # now we serialize %state my $path = $h{file}; serialize_and_write(\%state, $path, $format); } } sub json_out { logsub("&json_out"); my $data_ref = shift; my $type = ref $data_ref; croak "attempting to code wrong data type: $type" if $type !~ /HASH|ARRAY/; $to_json->encode($data_ref); } sub json_in { logsub("&json_in"); my $json = shift; my $data_ref = decode_json($json); $data_ref } sub yaml_in { # logsub("&yaml_in"); my $input = shift; my $yaml = $input =~ /\n/ # check whether file or text ? $input # yaml text : do { logpkg(__FILE__,__LINE__,'debug',"filename: $input"); read_file($input); # file name }; if ($yaml =~ /\t/){ croak "YAML file: $input contains illegal TAB character."; } $yaml =~ s/^\n+// ; # remove leading newline at start of file $yaml =~ s/\n*$/\n/; # make sure file ends with newline my $y = YAML::Tiny->read_string($yaml); Audio::Nama::throw("YAML::Tiny read error: $YAML::Tiny::errstr\n") if $YAML::Tiny::errstr; $y->[0]; } sub quote_yaml_scalars { my $yaml = shift; my @modified; map { chomp; if( /^(?(\s*\w+: )|(\s+- ))(?.+)$/ ){ my($beg,$end) = ($+{beg}, $+{end}); # quote if contains colon and not quoted if ($end =~ /:\s/ and $end !~ /^('|")/ ){ $end =~ s(')(\\')g; # escape existing single quotes $end = qq('$end') } # single-quote string push @modified, "$beg$end\n"; } else { push @modified, "$_\n" } } split "\n", $yaml; join "", @modified; } 1;Audio-Nama-1.216/lib/Audio/Nama/Initializations.pm0000644000175000017500000004054213544212613020675 0ustar jrothjroth# ----------- Initializations -------- # # # These routines are executed once on program startup # # package Audio::Nama; use Modern::Perl; use Carp; use Socket qw(getnameinfo NI_NUMERICHOST) ; sub is_test_script { $config->{opts}->{J} } # if we are using fake JACK client data, # probably a test script is running sub apply_test_args { push @ARGV, qw(-f /dev/null), # force to use internal namarc qw(-t), # set text mode qw(-d), $Audio::Nama::test_dir, q(-E), # suppress loading Ecasound q(-J), # fake jack client data q(-T), # don't initialize terminal # load fake effects cache # q(-c), 'test-project', #qw(-L SUB); # logging $jack->{periodsize} = 1024; } sub apply_ecasound_test_args { apply_test_args(); @ARGV = grep { $_ ne q(-E) } @ARGV } sub definitions { $| = 1; # flush STDOUT buffer on every write $ui eq 'bullwinkle' or die "no \$ui, bullwinkle"; @global_effect_chain_vars = qw( @global_effect_chain_data $Audio::Nama::EffectChain::n $fx->{alias} ); @tracked_vars = qw( @tracks_data @bus_data @groups_data @marks_data @fade_data @edit_data @inserts_data @effects_data $project->{nama_version} $project->{sample_rate} $fx->{applied} $fx->{params} $fx->{params_log} ); @persistent_vars = qw( $project->{nama_version} $project->{timebase} $project->{command_buffer} $project->{track_version_comments} $project->{track_comments} $project->{bunch} $project->{current_op} $project->{current_param} $project->{current_stepsize} $project->{playback_position} $project->{sample_rate} $project->{waveform} @project_effect_chain_data $fx->{id_counter} $setup->{loop_endpoints} $mode->{loop_enable} $mode->{mastering} $mode->{preview} $mode->{midi_transport_sync} $gui->{_seek_unit} $text->{command_history} $this_track_name $this_op ); $text->{wrap} = new Text::Format { columns => 75, firstIndent => 0, bodyIndent => 0, tabstop => 4, }; ####### Initialize singletons ####### # Some of these "singletons" (imported by 'use Globals') # are just hashes, some have object behavior as # the sole instance of their class. $project = bless {}, 'Audio::Nama::Project'; our $mode = bless {}, 'Audio::Nama::Mode'; { package Audio::Nama::Mode; sub mastering { $Audio::Nama::tn{Eq} and ! $Audio::Nama::tn{Eq}->{hide} } sub eager { $mode->{eager} } sub doodle { $mode->{doodle} } sub preview { $mode->{preview} } sub song { $mode->eager and $mode->preview } sub live { $mode->eager and $mode->doodle } } # for example, $file belongs to class Audio::Nama::File, and uses # AUTOLOAD to generate methods to provide full path # to various system files, such as $file->state_store { package Audio::Nama::File; use Carp; sub logfile { my $self = shift; $ENV{NAMA_LOGFILE} || $self->_logfile } sub AUTOLOAD { my ($self, $filename) = @_; # get tail of method call my ($method) = $Audio::Nama::File::AUTOLOAD =~ /([^:]+)$/; croak "$method: illegal method call" unless $self->{$method}; my $dir_sub = $self->{$method}->[1]; $filename ||= $self->{$method}->[0]; my $path = Audio::Nama::join_path($dir_sub->(), $filename); $path; } sub DESTROY {} 1; } $file = bless { effects_cache => ['.effects_cache.json', \&project_root], gui_palette => ['palette', \&project_root], state_store => ['State', \&project_dir ], git_state_store => ['State.json', \&project_dir ], untracked_state_store => ['Aux', \&project_dir ], effect_profile => ['effect_profiles', \&project_root], chain_setup => ['Setup.ecs', \&project_dir ], user_customization => ['customize.pl', \&project_root], project_effect_chains => ['project_effect_chains',\&project_dir ], global_effect_chains => ['global_effect_chains', \&project_root], old_effect_chains => ['effect_chains', \&project_root], _logfile => ['nama.log', \&project_root], midi_store => ['midi.msh', \&project_dir ], aux_midi_commands => ['aux_midi_commands', \&project_root], }, 'Audio::Nama::File'; $gui->{_save_id} = "State"; $gui->{_seek_unit} = 1; $gui->{marks} = {}; # # use this section to specify # defaults for config variables # # These are initial, lowest priority defaults # defaults for Nama config. Some variables # may be overwritten during subsequent read_config's # # config variable sources are prioritized as follows # # + command line argument -f /path/to/namarc # + project specific namarc # currently disabled # + user namarc (usually ~/.namarc) # + internal namarc # + internal initialization $config = bless { root_dir => join_path( $ENV{HOME}, "nama"), soundcard_channels => 10, memoize => 1, use_pager => 1, use_placeholders => 1, use_git => 1, autosave => 'undo', volume_control_operator => 'ea', # default to linear scale sync_mixdown_and_playback_version_numbers => 1, # not implemented yet engine_tcp_port => 2868, # 'default' engine engine_fade_length_on_start_stop => 0.18,# when starting/stopping transport engine_fade_default_length => 0.5, # for fade-in, fade-out engine_base_jack_seek_delay => 0.1, # seconds jack_transport_mode => 'send', ecasound_engine_name => 'ecasound', ecasound_jack_client_name => 'nama', midi_engine_name => 'midish', engine_command_output_buffer_size => 2**26, # 64 MB edit_playback_end_margin => 3, edit_crossfade_time => 0.03, engine_muting_time => 0.03, fade_down_fraction => 0.75, fade_time1_fraction => 0.9, fade_time2_fraction => 0.1, fade_resolution => 100, # steps per second fader_op => 'ea', mute_level => {ea => 0, eadb => -96}, fade_out_level => {ea => 0, eadb => -40}, unity_level => {ea => 100, eadb => 0}, enforce_channel_bounds => 1, serialize_formats => 'json', # for save_system_state() latency_op => 'el:delay_n', latency_op_init => [0,0], latency_op_set => sub { my $id = shift; my $delay = shift(); modify_effect($id,2,undef,$delay) }, hotkey_beep => 'beep -f 250 -l 200', # this causes beeping during make test # beep_command => 'beep -f 350 -l 700', midi_record_buffer => 'midi_record', midi_default_input_channel => 'keyboard', ecasound_channel_ops => {map{$_,1} qw(chcopy chmove chorder chmix chmute)}, waveform_height => 200, waveform_canvas_x => 2400, waveform_canvas_y => 4800, waveform_pixels_per_second => 10, loop_chain_channel_width => 16, }, 'Audio::Nama::Config'; { package Audio::Nama::Config; use Carp; use Audio::Nama::Globals qw(:singletons); use Modern::Perl; our @ISA = 'Audio::Nama::Object'; # for ->dump and ->as_hash methods sub serialize_formats { split " ", $_[0]->{serialize_formats} } sub hardware_latency { no warnings 'uninitialized'; $config->{devices}->{$config->{alsa_capture_device}}{hardware_latency} || 0 } sub buffersize { package Audio::Nama; Audio::Nama::ChainSetup::setup_requires_realtime() ? $config->{engine_buffersize}->{realtime}->{default} : $config->{engine_buffersize}->{nonrealtime}->{default} } sub globals_realtime { Audio::Nama::ChainSetup::setup_requires_realtime() ? $config->{ecasound_globals}->{realtime} : $config->{ecasound_globals}->{nonrealtime} } } # end Audio::Nama::Config package $prompt = "nama ('h' for help)> "; $this_bus = 'Main'; $setup->{_old_snapshot} = {}; $setup->{_last_rec_tracks} = []; $mastering->{track_names} = [ qw(Eq Low Mid High Boost) ]; init_wav_memoize() if $config->{memoize}; } sub initialize_interfaces { logsub("&intialize_interfaces"); if ( ! $config->{opts}->{t} and Audio::Nama::Graphical::initialize_tk() ){ $ui = Audio::Nama::Graphical->new(); } else { pager_newline( "Unable to load perl Tk module. Starting in console mode.") if $config->{opts}->{g}; $ui = Audio::Nama::Text->new(); can_load( modules =>{ Event => undef}) or die "Perl Module 'Event' not found. Please install it and try again. Stopping."; ; import Event qw(loop unloop unloop_all); } can_load( modules => {AnyEvent => undef}) or die "Perl Module 'AnyEvent' not found. Please install it and try again. Stopping."; use AnyEvent::TermKey qw( FORMAT_VIM KEYMOD_CTRL ); can_load( modules => {jacks => undef}) and $jack->{use_jacks}++; choose_sleep_routine(); $config->{want_logging} = initialize_logger($config->{opts}->{L}); $project->{name} = shift @ARGV; {no warnings 'uninitialized'; logpkg(__FILE__,__LINE__,'debug',"project name: $project->{name}"); } logpkg(__FILE__,__LINE__,'debug', sub{"Command line options\n". json_out($config->{opts})}); read_config(global_config()); # from .namarc if we have one # set sample rate is needed for prepare_static_effects_data() and initialize_project_data() $config->{sample_rate} = $config->{opts}->{z} if $config->{opts}->{z}; logpkg(__FILE__,__LINE__,'debug',sub{"Config data\n".Dumper $config}); Audio::Nama::MidiEngine->new(name => $config->{midi_engine_name}) if $config->{use_midi}; initialize_ecasound_engine(); start_osc_listener($config->{osc_listener_port}) if $config->{osc_listener_port} and can_load(modules => {'Protocol::OSC' => undef}); start_remote_listener($config->{remote_control_port}) if $config->{remote_control_port}; logpkg(__FILE__,__LINE__,'debug',"reading config file"); if ($config->{opts}->{d}){ pager("project_root $config->{opts}->{d} specified on command line\n"); $config->{root_dir} = $config->{opts}->{d}; } if ($config->{opts}->{p}){ $config->{root_dir} = getcwd(); pager("placing all files in current working directory ($config->{root_dir})\n"); } # skip initializations if user (test) supplies project # directory first_run() unless $config->{opts}->{d}; prepare_static_effects_data() unless $config->{opts}->{S}; setup_user_customization(); # depends on effect_index() in above get_ecasound_iam_keywords(); load_keywords(); # for autocompletion parse_midi_help(); chdir $config->{root_dir} # for filename autocompletion or warn "$config->{root_dir}: chdir failed: $!\n"; $ui->init_gui; $ui->transport_gui; $ui->time_gui; # fake JACK for testing environment if( $config->{opts}->{J}){ parse_ports_list(get_data_section("fake_jack_lsp")); parse_port_latency(get_data_section("fake_jack_latency")); $jack->{jackd_running} = 1; } # periodically check if JACK is running, and get client/port/latency list sleeper(0.2); # allow time for first polling # we will start jack-plumbing only when we need it if( $config->{use_jack_plumbing} and $jack->{jackd_running} and process_is_running('jack-plumbing') ){ pager_newline(<{opts}->{T}; 1; } { my $is_connected_remote; sub start_remote_listener { my $port = shift; pager_newline("Starting remote control listener on port $port"); $project->{remote_control_socket} = IO::Socket::INET->new( LocalAddr => 'localhost', LocalPort => $port, Proto => 'tcp', Type => SOCK_STREAM, Listen => 1, Reuse => 1) || die $!; start_remote_watcher(); } sub start_remote_watcher { $project->{events}->{remote_control} = AE::io( $project->{remote_control_socket}, 0, \&process_remote_command ) } sub remove_remote_watcher { undef $project->{events}->{remote_control}; } sub process_remote_command { if ( ! $is_connected_remote++ ){ pager_newline("making connection"); $project->{remote_control_socket} = $project->{remote_control_socket}->accept(); remove_remote_watcher(); $project->{events}->{remote_control} = AE::io( $project->{remote_control_socket}, 0, \&process_remote_command ); } my $input; eval { $project->{remote_control_socket}->recv($input, $project->{remote_control_socket}->sockopt(SO_RCVBUF)); }; $@ and throw("caught error: $@, resetting..."), reset_remote_control_socket(), revise_prompt(), return; logpkg(__FILE__,__LINE__,'debug',"Got remote control socketput: $input"); nama_cmd($input); my $out; { no warnings 'uninitialized'; $out = $text->{eval_result} . "\n"; } eval { $project->{remote_control_socket}->send($out); }; $@ and throw("caught error: $@, resetting..."), reset_remote_control_socket(), revise_prompt(), return; revise_prompt(); } sub reset_remote_control_socket { undef $is_connected_remote; undef $@; $project->{remote_control_socket}->shutdown(2); undef $project->{remote_control_socket}; remove_remote_watcher(); start_remote_listener($config->{remote_control_port}); } } sub start_osc_listener { my $port = shift; say("Starting OSC listener on port $port"); my $osc_in = $project->{osc_socket} = IO::Socket::INET->new( LocalAddr => 'localhost', LocalPort => $port, Proto => 'udp', Type => SOCK_DGRAM) || die $!; $project->{events}->{osc} = AE::io( $osc_in, 0, \&process_osc_command ); $project->{osc} = Protocol::OSC->new; } sub process_osc_command { my $in = $project->{osc_socket}; my $osc = $project->{osc}; my $source_ip = $in->recv(my $packet, $in->sockopt(SO_RCVBUF)); my($err, $hostname, $servicename) = getnameinfo($source_ip, NI_NUMERICHOST); my $p = $osc->parse($packet); my @args = @$p; my ($path, $template, $command, @vals) = @args; $path =~ s(^/)(); $path =~ s(/$)(); my ($trackname, $fx, $param) = split '/', $path; nama_cmd($trackname); nama_cmd("$command @vals") if $command; nama_cmd("show_effect $fx") if $fx; # select nama_cmd("show_track") if $trackname and not $fx; nama_cmd("show_tracks") if ! $trackname; say "got OSC: ", Dumper $p; say "got args: @args"; my $osc_out = IO::Socket::INET->new( PeerAddr => $hostname, PeerPort => $config->{osc_reply_port}, Proto => 'udp', Type => SOCK_DGRAM) || die $!; $osc_out->send(join "",@{$text->{output_buffer}}); delete $text->{output_buffer}; } sub sanitize_remote_input { my $input = shift; my $error_msg; do{ $input = "" ; $error_msg = "error: perl/shell code is not allowed"} if $input =~ /(^|;)\s*(!|eval\b)/; throw($error_msg) if $error_msg; $input } sub initialize_ecasound_engine { my %args; my $class; if ($config->{opts}->{A} or $config->{opts}->{E}) { pager_newline("Starting dummy engine only"); %args = ( name => $config->{ecasound_engine_name} ); $class = 'Audio::Nama::Engine'; } elsif ( $config->{opts}->{l} and can_load( modules => { 'Audio::Ecasound' => undef }) and say("loaded Audio::Ecasound") ){ %args = ( name => $config->{ecasound_engine_name}, ); $class = 'Audio::Nama::LibEngine'; } else { %args = ( name => $config->{ecasound_engine_name}, port => $config->{engine_tcp_port}, ); $class = 'Audio::Nama::NetEngine'; } $class->new(%args); } sub choose_sleep_routine { if ( can_load(modules => {'Time::HiRes'=> undef} ) ) { *sleeper = *finesleep; $config->{hires_timer}++; } else { *sleeper = *select_sleep } } sub finesleep { my $sec = shift; Time::HiRes::usleep($sec * 1e6); } sub select_sleep { my $seconds = shift; select( undef, undef, undef, $seconds ); } sub munge_category { my $cat = shift; # override undefined category by magical global setting # default to 'ECI_OTHER' $cat ||= ($config->{category} || 'ECI_OTHER'); # force all categories to 'ECI' if 'ECI' is selected for logging # (exception: ECI_WAVINFO, which is too noisy) no warnings 'uninitialized'; return 'ECI' if $config->{want_logging}->{ECI} and not $cat eq 'ECI_WAVINFO'; $cat } sub start_logging { $config->{want_logging} = initialize_logger($config->{opts}->{L}) } sub ecasound_iam{ $en{$Audio::Nama::config->{ecasound_engine_name}} and $en{$Audio::Nama::config->{ecasound_engine_name}}->ecasound_iam(@_) } 1; __END__Audio-Nama-1.216/lib/Audio/Nama/Util.pm0000644000175000017500000001175013544212613016437 0ustar jrothjroth# ----------- Util.pm ----------- # this package is for small subroutines with # well-defined interfaces package Audio::Nama::Util; use Modern::Perl; use Carp; use Data::Dumper::Concise; use Audio::Nama::Assign qw(json_out); use Audio::Nama::Globals qw(:all); use Audio::Nama::Log qw(logit logsub logpkg); no warnings 'uninitialized'; require Exporter; our @ISA = qw(Exporter); our %EXPORT_TAGS = ( 'all' => [ qw( freq channels input_node output_node signal_format process_is_running d1 d2 dn round colonize time_tag heuristic_time dest_type dest_string create_dir join_path wav_off strip_all strip_blank_lines strip_comments remove_spaces expand_tilde resolve_path dumper route_output_channels ) ] ); our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); our @EXPORT = (); sub freq { [split ',', $_[0] ]->[2] } # e.g. s16_le,2,44100 sub channels { [split ',', $_[0] ]->[1] } # these are the names of loop devices corresponding # to pre- and post-fader nodes of a track signal sub input_node { $_[0].'_in' } sub output_node {$_[0].'_out'} sub signal_format { my ($template, $channel_count) = @_; $template =~ s/N/$channel_count/; my $format = $template; } sub process_is_running { my $name = shift; my @pids = split " ", qx(pgrep $name); my @ps_ax = grep{ my $pid; /$name/ and ! /defunct/ and ($pid) = /(\d+)/ and grep{ $pid == $_ } @pids } split "\n", qx(ps ax) ; } sub d1 { my $n = shift; sprintf("%.1f", $n) } sub d2 { my $n = shift; sprintf("%.2f", $n) } sub dn { my ($n, $places) = @_; sprintf("%." . $places . "f", $n); } sub round { my $n = shift; return 0 if $n == 0; $n = int $n if $n > 10; $n = d2($n) if $n < 10; $n; } sub colonize { # convert seconds to hours:minutes:seconds my $sec = shift || 0; my $hours = int ($sec / 3600); $sec = $sec % 3600; my $min = int ($sec / 60); $sec = $sec % 60; $sec = "0$sec" if $sec < 10; $min = "0$min" if $min < 10 and $hours; ($hours ? "$hours:" : "") . qq($min:$sec); } sub time_tag { my @time = localtime time; $time[4]++; $time[5]+=1900; @time = @time[5,4,3,2,1,0]; sprintf "%4d.%02d.%02d-%02d:%02d:%02d", @time } sub heuristic_time { my $sec = shift; d1($sec) . ( $sec > 120 ? " (" . colonize( $sec ) . ") " : " " ) } sub dest_type { my $dest = shift; if($dest eq undef ) { undef } elsif($dest eq 'bus') { 'bus' } elsif($dest eq 'null') { 'null' } elsif($dest eq 'rtnull') { 'rtnull' } elsif($dest =~ /^loop,/) { 'loop' } elsif($dest !~ /\D/) { 'soundcard' } # digits only elsif($dest =~ /^man/) { 'jack_manual' } elsif($dest eq 'jack') { 'jack_manual' } elsif($dest =~ /\.ports$/) { 'jack_ports_list' } elsif( $tn{$dest} ) { 'track' } else { 'jack_client' } } sub dest_string { my ($type, $id, $width) = @_; if ($type eq 'soundcard'){ my $ch = $id; my @channels; push @channels, $_ for $ch .. ($ch + $width - 1); 'CH '.join '/', @channels } else { $id } } sub create_dir { my @dirs = @_; map{ my $dir = $_; logpkg(__FILE__,__LINE__,'debug',"creating directory [ $dir ]"); -e $dir #and (carp "create_dir: '$dir' already exists, skipping...\n") or system qq( mkdir -p $dir) } @dirs; } sub join_path { my @parts = @_; my $path = join '/', @parts; $path =~ s(/{2,})(/)g; $path; } sub wav_off { my $wav = shift; $wav =~ s/\.wav\s*$//i; $wav; } sub strip_all{ strip_trailing_spaces(strip_blank_lines( strip_comments(@_))) } sub strip_trailing_spaces { map {s/\s+$//} @_; @_; } sub strip_blank_lines { map{ s/\n(\s*\n)+/\n/sg } @_; map{ s/^\n+//s } @_; @_; } sub strip_comments { # map{ s/#.*$//mg; } @_; map{ s/\s+$//mg; } @_; @_ } sub remove_spaces { my $entry = shift; # remove leading and trailing spaces $entry =~ s/^\s*//; $entry =~ s/\s*$//; # convert other spaces to underscores $entry =~ s/\s+/_/g; $entry; } sub resolve_path { my $path = shift; $path = expand_tilde($path); $path = File::Spec::Link->resolve_all($path); } sub expand_tilde { my $path = shift; my $home = File::HomeDir->my_home; # ~bob -> /home/bob $path =~ s( ^ # beginning of line ~ # tilde (\w+) # username ) (File::HomeDir->users_home($1))ex; # ~/something -> /home/bob/something $path =~ s( ^ # beginning of line ~ # tilde / # slash ) ($home/)x; $path } sub dumper { ! defined $_ and "undef" or ! (ref $_) and $_ #or (ref $_) =~ /HASH|ARRAY/ and Audio::Nama::json_out($_) or ref $_ and Dumper($_) } sub route_output_channels { # routes signals (1..$width) to ($dest..$dest+$width-1 ) # returns pairs as arguments to chmove my ($width, $dest) = @_; return '' if ! $dest or $dest == 1; # print "route: width: $width, destination: $dest\n\n"; my $offset = $dest - 1; my @route; for my $channel ( map{$width - $_ + 1} 1..$width ) { push @route,[$channel,($channel + $offset)]; } @route; } 1; __END__Audio-Nama-1.216/lib/Audio/Nama/EffectChain.pm0000644000175000017500000002741113544212613017662 0ustar jrothjroth# ------------- Effect-Chain and -Profile routines -------- # Effect Chains # # we have two type of effect chains # + global effect chains - usually user defined, available to all projects # + system generated effect chains, per project { package Audio::Nama::EffectChain; use Modern::Perl; use Data::Dumper::Concise; use Carp; use Exporter qw(import); use Storable qw(dclone); use Audio::Nama::Effect qw(fxn append_effect); use Audio::Nama::Log qw(logpkg logsub); use Audio::Nama::Assign qw(json_out); use Audio::Nama::Globals qw($fx_cache %tn $fx); our $AUTOLOAD; our $VERSION = 0.001; no warnings qw(uninitialized); our @ISA; our ($n, %by_index, @attributes, %is_attribute); use Audio::Nama::Object qw( n ops_list ops_data inserts_data fade_data region attrib class is_mixing source_tag ); @attributes = qw( name project global profile user system track_name track_version_result track_version_original track_target_original insert track_cache track_target bypass id ) ; %is_attribute = map{ $_ => 1 } @attributes; initialize(); ## sugar for accessing individual effect attributes ## similar sugar is used for effects. sub is_controller { my ($self, $id) = @_; $self->{ops_data}->{$id}->{belongs_to} } sub parent_id : lvalue { my ($self, $id) = @_; $self->{ops_data}->{$id}->{belongs_to} } sub type { my ($self, $id) = @_; $self->{ops_data}->{$id}->{type} } sub params { my ($self, $id) = @_; $self->{ops_data}->{$id}->{params} } sub initialize { $n = 0; %by_index = (); } sub new_index { $n++; $by_index{$n} ? new_index() : $n } sub new { # arguments: ops_list, ops_data, inserts_data # ops_list => [id1, id2, id3,...]; my $class = shift; my %vals = @_; # we need to so some preparation if we are creating # an effect chain for the first time (as opposed # to restoring a serialized effect chain) if (! $vals{n} ) { # move secondary attributes to $self->{attrib}->{...} move_attributes(\%vals); $vals{n} = new_index(); $vals{inserts_data} ||= []; $vals{ops_list} ||= []; $vals{ops_data} ||= {}; $vals{fade_data} ||= []; croak "undeclared field in: @_" if grep{ ! $_is_field{$_} } keys %vals; croak "must have exactly one of 'global' or 'project' fields defined" unless ($vals{attrib}{global} xor $vals{attrib}{project}); logpkg(__FILE__,__LINE__,'debug','constructor arguments ', sub{ json_out(\%vals) }); # we expect some effects logpkg(__FILE__,__LINE__,'warn',"Nether ops_list or nor insert_data is present") if ! scalar @{$vals{ops_list}} and ! scalar @{$vals{inserts_data}}; my $ops_data = {}; # ops data is taken preferentially # from ops_data argument, with fallback # to existing effects # in both cases, we clone the data structures # to ensure we don't damage the original map { if ( $vals{ops_data}->{$_} ) { $ops_data->{$_} = dclone($vals{ops_data}->{$_}); } else { my $filtered_op_data = dclone( fxn($_)->as_hash );# copy my @unwanted_keys = qw( chain bypassed name surname display); delete $filtered_op_data->{$_} for @unwanted_keys; $ops_data->{$_} = $filtered_op_data; } } @{$vals{ops_list}}; $vals{ops_data} = $ops_data; if( scalar @{$vals{inserts_data}}) { # rewrite inserts to store what we need: # 1. for general-purpose effects chain use # 2. for track caching use $vals{inserts_data} = [ map { logpkg(__FILE__,__LINE__,'debug',"insert: ", sub{Dumper $_}); my @wet_ops = @{$tn{$_->wet_name}->ops}; my @dry_ops = @{$tn{$_->dry_name}->ops}; my $wet_effect_chain = Audio::Nama::EffectChain->new( project => 1, insert => 1, ops_list => \@wet_ops, ); my $dry_effect_chain = Audio::Nama::EffectChain->new( project => 1, insert => 1, ops_list => \@dry_ops, ); my $hash = dclone($_->as_hash); $hash->{wet_effect_chain} = $wet_effect_chain->n; $hash->{dry_effect_chain} = $dry_effect_chain->n; map{ delete $hash->{$_} } qw(n dry_vol wet_vol track); # Reasons for deleting insert attributes # n: we'll get a new index when we re-apply # dry_vol, wet_vol: will never be re-allocated # so why not reuse them? # except for general purpose we'd like to # re-allocate # track: we already know the track from # the parent effect chain # What is left: # # class # wetness # send_type # send_id # return_type # return_id # wet_effect_chain => ec_index, # dry_effect_chain => ec_index, $hash } @{$vals{inserts_data}} ]; } #say Audio::Nama::json_out($vals{inserts_data}) if $vals{inserts_data}; } my $object = bless { %vals }, $class; $by_index{$vals{n}} = $object; logpkg(__FILE__,__LINE__,'debug',sub{$object->dump}); $object; } sub AUTOLOAD { my $self = shift; my ($call) = $AUTOLOAD =~ /([^:]+)$/; return $self->{attrib}->{$call} if exists $self->{attrib}->{$call} or $is_attribute{$call}; croak "Autoload fell through. Object type: ", (ref $self), ", illegal method call: $call\n"; } ### apply effect chain to the specified track sub add_ops { my($self, $track, $ec_args) = @_; # Higher priority: track argument # Lower priority: effect chain's own track name attribute $track ||= $tn{$self->track_name} if $tn{$self->track_name}; # make sure surname is unique my ($new_surname, $existing) = $track->unique_surname($ec_args->{surname}); if ( $new_surname ne $ec_args->{surname}) { Audio::Nama::pager_newline( "track ". $track->name.qq(: other effects with surname "$ec_args->{surname}" found,), qq(using "$new_surname". Others are: $existing.)); $ec_args->{surname} = $new_surname; } logpkg(__FILE__,__LINE__,'debug',$track->name, qq(: adding effect chain ), $self->name, Dumper $self ); # Exclude restoring vol/pan for track_caching. # (This conditional is a hack that would be better # implemented by subclassing EffectChain # for cache/uncache) my @ops_list; my @added; if( $self->track_cache ){ @ops_list = grep{ $_ ne $track->vol and $_ ne $track->pan } @{$self->ops_list} } else { @ops_list = @{$self->ops_list}; } map { my $args = { chain => $track->n, type => $self->type($_), params => $self->params($_), parent => $self->parent_id($_), }; # drop the ID if it is already used $args->{id} = $_ unless fxn($_); logpkg(__FILE__,__LINE__,'debug',"args ", json_out($args)); $args->{surname} = $ec_args->{surname} if $ec_args->{surname}; my $FX = append_effect($args)->[0]; push @added, $FX; my $new_id = $FX->id; # the effect ID may be new, or it may be previously # assigned ID, # whatever value is supplied is guaranteed # to be unique; not to collide with any other effect logpkg(__FILE__,__LINE__,'debug',"new id: $new_id"); my $orig_id = $_; if ( $new_id ne $orig_id) # re-write all controllers to belong to new id { map{ $self->parent_id($_) =~ s/^$orig_id$/$new_id/ } @{$self->ops_list} } } @ops_list; \@added } sub add_inserts { my ($self, $track) = @_; map { my $insert_data = dclone($_); # copy so safe to modify #say "found insert data:\n",Audio::Nama::json_out($insert_data); # get effect chain indices for wet/dry arms my $wet_effect_chain = delete $insert_data->{wet_effect_chain}; my $dry_effect_chain = delete $insert_data->{dry_effect_chain}; my $class = delete $insert_data->{class}; $insert_data->{track} = $track->name; my $insert = $class->new(%$insert_data); #$Audio::Nama::by_index{$wet_effect_chain}->add($insert->wet_name, $tn{$insert->wet_name}->vol) #$Audio::Nama::by_index{$dry_effect_chain}->add($insert->dry_name, $tn{$insert->dry_name}->vol) } @{$self->inserts_data}; } sub add_region { my ($self, $track) = @_; # there is also a check in uncache track Audio::Nama::throw($track->name.": track already has region definition\n", "failed to apply region @$self->{region}\n"), return if $track->is_region; $track->set(region_start => $self->{region}->[0], region_end => $self->{region}->[1]); } sub add { my ($self, $track, $successor) = @_; # TODO stop_do_start should take place at this level # possibly reconfiguring engine my $args = {}; $args->{before} = $successor; $args->{surname} = $self->name if $self->name; my $added = $self->add_ops($track, $args); $self->add_inserts($track); $self->add_region($track) if $self->region; $self->add_fades($track) if $self->fade_data; $added } sub add_fades { my ($self, $track) = @_; map{ my %h = %$_; my $fade = Audio::Nama::Fade->new( %h, track => $track->name ) ; } @{ $self->{fade_data} } } sub destroy { my $self = shift; delete $by_index{$self->n}; } #### class routines sub find { # find(): search for an effect chain by attributes # # Returns EffectChain objects in list context, # number of matches in scalar context. my %args = @_; my $unique = delete $args{unique}; # first check for a specified index that matches # an existing chain return $by_index{$args{n}} if $args{n}; # otherwise all specified fields must match my @found = grep { my $fx_chain = $_; # check if any specified fields *don't* match my @non_matches = grep { ! ($fx_chain->{attrib}->{$_} eq $args{$_}) #! ($_ ne 'version' and $args{$_} eq 1 and $fx_chain->$_) } keys %args; # if no non-matches, then all have matched, # and we return true ! scalar @non_matches } values %by_index; warn("unique chain requested but multiple chains found. Skipping.\n"), return if $unique and @found > 1; if( wantarray() ){ $unique ? pop @found : sort{ $a->n cmp $b->n } @found } else { scalar @found } } sub summary { my $self = shift; my @output; push @output, " name: ".$self->name if $self->name; push @output, " track name: ".$self->track_name if $self->track_name; push @output, map{ my $i = Audio::Nama::effect_index( $self->{ops_data}->{$_}->{type} ); my $name = " ". $fx_cache->{registry}->[$i]->{name}; } @{$_->ops_list}; map{ $_,"\n"} @output; } sub move_attributes { my $ec_hash = shift; map { $ec_hash->{attrib}->{$_} = delete $ec_hash->{$_} } grep{ $ec_hash->{$_} } @attributes; } sub DESTROY {} } { #### Effect-chain and -profile routines package Audio::Nama; sub add_effect_chain { my ($name, $track, $successor) = @_; my ($ec) = Audio::Nama::EffectChain::find( unique => 1, user => 1, name => $name, ); if( $ec ){ $ec->add($Audio::Nama::this_track, $successor) } else { Audio::Nama::throw("$name: effect chain not found") } 1; } sub new_effect_profile { logsub("&new_effect_profile"); my ($bunch, $profile) = @_; my @tracks = bunch_tracks($bunch); Audio::Nama::pager( qq(effect profile "$profile" created for tracks: @tracks) ); map { Audio::Nama::EffectChain->new( profile => $profile, user => 1, global => 1, track_name => $_, ops_list => [ $tn{$_}->user_ops ], inserts_data => $tn{$_}->inserts, ); } @tracks; } sub delete_effect_profile { logsub("&delete_effect_profile"); my $name = shift; Audio::Nama::pager( qq(deleting effect profile: $name) ); map{ $_->destroy} Audio::Nama::EffectChain::find( profile => $name ); } sub apply_effect_profile { # overwriting current effects logsub("&apply_effect_profile"); my ($profile) = @_; my @chains = Audio::Nama::EffectChain::find(profile => $profile); # add missing tracks map{ Audio::Nama::pager( "adding track $_" ); add_track($_) } grep{ !$tn{$_} } map{ $_->track_name } @chains; # add effect chains map{ $_->add } @chains; } sub is_effect_chain { my $name = shift; my ($fxc) = Audio::Nama::EffectChain::find(name => $name, unique => 1); $fxc } } 1; __END__Audio-Nama-1.216/lib/Audio/Nama/Options.pm0000644000175000017500000000543613544212613017161 0ustar jrothjroth# --------- Command line options ---------- package Audio::Nama; use Modern::Perl; sub process_command_line_options { my %options = qw( save-alsa a project-root=s d use-pwd p create-project c config=s f gui g text t no-state M net-eci n libecasoundc l help h regenerate-effects-cache r no-static-effects-data S no-static-effects-cache C no-reconfigure-engine R fake-jack J fake-alsa A fake-ecasound E debugging-output D execute-command=s X no-terminal T no-fade-on-transport-start F log=s L no-latency O latency Q sample-rate=s z ); map{$config->{opts}->{$_} = ''} values %options; # long options Getopt::Long::Configure ("bundling"); my $getopts = 'GetOptions( '; map{ $getopts .= qq("$options{$_}|$_" => \\\$config->{opts}->{$options{$_}}, \n)} keys %options; $getopts .= ' )' ; #say $getopts; eval $getopts or die "Stopped.\n"; if ($config->{opts}->{h}){ say $help->{usage}; exit; } } BEGIN { $help->{usage} = <vol return the effect_id # ->old_volume_level is the level saved before muting # ->old_pan_level is the level saved before pan full right/left # commands initialize(); ### class subroutines sub initialize { $n = 0; # incrementing numeric key %by_index = (); # return ref to Track by numeric key %by_name = (); # return ref to Track by name %track_names = (); } sub idx { # return first free track index my $n = 0; while (++$n){ return $n if not $by_index{$n} } } sub new { # returns a reference to an object # # tracks are indexed by: # (1) name and # (2) by an assigned index that is used as chain_id # the index may be supplied as a parameter # # my $class = shift; my %vals = @_; my $novol = delete $vals{novol}; my $nopan = delete $vals{nopan}; my $restore = delete $vals{restore}; say "restoring track $vals{name}" if $restore; my @undeclared = grep{ ! $_is_field{$_} } keys %vals; croak "undeclared field: @undeclared" if @undeclared; # silently return if track already exists # why not return track? TODO return if $by_name{$vals{name}}; my $n = $vals{n} || idx(); my $object = bless { ## defaults ## class => $class, name => "Audio_$n", group => 'Main', n => $n, ops => [], width => 1, vol => undef, pan => undef, modifiers => q(), # start, reverse, audioloop, playat looping => undef, # do we repeat our sound sample source_type => q(soundcard), source_id => "1", send_type => undef, send_id => undef, old_vol_level => undef, @_ }, $class; $track_names{$vals{name}}++; $by_index{$n} = $object; $by_name{ $object->name } = $object; Audio::Nama::add_pan_control($n) unless $nopan or $restore; Audio::Nama::add_volume_control($n) unless $novol or $restore; $Audio::Nama::this_track = $object; $Audio::Nama::ui->track_gui($object->n) unless $object->hide; logpkg(__FILE__,__LINE__,'debug',$object->name, ": ","newly created track",$/,json_out($object->as_hash)); $object; } ### object methods sub snapshot { my $track = shift; my $fields = shift; my %snap; my $i = 0; for(@$fields){ $snap{$_} = $track->$_; } \%snap; } # create an edge representing sound source # blows up when I move it to TrackIO sub input_path { my $track = shift; # the corresponding bus handles input routing for mix tracks # so they don't need to be connected here return() if $track->is_mixing and ! $track->play; # the track may route to: # + another track # + an external source (soundcard or JACK client) # + a WAV file if($track->source_type eq 'track'){ ($track->source_id, $track->name) } elsif($track->rec_status =~ /REC|MON/){ (input_node($track->source_type), $track->name) } elsif($track->play and ! $mode->doodle){ (input_node('wav'), $track->name) } } # remove track object and all effects sub remove { my $track = shift; my $n = $track->n; $ui->remove_track_gui($n); # remove corresponding fades map{ $_->remove } grep { $_->track eq $track->name } values %Audio::Nama::Fade::by_index; # remove effects map{ Audio::Nama::remove_effect($_) } @{ $track->ops }; delete $by_index{$n}; delete $by_name{$track->name}; } # Modified from Object.p to save class # should this be used in other classes? sub as_hash { my $self = shift; my $class = ref $self; my %guts = %{ $self }; $guts{class} = $class; # make sure we save the correct class name return \%guts; } sub input_object { my $track = shift; $Audio::Nama::IO::by_name{$track->name}->{input} } sub output_object { my $track = shift; $Audio::Nama::IO::by_name{$track->name}->{output} } sub rec_setup_script { my $track = shift; join_path(Audio::Nama::project_dir(), $track->name."-rec-setup.sh") } sub rec_cleanup_script { my $track = shift; join_path(Audio::Nama::project_dir(), $track->name."-rec-cleanup.sh") } sub current_edit { $_[0]->{current_edit}//={} } sub is_mixing { my $track = shift; $track->is_mixer and ($track->mon or $track->rec) } sub bus { $bn{$_[0]->group} } { my %system_track = map{ $_, 1} qw( Main Mixdown Eq Low Mid High Boost midi_record_buffer); sub is_user_track { ! $system_track{$_[0]->name} } sub is_system_track { $system_track{$_[0]->name} } } sub engine_group { my $track = shift; $track->{engine_group} || $Audio::Nama::config->{ecasound_engine_name} } sub engine { my $track = shift; $en{$track->engine_group} } sub select_track { my $track = shift; $Audio::Nama::this_track = $track; $this_engine->current_chain( $track->n ); # XXX wrong engine for MIDI track Audio::Nama::set_current_bus(); } sub is_selected { $Audio::Nama::this_track->name eq $_[0]->name } sub rec { $_[0]->rec_status eq REC } sub mon { $_[0]->rec_status eq MON } sub play { $_[0]->rec_status eq PLAY} sub off { $_[0]->rec_status eq OFF } sub current_midi {} sub fades { grep { $_->{track} eq $_[0]->name } values %Audio::Nama::Fade::by_index } } # end package # subclasses { package Audio::Nama::SimpleTrack; # used for Main track use Audio::Nama::Globals qw(:all); use Modern::Perl; use Carp; use Audio::Nama::Log qw(logpkg); use SUPER; no warnings qw(uninitialized redefine); our @ISA = 'Audio::Nama::Track'; sub rec_status { my $track = shift; $track->rw ne OFF ? MON : OFF } sub destination { my $track = shift; return 'Mixdown' if $tn{Mixdown}->rec; return $track->SUPER() if $track->rec_status ne OFF } #sub rec_status_display { $_[0]->rw ne OFF ? PLAY : OFF } sub activate_bus {} } { package Audio::Nama::MasteringTrack; # used for mastering chains use Audio::Nama::Globals qw(:all); use Modern::Perl; use Audio::Nama::Log qw(logpkg); no warnings qw(uninitialized redefine); our @ISA = 'Audio::Nama::SimpleTrack'; sub rec_status{ my $track = shift; return OFF if $track->engine_group ne $en{$Audio::Nama::config->{ecasound_engine_name}}->name; $mode->mastering ? MON : OFF; } sub source_status {} sub group_last {0} sub version {0} } { package Audio::Nama::EarTrack; # for submix helper tracks use Audio::Nama::Globals qw(:all); use Audio::Nama::Util qw(dest_string); use Modern::Perl; use Audio::Nama::Log qw(logpkg); use SUPER; no warnings qw(uninitialized redefine); our @ISA = 'Audio::Nama::SlaveTrack'; sub destination { my $track = shift; my $bus = $track->bus; dest_string($bus->send_type,$bus->send_id, $track->width); } sub source_status { $tn{$_[0]->target}->source_status } sub rec_status { $_[0]->{rw} } sub width { $_[0]->{width} } } { package Audio::Nama::SlaveTrack; use Audio::Nama::Globals qw(:all); use Modern::Perl; use Audio::Nama::Log qw(logpkg); no warnings qw(uninitialized redefine); our @ISA = 'Audio::Nama::Track'; sub width { $tn{$_[0]->target}->width } sub rec_status { $tn{$_[0]->target}->rec_status } sub full_path { $tn{$_[0]->target}->full_path} sub playback_version { $tn{$_[0]->target}->playback_version} sub source_type { $tn{$_[0]->target}->source_type} sub source_id { $tn{$_[0]->target}->source_id} sub source_status { $tn{$_[0]->target}->source_status } sub send_type { $tn{$_[0]->target}->send_type} sub send_id { $tn{$_[0]->target}->send_id} sub dir { $tn{$_[0]->target}->dir } } { package Audio::Nama::BoostTrack; use Audio::Nama::Globals qw(:all); use Modern::Perl; use Audio::Nama::Log qw(logpkg); no warnings qw(uninitialized redefine); our @ISA = 'Audio::Nama::Track'; sub rec_status{ my $track = shift; $mode->mastering ? MON : OFF; } sub send_type { $tn{Main}->send_type } sub send_id { $tn{Main}->send_id } } { package Audio::Nama::CacheRecTrack; use Audio::Nama::Globals qw(:all); use Audio::Nama::Log qw(logpkg); our @ISA = qw(Audio::Nama::SlaveTrack); sub current_version { my $track = shift; my $target = $tn{$track->target}; $target->last + 1 } sub current_wav { my $track = shift; $tn{$track->target}->name . '_' . $track->current_version . '.wav' } sub full_path { my $track = shift; Audio::Nama::join_path( $track->dir, $track->current_wav) } } { package Audio::Nama::MixDownTrack; use Audio::Nama::Globals qw(:all); use Audio::Nama::Log qw(logpkg); use SUPER; our @ISA = qw(Audio::Nama::Track); sub current_version { my $track = shift; my $last = $track->last; my $status = $track->rec_status; #logpkg(__FILE__,__LINE__,'debug', "last: $last status: $status"); if ($status eq REC){ return ++$last} elsif ( $status eq PLAY){ return $track->playback_version } else { return 0 } } sub source_status { my $track = shift; return 'Main' if $track->rec; my $super = $track->super('source_status'); $super->($track) } sub destination { my $track = shift; $tn{Main}->destination if $track->play } sub rec_status { my $track = shift; $track->rw # return REC if $track->rw eq REC; # Audio::Nama::Track::rec_status($track); } sub forbid_user_ops { 1 } } { package Audio::Nama::EditTrack; use Carp qw(carp cluck); use Audio::Nama::Globals qw(:all); use Audio::Nama::Log qw(logpkg); our @ISA = 'Audio::Nama::Track'; our $AUTOLOAD; sub AUTOLOAD { my $self = shift; logpkg(__FILE__,__LINE__,'debug', $self->name, ": args @_"); # get tail of method call my ($call) = $AUTOLOAD =~ /([^:]+)$/; $Audio::Nama::Edit::by_name{$self->name}->$call(@_); } sub DESTROY {} sub current_version { my $track = shift; my $last = $track->last; my $status = $track->rec_status; #logpkg(__FILE__,__LINE__,'debug', "last: $last status: $status"); if ($status eq REC){ return ++$last} elsif ( $status eq PLAY){ return $track->playback_version } else { return 0 } } sub playat_time { logpkg(__FILE__,__LINE__,'logcluck',$_[0]->name . "->playat_time"); $_[0]->play_start_time } } { package Audio::Nama::VersionTrack; use Audio::Nama::Globals qw(:all); use Audio::Nama::Log qw(logpkg); our @ISA ='Audio::Nama::Track'; sub set_version {} sub versions { [$_[0]->version] } } { package Audio::Nama::Clip; # Clips are the units of audio used to # to make sequences. # A clip is created from a track. Clips extend the Track # class in providing a position which derives from the # object's ordinal position in an array (clips attribute) of # the parent sequence object. # Clips differ from tracks in that clips # their one-based position (index) in the sequence items array. # index is one-based. use Audio::Nama::Globals qw(:all); use Audio::Nama::Log qw(logpkg); our @ISA = qw( Audio::Nama::VersionTrack Audio::Nama::Track ); sub sequence { my $self = shift; $Audio::Nama::bn{$self->group} }; sub index { my $self = shift; my $i = 0; for( @{$self->sequence->items} ){ $i++; return $i if $self->name eq $_ } } sub predecessor { my $self = shift; $self->sequence->clip($self->index - 1) } sub duration { my $self = shift; $self->{duration} ? Audio::Nama::Mark::duration_from_tag($self->{duration}) : $self->is_region ? $self->region_end_time - $self->region_start_time : $self->wav_length; } sub endpoint { my $self = shift; $self->duration + ( $self->predecessor ? $self->predecessor->endpoint : 0 ) } sub playat_time { my $self = shift; my $previous = $self->predecessor; $previous ? $previous->endpoint : 0 } sub rec_status { $_[0]->version ? PLAY : OFF } # we currently are not compatible with offset run mode # perhaps we can enforce OFF status for clips under # offset run mode } # end package { package Audio::Nama::Spacer; our @ISA = 'Audio::Nama::Clip'; use SUPER; use Audio::Nama::Object qw(duration); sub rec_status { OFF } sub new { my ($class,%args) = @_; # remove args we will process my $duration = delete $args{duration}; # give the remainder to the superclass constructor @_ = ($class, %args); my $self = super(); #logpkg(__FILE__,__LINE__,'debug',"new object: ", json_out($self->as_hash)); #logpkg(__FILE__,__LINE__,'debug', "items: ",json_out($items)); # set the args removed above $self->{duration} = $duration; $self; } } # end package { package Audio::Nama::WetTrack; # for inserts use Audio::Nama::Globals qw(:all); use Modern::Perl; use Audio::Nama::Log qw(logpkg); our @ISA = 'Audio::Nama::SlaveTrack'; } { package Audio::Nama::MidiTrack; use Audio::Nama::Globals qw(:all); use Modern::Perl; use SUPER; use Audio::Nama::Log qw(logpkg); our @ISA = qw(Audio::Nama::Track); sub new { my ($class, %args) = @_; my $self = super(); $self } # TODO enable sub mute { my $track = shift; if ( $track->exists_midi ) { Audio::Nama::midish_cmd( 'mute ' . $_[0]->current_midi ) } } sub unmute { my $track = shift; if ( $track->exists_midi ) { # mute unselected versions map{ Audio::Nama::midish_cmd( 'mute '. midi_version_name($track->name, $_) ) } grep{ $_ != $track->version } @{$track->versions}; Audio::Nama::midish_cmd( 'unmute ' . $_[0]->current_midi ) } } sub rw_set { my $track = shift; my ($bus, $setting) = @_; $track->{rw} = uc $setting; } sub exists_midi { my $track = shift; my ($tlist) = Audio::Nama::midish_cmd('print [tlist]'); $tlist =~ s/[}{]//g; my ($match) = grep{$_ eq $track->current_midi} split " ", $tlist; } sub rec_status { my $self = shift; if ( $self->rw eq REC and $self->is_selected ) { REC } elsif( $self->rw eq REC and ! $self->is_selected ) { PLAY } elsif( $self->rw eq PLAY ) { PLAY } else { OFF } } sub versions { $_[0]->{midi_versions} } sub select_track { my $track = shift; $Audio::Nama::this_track = $track; $track->unmute; Audio::Nama::set_current_bus(); } sub current_midi { # current MIDI track # provides the name of the midish track corresponding to the selected version # example: synth_2, for track synth, version 2 # analagous to current_wav() for audio track which would output synth_2.wav my $track = shift; if ($track->rec_status eq REC) { midi_version_name($track->name, $track->current_version) } elsif ( $track->rec_status eq PLAY) { midi_version_name($track->name, $track->playback_version) } else { logpkg(__FILE__,__LINE__,'debug', "track ", $track->name, ": no current version") ; undef; } } sub set_io { my $track = shift; my ($direction, $id) = @_; my $type = 'midi'; my $type_field = $direction."_type"; my $id_field = $direction."_id"; # respond to query if ( ! $id ){ return $track->$type_field ? $track->$id_field : undef } # set values, returning new setting $track->set($type_field => $type); $track->set($id_field => $id); } sub set_rw { my $track = shift; my ($bus, $setting) = @_; Audio::Nama::throw("can't set MIDI track to MON. Setting is unchanged"), return if $setting eq MON; $track->{rw} = $setting; # mute all versions #$logic{$setting}->($bus, $setting); } sub create_midi_version { my $track = shift; my $n = shift; Audio::Nama::add_midi_track(midi_version_name($track->name, $n), hide => 1); } sub set_version { my ($track, $n) = @_; my $name = $track->name; if ($n == 0){ Audio::Nama::pager("$name: version set to zero, following bus default\n"); $track->set(version => $n) } elsif ( grep{ $n == $_ } @{$track->versions} ){ Audio::Nama::pager("$name: anchoring version $n\n"); $track->set(version => $n); } else { Audio::Nama::throw("$name: version $n does not exist, skipping.\n") } } sub midi_version { my $track = shift; join '_', $track->name, $track->version if $track->version } } 1; __END__Audio-Nama-1.216/lib/Audio/Nama/BusUtil.pm0000644000175000017500000000137013544212613017106 0ustar jrothjroth{ package Audio::Nama::BusUtil; use Role::Tiny; use Modern::Perl; use Audio::Nama::Globals qw(%tn %bn PLAY OFF MON); sub version_has_edits { my ($track) = @_; grep { $_->host_track eq $track->name and $_->host_version == $track->playback_version } values %Audio::Nama::Edit::by_name; } sub bus_tree { # for solo function to work in sub buses my $track = shift; my $mix = $track->group; return if $mix eq 'Main'; ($mix, $tn{$mix}->bus_tree); } sub activate_bus { my $track = shift; Audio::Nama::add_bus($track->name) unless $track->is_system_track; } sub is_mixer { my $track = shift; my $type = $track->{source_type}; return unless defined $type and $type eq 'bus'; my $id = $track->{source_id}; my $bus = $bn{$id}; $bus } } 1;Audio-Nama-1.216/lib/Audio/Nama/EffectNickname.pm0000644000175000017500000000222713544212613020363 0ustar jrothjrothpackage Audio::Nama::EffectNickname; use Modern::Perl; use Role::Tiny; sub effect_nickname_count { my ($track, $nick) = @_; my $count = 0; for my $FX ($track->user_ops_o){ $count++ if $FX->name =~ /^$nick\d*$/ } $count } sub unique_surname { my ($track, $surname) = @_; # increment supplied surname to be unique to the track if necessary # return arguments: # $surname, $previous_surnames my $max = undef; my %found; for my $FX ($track->user_ops_o) { if( $FX->surname =~ /^$surname(\d*)$/) { $found{$FX->surname}++; no warnings qw(uninitialized numeric); $max = $1 if $1 > $max; } } if (%found){ $surname.++$max, join ' ',sort keys %found } else { $surname } } sub unique_nickname { my ($track, $nickname) = @_; my $i = 0; my @found; for my $FX ($track->user_ops_o) { if( $FX->name =~ /^$nickname(\d*)$/) { push @found, $FX->name; $i = $1 if $1 and $1 > $i } } $nickname. (@found ? ++$i : ""), "@found" } # return effect IDs matching a surname sub with_surname { my ($track, $surname) = @_; my @found; for my $FX ($track->user_ops_o) { push @found, $FX->id if $FX->surname eq $surname } @found ? "@found" : undef } 1;Audio-Nama-1.216/lib/Audio/Nama/Memoize.pm0000644000175000017500000000241513544212613017125 0ustar jrothjroth# ------ Memoize subroutines ------ package Audio::Nama; use Modern::Perl; use Memoize qw(memoize unmemoize); BEGIN { # OPTMIZATION my @wav_functions = qw( get_versions candidates _targets _versions ); my @track_methods = qw( dir basename full_path group_last last current_wav current_version playback_version maybe_playback rec_status region_start_time region_end_time playat_time user_ops input_path waveform ); sub track_memoize { # before generate_setup return unless $config->{memoize}; map{package Audio::Nama::Track; memoize($_) } @track_methods; } sub track_unmemoize { # after generate_setup return unless $config->{memoize}; map{package Audio::Nama::Track; unmemoize ($_)} @track_methods; } sub restart_wav_memoize { return unless $config->{memoize}; map{package Audio::Nama::Wav; unmemoize ($_); memoize($_) } @wav_functions; } sub latency_memoize { map{ memoize($_) } ('Audio::Nama::self_latency','Audio::Nama::latency_of'); } sub latency_unmemoize { map{ unmemoize($_) } ('Audio::Nama::self_latency','Audio::Nama::latency_of'); } sub latency_rememoize { latency_unmemoize(); latency_memoize() } sub init_wav_memoize { return unless $config->{memoize}; map{package Audio::Nama::Wav; memoize($_) } @wav_functions; } } 1; __END__Audio-Nama-1.216/lib/Audio/Nama/Wavinfo.pm0000644000175000017500000000352013544212613017127 0ustar jrothjroth# ------- WAV file info routines --------- package Audio::Nama; use Modern::Perl; ### WAV file length/format/modify_time are cached in $setup->{wav_info} ### Cached methods sub wav_length { my $path = shift; _update_wav_cache($path); $setup->{wav_info}->{$path}{length} } sub wav_format { my $path = shift; _update_wav_cache($path); $setup->{wav_info}->{$path}{format} } ### Implementation sub cache_wav_info { my @files = File::Find::Rule ->file() ->name( '*.wav' ) ->in( this_wav_dir() ); map{ _get_wav_info($_) } @files; } sub _get_wav_info { my $path = shift; #say "path: $path"; $setup->{wav_info}->{$path}{length} = _get_length($path); $setup->{wav_info}->{$path}{format} = _get_format($path); $setup->{wav_info}->{$path}{modify_time} = _get_modify_time($path); } sub _get_length { my $path = shift; my $length = ecasound_get_info($path, 'ai-get-length'); $length ? sprintf("%.4f", $length) : undef } sub _get_format { my $path = shift; ecasound_get_info($path, 'ai-get-format'); } sub _get_modify_time { my $path = shift; my @stat = stat $path; $stat[9] } sub _update_wav_cache { my $path = shift; { no warnings 'uninitialized'; return unless _get_modify_time($path) != $setup->{wav_info}->{$path}{modify_time}; } throw("WAV file $path has changed! Updating cache."); _get_wav_info($path) } sub ecasound_get_info { # get information about an audio object my ($path, $command) = @_; local $config->{category} = 'ECI_WAVINFO'; $path = qq("$path"); my $old = ecasound_iam('cs-selected'); teardown_engine(); ecasound_iam('cs-add gl'); ecasound_iam('c-add g'); ecasound_iam('ai-add ' . $path); ecasound_iam('ao-add null'); ecasound_iam('cs-connect'); ecasound_iam('ai-select '. $path); my $result = ecasound_iam($command); teardown_engine(); ecasound_iam("cs-load $old") if $old; $result; } 1; __END__ Audio-Nama-1.216/lib/Audio/Nama/Effect.pm0000644000175000017500000010465713544212613016727 0ustar jrothjroth{ package Audio::Nama::Effect; use Modern::Perl; use List::MoreUtils qw(first_index insert_after_string); use Carp qw(carp cluck croak confess); use Data::Dumper::Concise; use Audio::Nama::Assign qw(json_out); use Audio::Nama::Log qw(logsub logpkg); use Audio::Nama::Globals qw( $fx $fx_cache $ui %ti %tn %bn %en $config $setup $project $this_engine $this_track); use Audio::Nama::Object qw( id type chain class params params_log display parent owns bypassed name surname predecessor ); *this_op = \&Audio::Nama::this_op; *this_param = \&Audio::Nama::this_param; *this_stepsize = \&Audio::Nama::this_stepsize; our %by_id; our $AUTOLOAD; import_engine_subs(); sub initialize { %by_id = () ; # effect variables - no object code (yet) $fx->{id_counter} = "A"; # autoincrement counter # volume settings $fx->{muted} = []; } sub AUTOLOAD { my $self = shift; #say "got self: $self", Audio::Nama::Dumper $self; die 'not object' unless ref $self; # get tail of method call my ($call) = $AUTOLOAD =~ /([^:]+)$/; # see if this can be satisfied by a field from # the corresponding effects registry entry $call = 'name' if $call eq 'fxname'; $self->about->{$call} } sub DESTROY {} sub new { my ($class, %args) = @_; my $is_restore = $args{restore}; # remove arguments that won't be part of object delete $args{$_} for qw(restore before); my $self; my $id = $args{id}; # return an existing object that has this ID # Why do this instead of throw an exception? if ($id and $self = fxn($id)){ logpkg(__FILE__,__LINE__,'debug',"$id: returning existing object, constructor arguments ignored"); return $self } my $i = effect_index($args{type}); defined $i or confess "$args{type}: plugin not found."; # allocate effect ID if we don't have one yet # my $how_allocated = $id ? 'recycled' : 'issued'; $id //= new_effect_id(); logpkg(__FILE__,__LINE__,'debug',"$id: effect ID $how_allocated for track $args{chain}"); $args{id} = $id; $args{display} //= $fx_cache->{registry}->[$i]->{display}; $args{owns} //= []; my $track = $ti{$args{chain}}; my $parent_id = $args{parent}; # set defaults for effects without values provided # but skip controllers # append_effect() also assigns defaults, so why not # do all the assigning here? if (! $parent_id and ! $args{params}){ my @vals; logpkg(__FILE__,__LINE__,'debug', "no settings found, loading defaults if present"); # if the effect is a controller (has a parent), we don't # initialize the first parameter (the control target) for my $j (0..$fx_cache->{registry}->[$i]->{count} - 1) { push @vals, $fx_cache->{registry}->[$i]->{params}->[$j]->{default}; } logpkg(__FILE__,__LINE__,'debug', "copid: $id defaults: @vals"); $args{params} = \@vals; } logpkg(__FILE__,__LINE__,'debug', "effect args: ",Dumper \%args); $self = bless \%args, $class; $by_id{$self->id} = $self; return $self if $is_restore; if ($parent_id) { logpkg(__FILE__,__LINE__,'debug', "parent found: $parent_id"); # store relationship my $parent = fxn($parent_id); my $owns = $parent->owns; logpkg(__FILE__,__LINE__,'debug',"parent owns @$owns"); # register effect_id with parent unless it is already there push @$owns, $id unless grep { $id eq $_ } @$owns; logpkg(__FILE__,__LINE__,'debug',sub{join " ", "my attributes:", json_out($self->as_hash)}); # find position of parent id in the track ops array # and insert child id immediately afterwards # unless already present insert_after_string($parent_id, $id, @{$track->ops}) unless grep {$id eq $_} @{$track->ops} } else { # append effect_id to track list unless already present push @{$track->ops}, $id unless grep {$id eq $_} @{$track->ops} } $self } # provide object { no warnings 'redefine'; sub parent { my $self = shift; fxn($self->{parent}); } } sub is_read_only { my ($self, $param) = @_; no warnings 'uninitialized'; $self->about->{params}->[$param]->{dir} eq 'output' } sub remove_name { my $self = shift; delete $self->{name} } sub set_name { my $self = shift; $self->{name} = shift } sub set_surname { my $self = shift; $self->{surname} = shift} sub is_controller { my $self = shift; $self->parent } sub is_channel_op { my $self = shift; $Audio::Nama::config->{ecasound_channel_ops}->{$self->type} } sub has_read_only_param { my $self = shift; no warnings 'uninitialized'; my $entry = $self->about; for(0..scalar @{$entry->{params}} - 1) { return 1 if $entry->{params}->[$_]->{dir} eq 'output' } } sub registry_index { my $self = shift; $fx_cache->{full_label_to_index}->{ $self->type }; } sub ecasound_controller_index { logsub("&ecasound_controller_index"); my $self = shift; my $n = $self->chain; my $id = $self->id; my $opcount = 0; logpkg(__FILE__,__LINE__,'debug', "id: $id, n: $n, ops: @{ $ti{$n}->ops }" ); for my $op (@{ $ti{$n}->ops }) { # increment only controllers next unless fxn($op)->is_controller; $opcount++; last if $op eq $id; } $opcount; } sub ecasound_effect_index { logsub("&ecasound_effect_index"); my $self = shift; my $n = $self->chain; my $id = $self->id; my $opcount = 0; logpkg(__FILE__,__LINE__,'debug', "id: $id, n: $n, ops: @{ $ti{$n}->ops }" ); for my $op (@{ $ti{$n}->ops }) { my $fx = fxn($op); next if $fx->is_controller; ++$opcount; # first index is 1 last if $op eq $id } no warnings 'uninitialized'; $self->offset + $opcount; } sub ecasound_effect_index_ { my $self = shift; 1 + first_index {$_->id eq $self->id } $self->track->ops_ecasound_order(); } sub track_effect_index { # the position of the ID in the track's op array my $self = shift; my $id = $self->id; my $pos = first_index {$id eq $_} @{$self->track->ops} ; $pos } sub controllers { my $self = shift; my %children; # we want controllers with this parent, also controllers # whos parents are children of this parent, and children # of those children no warnings; my @ctrl = map { $_->id } grep{ $_->{parent} eq $self->id or $children{$_->{parent}} and $children{$_->id}++ } map{ fxn($_)} @{ $self->track->ops }; @ctrl } sub sync_one_effect { my $self = shift; my $chain = $self->chain; $this_engine->current_chain($chain); $this_engine->current_chain_operator($self->ecasound_effect_index); $self->set(params => get_ecasound_cop_params( scalar @{$self->params} )); } sub offset { my $self = shift; $fx->{offset}->{$self->chain} } sub root_parent { my $self = shift; my $parent = $self->parent; $parent and $parent->parent or $parent or $self } sub about { my $self = shift; $fx_cache->{registry}->[$self->registry_index] } sub track { $ti{$_[0]->chain} } sub trackname { $_[0]->track->name } sub ladspa_id { my $self = shift; $Audio::Nama::fx_cache->{ladspa_label_to_unique_id}->{$self->type} } sub nameline { my $self = shift; my @attr_keys = qw(id name surname fxname type ladspa_id); my %display = map{ $_ => 1 } grep { !/fxname|type|ladspa_id|id/ } @attr_keys; my %attr = map{ $_ => $self->$_ } @attr_keys ; my $bypassed = $self->{bypassed} ? " (bypassed)" : ''; not defined $attr{$_} and delete $attr{$_} for @attr_keys; my $nameline = join qq( ), map{ $display{$_} ? "$_:$attr{$_}" : $attr{$_} } grep{$attr{$_}} @attr_keys; $nameline .= "$bypassed\n"; $nameline; } sub _effect_index { my $self = shift; effect_index($self->type) } sub _modify_effect { my ($self, $parameter, $value, $sign) = @_; no warnings 'uninitialized'; my $op_id = $self->id; $parameter--; # convert to zero-based my $code = $self->type; my $i = $self->_effect_index; defined $i or confess "undefined effect code for $op_id: ",Audio::Nama::Dumper $self; my $parameter_count = scalar @{ $self->about->{params} }; Audio::Nama::pager("$op_id: parameter (", $parameter + 1, ") out of range, skipping.\n"), return unless ($parameter >= 0 and $parameter < $parameter_count); Audio::Nama::pager("$op_id: parameter $parameter is read-only, skipping\n"), return if $self->is_read_only($parameter); my $new_value; if ($sign) { $new_value = eval ( join " ", $self->params->[$parameter], $sign, $value ); } else { $new_value = $value } logpkg(__FILE__,__LINE__,'debug', "id $op_id p: $parameter, sign: $sign value: $value"); update_effect( $op_id, $parameter, $new_value); 1 } sub _remove_effect { logsub("&_remove_effect"); my $self = shift; my $id = $self->id; my $n = $self->chain; my $parent = $self->parent; my $owns = $self->owns; logpkg(__FILE__,__LINE__,'debug', "id: $id", ($parent ? ". parent: ".$parent->id : '' )); my $object = $parent ? q(controller) : q(chain operator); logpkg(__FILE__,__LINE__,'debug', qq(ready to remove $object "$id" from track "$n")); $ui->remove_effect_gui($id); # recursively remove children logpkg(__FILE__,__LINE__,'debug',"children found: ". join ",",@$owns) if defined $owns; map{ remove_effect($_) } @$owns if defined $owns; ; # remove chain operator if ( ! $parent ) { remove_op($id) } # remove controller else { remove_op($id); # remove parent ownership of deleted controller my $parent_owns = $parent->owns; logpkg(__FILE__,__LINE__,'debug',"parent $parent owns: ". join ",", @$parent_owns); @$parent_owns = (grep {$_ ne $id} @$parent_owns); logpkg(__FILE__,__LINE__,'debug',"parent $parent new owns list: ". join ",", @$parent_owns); } # remove effect ID from track if( my $track = $ti{$n} ){ my @ops_list = @{$track->ops}; my @new_list = grep { $_ ne $id } @ops_list; $track->{ops} = [ @new_list ]; } #set_current_op($this_track->ops->[0]); #set_current_param(1); delete $by_id{$self->id}; return(); } sub position_effect { #logsub('&position_effect'); my($self, $pos) = @_; my $op = $self->id; #Audio::Nama::pager("$op or $pos: controller not allowed, skipping.\n"), return # if grep{ fxn($_)->is_controller } $op, $pos; # first, modify track data structure my $track = $ti{$self->chain}; my $op_index = $self->track_effect_index; my @children = $self->controllers; my $count = scalar @children + 1; my @new_op_list = @{$track->ops}; # remove op and children my @op_and_ctrl = splice @new_op_list, $op_index, $count; if ( $pos eq 'ZZZ'){ # put it at the end push @new_op_list, @op_and_ctrl } else { my $POS = fxn($pos); my $track2 = $ti{$POS->chain}; Audio::Nama::pager("$pos: position belongs to a different track, skipping.\n"), return unless $track eq $track2; my $new_op_index = $POS->track_effect_index; # insert op splice @new_op_list, $new_op_index, 0, @op_and_ctrl; } # easier to reconfigure the engine than to code for # repositioning ecasound effects. say join " - ",@new_op_list; @{$track->ops} = @new_op_list; Audio::Nama::request_setup(); $this_track = $track; nama_cmd('show_track'); } sub apply_op { logsub("&apply_op"); my $self = shift; local $config->{category} = 'ECI_FX'; my $id = $self->id; logpkg(__FILE__,__LINE__,'debug', "id: $id"); logpkg(__FILE__,__LINE__,'logcluck', "$id: expected effect entry not found!"), return if effect_entry_is_bad($id); my $code = $self->type; my $dad = $self->parent; my $chain = $self->chain; logpkg(__FILE__,__LINE__,'debug', "chain: $chain, type: $code"); # if code contains colon, then follow with comma (preset, LADSPA) # if code contains no colon, then follow with colon (ecasound, ctrl) $code = '-' . $code . ($code =~ /:/ ? q(,) : q(:) ); my @vals = @{ $self->params }; logpkg(__FILE__,__LINE__,'debug', "values: @vals"); # we start to build iam command my $add_cmd = $dad ? "ctrl-add " : "cop-add "; $add_cmd .= $code . join ",", @vals; # append the -kx operator for a controller-controller $add_cmd .= " -kx" if $dad and $dad->is_controller; logpkg(__FILE__,__LINE__,'debug', "command: $add_cmd"); $this_engine->current_chain($chain); $this_engine->current_chain_operator($dad->ecasound_effect_index) if $dad; $this_engine->ecasound_iam($add_cmd); $this_engine->ecasound_iam("cop-bypass on") if $self->bypassed; my $owns = $self->owns; (ref $owns) =~ /ARRAY/ or croak "expected array"; logpkg(__FILE__,__LINE__,'debug',"children found: ". join ",", @$owns); } #### Effect related routines, some exported, non-OO # main namespace imports from us, we'll import manually # to work around dependence issues sub import_engine_subs { *ecasound_iam = \&Audio::Nama::ecasound_iam; *sleeper = \&Audio::Nama::sleeper; *nama_cmd = \&Audio::Nama::nama_cmd; *pager = \&Audio::Nama::pager; *this_op = \&Audio::Nama::this_op; *this_param = \&Audio::Nama::this_param; *this_stepsize = \&Audio::Nama::this_stepsize; } use Exporter qw(import); our %EXPORT_TAGS = ( 'all' => [ qw( effect_index full_effect_code effect_entry_is_bad check_fx_consistency new_effect_id add_effect _add_effect append_effect remove_effect remove_fader_effect modify_effect modify_multiple_effects update_ecasound_effect update_effect sync_effect_parameters find_op_offsets apply_ops expanded_ops_list bypass_effects restore_effects fxn set_current_op set_current_param set_current_stepsize increment_param decrement_param set_parameter_value ) ] ); our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); our @EXPORT = (); no warnings 'uninitialized'; # needed to avoid confusing test TAP output sub effect_entry_is_bad { my $id = shift; ! defined $id or ! $Audio::Nama::Effect::by_id{$id} } # make sure the chain number (track index) is set sub set_chain_value { my $p = shift; return if $p->{chain}; # return if already set # set chain from track if known if( $p->{track} ) { $p->{chain} = $p->{track}->n; delete $p->{track} } # set chain from parent effect if known (add controller) elsif( $p->{parent_id}) { $p->{chain} = fxn($p->{parent_id})->chain } # set chain from insert target if known (insert effect) elsif( $p->{before} ) { $p->{chain} = fxn($p->{before})->chain; } #logpkg(__FILE__,__LINE__,'debug',(json_out($p)); } # How effect chains are added (by default before fader) # user command: add_effect # add_effect(effect_chain => $fxc) calls insert_effect() # insert_effect() # * removes preceding operators # * calls append_effect(effect_chain => $fxc) # + which calls $fxc->add # + which calls append_effect() for each effect # * restores the operators sub add_effect { #logsub('&add_effect'); my $args = shift; my $added = _add_effect($args); $added->[0]->id } sub _add_effect { my $p = shift; logsub("&_add_effect"); #logpkg(__FILE__,__LINE__,'debug',sub{ "add effect arguments - 0:\n".json_out($p)}); set_chain_value($p); ### We prohibit creating effects on the Mixdown track ### We check $track->forbid_user_ops ### which is set on the Mixdown track, ### An alternative would be giving each ### Track its own add_effect method ### For now this is a single case die "user effects forbidden on this track" if $ti{$p->{chain}} and $ti{$p->{chain}}->forbid_user_ops and $p->{type} !~ /$config->{latency_op}/; logpkg(__FILE__,__LINE__,'debug',sub{ "add effect arguments - 1:\n".json_out($p)}); # either insert or add, depending on 'before' setting my $added = (defined $p->{before} and $p->{before} ne 'ZZZ') ? insert_effect($p) : append_effect($p); } sub append_effect { my $p = shift; logsub("&append_effect",Dumper $p); my %args = %$p; $args{params} //= []; my $track = $ti{$args{chain}}; my $add_effects_sub; # we will execute this with engine stopped my @added; if( $args{effect_chain}) { # we will create and apply the effects later $add_effects_sub = sub{ $args{effect_chain}->add($track)}; } else { # create the effect now, apply it later # assign defaults if no values supplied my $count = $fx_cache->{registry}->[effect_index($args{type})]->{count} ; my @defaults = @{fx_defaults($args{type})}; if( @defaults ) { for my $i (0..$count - 1) { $args{params}[$i] = $defaults[$i] if ! defined $args{params}[$i] or $args{params}[$i] eq '*' } } my $FX = Audio::Nama::Effect->new(%args); push @added, $FX; if( ! $FX->name ) { while( my($alias, $type) = each %{$fx->{alias}} ) { $FX->set_name($track->unique_nickname($alias)), # need to reset 'each' keys %{$fx->{alias}}, last if $type eq $FX->type } } $ui->add_effect_gui(\%args) unless $track->hide; # XX we should announce that will take effect in new setup $add_effects_sub = $FX->is_channel_op ? sub { Audio::Nama::request_setup() } : sub{ $FX->apply_op }; } if( $this_engine->valid_setup() ) { if ($this_engine->started()) { $track->mute; my $result = Audio::Nama::stop_do_start($add_effects_sub, 0.05); push @added, @$result if is_array($result); $track->unmute; } else { my $result = $add_effects_sub->(); push @added, @$result if is_array($result); } } \@added } sub is_array { ref $_[0] eq 'ARRAY' } sub insert_effect { my $p = shift; logsub("&insert_effect",Dumper $p); my %args = %$p; local $config->{category} = 'ECI_FX'; return(append_effect(\%args)) if $args{before} eq 'ZZZ'; my $running = $this_engine->started(); pager("Cannot insert effect while engine is recording.\n"), return if $running and Audio::Nama::ChainSetup::really_recording(); pager("Cannot insert effect before controller.\n"), return if fxn($args{before})->is_controller; if ($running){ $ui->stop_heartbeat; Audio::Nama::mute(); $this_engine->stop_command; sleeper( 0.05); } my $pos = fxn($args{before}) or die "$args{before}: effect ID not found"; my $track = $pos->track; $this_track eq $pos->track or die "$args{before} is not on current track"; # #logpkg(__FILE__,__LINE__,'debug', $track->name, $/; #logpkg(__FILE__,__LINE__,'debug', "@{$track->ops}") my $offset = $pos->track_effect_index; my $last_index = $#{$track->ops}; # note ops after insertion point my @after_ops = @{$track->ops}[$offset..$last_index]; # remove corresponding chain operators from the engine logpkg(__FILE__,__LINE__,'debug',"ops to remove and re-apply: @after_ops"); if ( $this_engine->valid_setup ){ map{ remove_op($_)} reverse @after_ops; # reverse order for correct index } # remove the corresponding ids from the track list splice @{$track->ops}, $offset; # add the new effect in the proper position my $added = append_effect(\%args); logpkg(__FILE__,__LINE__,'debug',"@{$track->ops}"); # replace the effects that had been removed push @{$track->ops}, @after_ops; logpkg(__FILE__,__LINE__,'debug',sub{"@{$track->ops}"}); # replace the corresponding Ecasound chain operators if ($this_engine->valid_setup){ map{ fxn($_)->apply_op } @after_ops; } if ($running){ ecasound_iam('start'); sleeper(0.3); Audio::Nama::unmute(); $ui->start_heartbeat; } $added; } sub modify_effect { logsub("&modify_effect"); my ($op_id, $parameter, $sign, $value) = @_; # $parameter: one-based my $FX = fxn($op_id) or pager("$op_id: non-existing effect id. Skipping.\n"), return; $FX->_modify_effect($parameter, $value, $sign); } sub modify_multiple_effects { logsub("&modify_multiple_effects"); my ($op_ids, $parameters, $sign, $value) = @_; map{ my $op_id = $_; map{ my $parameter = $_; modify_effect($op_id, $parameter, $sign, $value); set_current_op($op_id); set_current_param($parameter); } @$parameters; } @$op_ids; } sub remove_effect { logsub("&remove_effect"); my $id = shift; my $FX = fxn($id) or logpkg(__FILE__,__LINE__,'logcarp',"$id: does not exist, skipping...\n"), return; $FX->_remove_effect; } sub full_effect_code { # get text effect code from user input, which could be # - LADSPA Unique ID (number) # - LADSPA Label (el:something) # - abbreviated LADSPA label (something) # - Ecasound operator (something) # - abbreviated Ecasound preset (something) # - Ecasound preset (pn:something) # - user alias # there is no interference in these labels at present, # so we offer the convenience of using them without # el: and pn: prefixes. my $input = shift; my $code; if ($input !~ /\D/) # i.e. $input is all digits { $code = $fx_cache->{ladspa_id_to_label}->{$input}; } elsif ( $fx_cache->{full_label_to_index}->{$input} ) { $code = $input } else { $code = $fx_cache->{partial_label_to_full}->{$input} } $code } # get integer effect index for Nama effect registry # e.g. ea => 2 sub effect_index { my $code = shift; my $i = $fx_cache->{full_label_to_index}->{full_effect_code($code)}; defined $i or $config->{opts}->{E} or warn("$code: effect index not found\n"); $i } sub fx_defaults { my $code = shift; my $i = effect_index($code); my $values = []; foreach my $p ( @{ $fx_cache->{registry}->[$i]->{params} }) { return [] unless defined $p->{default}; push @$values, $p->{default}; } $values } ## Ecasound engine -- apply/remove chain operators sub apply_ops { # in addition to operators in .ecs file logsub("&apply_ops"); for my $track ( Audio::Nama::audio_tracks() ) { my $n = $track->n; next unless Audio::Nama::ChainSetup::is_ecasound_chain($n); logpkg(__FILE__,__LINE__,'debug', "chain: $n, offset: $fx->{offset}->{$n}"); $this_engine->reset_ecasound_selections_cache(); $this_engine->current_chain($n); $track->apply_ops; } } sub remove_op { # remove chain operator from Ecasound engine logsub("&remove_op"); local $config->{category} = 'ECI_FX'; # only if engine is configured return unless $this_engine->valid_setup(); my $id = shift; my $self = fxn($id); Audio::Nama::request_setup(), return if $self->is_channel_op; my $n = $self->chain; # select chain return unless $this_engine->valid_setup(); $this_engine->current_chain($n); # deal separately with controllers and chain operators my $index; if ( ! $self->is_controller) { # chain operator logpkg(__FILE__,__LINE__,'debug', "no parent, assuming chain operator"); $index = $self->ecasound_effect_index; logpkg(__FILE__,__LINE__,'debug', "ops list for chain $n: @{$ti{$n}->ops}"); logpkg(__FILE__,__LINE__,'debug', "operator id to remove: $id"); logpkg(__FILE__,__LINE__,'debug', "ready to remove from chain $n, operator id $id, index $index"); logpkg(__FILE__,__LINE__,'debug',sub{ecasound_iam("cs")}); $this_engine->current_chain_operator($self->ecasound_effect_index); logpkg(__FILE__,__LINE__,'debug',sub{"selected operator: ". ecasound_iam("cop-selected")}); $this_engine->ecasound_iam("cop-remove"); $this_engine->reset_ecasound_selections_cache(); logpkg(__FILE__,__LINE__,'debug',sub{ecasound_iam("cs")}); } else { # controller logpkg(__FILE__,__LINE__,'debug', "has parent, assuming controller"); my $ctrl_index = $self->ecasound_controller_index; logpkg(__FILE__,__LINE__,'debug', ecasound_iam("cs")); $this_engine->current_chain_operator($self->root_parent->ecasound_controller_index); logpkg(__FILE__,__LINE__,'debug', "selected operator: ". ecasound_iam("cop-selected")); $this_engine->current_controller($ctrl_index); $this_engine->ecasound_iam("ctrl-remove"); logpkg(__FILE__,__LINE__,'debug', ecasound_iam("cs")); } } # Track sax effects: A B C GG HH II D E F # GG HH and II are controllers applied to chain operator C # # to remove controller HH: # # for Ecasound, chain op index = 3, # ctrl index = 2 # = track_effect_index HH - track_effect_index C # # # for Nama, chain op array index 2, # ctrl arrray index = chain op array index + ctrl_index # = effect index - 1 + ctrl_index # # ## Nama effects ## have a unique ID from capital letters ## IDs are kept in the $track->ops ## Rules for allocating IDs ## new_effect_id() - issues a new ID ## effect_init() - initializes a Nama effect, should be called effect_init() ## add_effect sub new_effect_id { # increment $fx->{id_counter} if necessary # to find an unused effect_id while( fxn($fx->{id_counter})){ $fx->{id_counter}++}; $fx->{id_counter} } ## synchronize Ecasound chain operator parameters # with Nama effect parameter sub update_ecasound_effect { local $config->{category} = 'ECI_FX'; # update the parameters of the Ecasound chain operator # referred to by a Nama operator_id #logsub("&update_effect"); return unless $this_engine->valid_setup; #my $es = ecasound_iam("engine-status"); #logpkg(__FILE__,__LINE__,'debug', "engine is $es"); #return if $es !~ /not started|stopped|running/; my ($id, $param, $val) = @_; my $FX = fxn($id) or carp("$id: effect not found. skipping...\n"), return; $param++; # so the value at $p[0] is applied to parameter 1 my $chain = $FX->chain; return unless Audio::Nama::ChainSetup::is_ecasound_chain($chain); logpkg(__FILE__,__LINE__,'debug', "chain $chain id $id param $param value $val"); # $param is zero-based. # $FX->params is zero-based. $this_engine->current_chain($chain); # update Ecasound's copy of the parameter if( $FX->is_controller ){ my $i = $FX->ecasound_controller_index; logpkg(__FILE__,__LINE__,'debug', "controller $id: track: $chain, index: $i, param: $param, value: $val"); $this_engine->current_controller($i); $this_engine->current_controller_parameter($param); $this_engine->ecasound_iam("ctrlp-set $val"); } else { # is operator my $i = $FX->ecasound_effect_index; logpkg(__FILE__,__LINE__,'debug', "operator $id: track $chain, index: $i, offset: ". $FX->offset . " param $param, value $val"); $this_engine->current_chain_operator($i); $this_engine->current_chain_operator_parameter($param); $this_engine->ecasound_iam("copp-set $val"); } } # set both Nama effect and Ecasound chain operator # parameters sub update_effect { my ($id, $param, $val) = @_; return if ! defined fxn($id); fxn($id)->params->[$param] = $val; update_ecasound_effect( @_ ); } sub sync_effect_parameters { logsub('&sync_effect_parameters'); local $config->{category} = 'ECI_FX'; # when a controller changes an effect parameter, the # parameter value can differ from Nama's value for that # parameter. # # this routine syncs them in prep for save_state() return unless $this_engine->valid_setup(); push my @ops, ops_with_controller(), ops_with_read_only_params(); return unless @ops; my $old_chain = $this_engine->current_chain; map{ $_->sync_one_effect } grep{ $_ } map{ fxn($_) } @ops; $this_engine->current_chain($old_chain); } sub get_ecasound_cop_params { local $config->{category} = 'ECI_FX'; my $count = shift; my @params; for (1..$count){ $this_engine->current_chain_operator_parameter($_); push @params, ecasound_iam("copp-get"); } \@params } sub ops_with_controller { grep{ ! $_->is_controller } grep{ scalar @{$_->owns} } map{ fxn($_) } map{ @{ $_->ops } } Audio::Nama::ChainSetup::engine_tracks(); } sub ops_with_read_only_params { grep{ $_->has_read_only_param() } map{ fxn($_) } map{ @{ $_->ops } } Audio::Nama::ChainSetup::engine_tracks(); } sub find_op_offsets { local $config->{category} = 'ECI_FX'; logsub("&find_op_offsets"); my @op_offsets = grep{ /"\d+"/} split "\n",ecasound_iam("cs"); logpkg(__FILE__,__LINE__,'debug', join "\n\n",@op_offsets); for my $output (@op_offsets){ my $chain_id; ($chain_id) = $output =~ m/Chain "(\w*\d+)"/; # "print chain_id: $chain_id\n"; next if $chain_id =~ m/\D/; # skip id's containing non-digits # i.e. M1 my $quotes = $output =~ tr/"//; logpkg(__FILE__,__LINE__,'debug', "offset: $quotes in $output"); $fx->{offset}->{$chain_id} = $quotes/2 - 1; } } sub expanded_ops_list { # including controllers # we assume existing ops my @ops_list = @_; return () unless @_; my @expanded = (); map { push @expanded, $_, expanded_ops_list( reverse @{fxn($_)->owns} ); # we reverse controllers listing so # the first controller is applied last # the insert operation places it adjacent to # its parent controller # as a result, the controllers end up # in the same order as the original # # which is convenient for RCS } @ops_list; my %seen; @expanded = grep { ! $seen{$_}++ } @expanded; } sub intersect_with_track_ops_list { my ($track, @effects) = @_; my %ops; map{ $ops{$_}++} @{$track->ops}; my @intersection = grep { $ops{$_} } @effects; my @outersection = grep { !$ops{$_} } @effects; carp "@outersection: effects don't belong to track: ", $track->name, ". skipping." if @outersection; @intersection } sub bypass_effects { my($track, @ops) = @_; set_bypass_state($track, 'on', @ops); } sub restore_effects { my($track, @ops) = @_; set_bypass_state($track, 'off', @ops); } sub set_bypass_state { local $config->{category} = 'ECI_FX'; my($track, $bypass_state, @ops) = @_; logsub('&set_bypass_state'); # only process ops that belong to this track @ops = intersect_with_track_ops_list($track,@ops); $this_engine->current_chain($track->n); $track->mute; foreach my $op ( @ops) { my $FX = fxn($op); my $i = $FX->ecasound_effect_index; $this_engine->current_chain_operator($i); $this_engine->ecasound_iam("cop-bypass $bypass_state"); $FX->set(bypassed => ($bypass_state eq 'on') ? 1 : 0); } $track->unmute; } sub remove_fader_effect { my ($track, $role) = @_; remove_effect($track->$role); delete $track->{$role} } # Object interface for effects sub fxn { my $id = shift; $by_id{$id}; } sub set_current_op { my $op_id = shift; my $FX = fxn($op_id); return unless $FX; $project->{current_op}->{$FX->trackname} = $op_id; } sub set_current_param { my $parameter = shift; $project->{current_param}->{Audio::Nama::this_op()} = $parameter; } sub set_current_stepsize { my $stepsize = shift; $project->{current_stepsize}->{Audio::Nama::this_op()}->[this_param()] = $stepsize; } sub increment_param { modify_effect(Audio::Nama::this_op(), this_param(),'+',this_stepsize())} sub decrement_param { modify_effect(Audio::Nama::this_op(), this_param(),'-',this_stepsize())} sub set_parameter_value { my $value = shift; modify_effect(Audio::Nama::this_op(), this_param(), undef, $value) } sub check_fx_consistency { my $result = {}; my %seen_ids; my $is_error; map { my $track = $_; my $name = $track->name; my @ops = @{ $track->{ops} }; my $is_track_error; # check for missing special-purpose ops my $no_vol_op = ! $track->vol; my $no_pan_op = ! $track->pan; my $no_latency_op = ! $track->latency_op; # check for orphan special-purpose op entries $is_track_error++, $result->{track}->{$name}->{orphan_vol} = $track->vol if $track->vol and ! grep { $track->vol eq $_ } @ops; $is_track_error++,$result->{track}->{$name}->{orphan_pan} = $track->pan if $track->pan and ! grep { $track->pan eq $_ } @ops; # we don't check for orphan latency ops as this is # allowed in order to keep constant $op_id over # time (slower incrementing of fx counter) #$is_track_error++,$result->{track}->{$name}->{orphan_latency_op} = $track->latency_op # if $track->latency_op and ! grep { $track->latency_op eq $_ } @ops; # check for undefined op ids my @track_undef_op_pos; my $i = 0; map { defined $_ or push @track_undef_op_pos, $i; $i++ } @ops; $is_track_error++,$result->{track}->{$name}->{undef_op_pos} = \@track_undef_op_pos if @track_undef_op_pos; # remove undefined op ids from list @ops = grep{ $_ } @ops; # check for op ids without corresponding entry my @uninstantiated_op_ids; map { fxn($_) or push @uninstantiated_op_ids, $_ } @ops; $is_track_error++, $result->{track}->{$name}->{uninstantiated_op_ids} = \@uninstantiated_op_ids if @uninstantiated_op_ids; $result->{track}->{$name}->{is_error}++ if $is_track_error; $result->{is_error}++ if $is_track_error; } Audio::Nama::audio_tracks(); # check for objects missing fields my @incomplete_entries = grep { ! fxn($_)->params or ! fxn($_)->type or ! fxn($_)->chain } grep { $_ } keys %Audio::Nama::Effect::by_id; if(@incomplete_entries) { $result->{incomplete_entries} = \@incomplete_entries; $result->{is_error}++ } $result; } sub fade { my $self = shift; # parameter starts at one my ($param, $from, $to, $seconds) = @_; my $id = $self->id; # no fade without Time::HiRes # no fade unless engine is running if ( $this_engine->started() and $config->{hires_timer} ) { my $steps = $seconds * $config->{fade_resolution}; my $wink = 1/$config->{fade_resolution}; my $size = ($to - $from)/$steps; logpkg(__FILE__,__LINE__,'debug', "id: $id, param: $param, from: $from, to: $to, seconds: $seconds"); # first step by step for (1..$steps - 1){ $self->_modify_effect($param, $size, '+'); sleeper( $wink ); } } $self->_modify_effect($param, $to) } sub plan_fade { return unless $this_engine->started(); my $self = shift; my %args = @_; my $param = $args{param} || 1; my $from = $args{from} || 0; my $to = $args{to} || 0; my $seconds = $args{seconds} || 1; my $in_future = $args{in_future}; my $steps = $seconds * $config->{fade_resolution}; my $wink = 1/$config->{fade_resolution}; my $id = $self->id; my $size = ($to - $from)/$steps; logpkg(__FILE__,__LINE__,'debug', "id: $id, param: $param, from: $from, to: $to, seconds: $seconds"); if ( not $in_future ){ for (1..$steps - 1){ $self->_modify_effect($param, $size, '+'); sleeper( $wink ); } $self->_modify_effect($param, $to) } else { my $advance = $in_future; my $coderef = sub { $self->_modify_effect($param, $size, '+') }; for (1..$steps - 1){ $advance += $wink; schedule_fade($advance, $coderef) } $advance += $wink; schedule_fade($advance, sub { $self->_modify_effect($param, $to) } ); sub schedule_fade { my ($after, $sub) = @_; $project->{events}->{"fade_".$setup->{fade_counter}++} = AE::timer( $after, 0, $sub ); } } } sub fadein { my $self = shift; my $to = shift; my $from = $config->{fade_out_level}->{$self->type}; $self->_modify_effect(1, $from); $self->fade(1, $from, $to, $config->{engine_fade_length_on_start_stop}); } sub fadeout { my $self = shift; my $from = $self->params->[0]; my $to = $config->{fade_out_level}->{$self->type}; $self->fade(1, $from, $to, $config->{engine_fade_length_on_start_stop} ); $self->_modify_effect(1, $config->{mute_level}->{$self->type}); } sub mute_level { my $self = shift; my $level = $config->{mute_level}->{$self->type}; #defined $level or die $self->nameline . " cannot be muted." $level } sub fade_out_level { my $self = shift; $config->{fade_out_level}->{$self->type} } sub ecasound_format { my $self = shift; my $cmd = '-'.$self->code; $cmd .= ':'.join ',' ,@{$self->{params}} if $self->{params} and @{$self->{params}} > 0; $cmd } } # end package Effect 1Audio-Nama-1.216/lib/Audio/Nama/Graphical.pm0000644000175000017500000012167013544212613017417 0ustar jrothjroth# ------------ Graphical User Interface ------------ package Audio::Nama::Graphical; ## gui routines use Modern::Perl; use Carp; our $VERSION = 1.071; use Audio::Nama::Globals qw($text $prompt); use Module::Load::Conditional qw(can_load); use Audio::Nama::Assign qw(:all); use Audio::Nama::Util qw(colonize); no warnings 'uninitialized'; our @ISA = 'Audio::Nama'; ## default to root namespace, e.g. Refresh_subs, Graphical_subs # actually this doesn't seem like a # good idea # widgets ## The following methods belong to the Graphical interface class sub hello {"make a window";} sub loop { $text->{term_attribs}->{already_prompted} = 0; $text->{term}->tkRunning(1); while (1) { my ($user_input) = $text->{term}->readline($prompt) ; Audio::Nama::process_line( $user_input ); } } sub initialize_tk { my $result1 = can_load( modules => { Tk => undef } ) ; my $result2 = can_load( modules => { 'Tk::PNG' => undef } ); $result1 } # the following graphical methods are placed in the root namespace # allowing access to root namespace variables # with a package path package Audio::Nama; # gui handling # in the $gui variable, keys with leading _underscore # indicate variables # # $gui->{_project_name} # scalar/array/hash var # $gui->{mw} # Tk objects (widgets, frames, etc.) sub init_gui { logsub("&init_gui"); init_palettefields(); # keys only ### Tk root window # Tk main window $gui->{mw} = MainWindow->new; get_saved_colors(); $gui->{mw}->optionAdd('*font', 'Helvetica 12'); $gui->{mw}->optionAdd('*BorderWidth' => 1); $gui->{mw}->title("Ecasound/Nama"); $gui->{mw}->deiconify; ### init effect window $gui->{ew} = $gui->{mw}->Toplevel; $gui->{ew}->title("Effect Window"); $gui->{ew}->deiconify; # $gui->{ew}->withdraw; ### init waveform window if ($config->{display_waveform}) { $gui->{ww} = $gui->{mw}->Toplevel; $gui->{ww}->title("Waveform Window"); $gui->{ww}->deiconify; $gui->{ww}->bind('' => sub { exit } ); $gui->{ww}->bind('' => \&toggle_transport); } ### Exit via Ctrl-C $gui->{mw}->bind('' => sub { exit } ); $gui->{ew}->bind('' => sub { exit } ); ## Press SPACE to start/stop transport $gui->{mw}->bind('' => \&toggle_transport); $gui->{ew}->bind('' => \&toggle_transport); if ($config->{display_waveform}) { $gui->{wwcanvas} = $gui->{ww}->Scrolled('Canvas')->pack; configure_waveform_window(); } $gui->{fx_canvas} = $gui->{ew}->Scrolled('Canvas')->pack; configure_effects_window(); $gui->{fx_frame} = $gui->{fx_canvas}->Frame; my $id = $gui->{fx_canvas}->createWindow(30,30, -window => $gui->{fx_frame}, -anchor => 'nw'); $gui->{project_head} = $gui->{mw}->Label->pack(-fill => 'both'); $gui->{time_frame} = $gui->{mw}->Frame( # -borderwidth => 20, # -relief => 'groove', )->pack( -side => 'bottom', -fill => 'both', ); $gui->{mark_frame} = $gui->{time_frame}->Frame->pack( -side => 'bottom', -fill => 'both'); $gui->{seek_frame} = $gui->{time_frame}->Frame->pack( -side => 'bottom', -fill => 'both'); $gui->{transport_frame} = $gui->{mw}->Frame->pack(-side => 'bottom', -fill => 'both'); # $oid_frame = $gui->{mw}->Frame->pack(-side => 'bottom', -fill => 'both'); $gui->{clock_frame} = $gui->{mw}->Frame->pack(-side => 'bottom', -fill => 'both'); #$gui->{group_frame} = $gui->{mw}->Frame->pack(-side => 'bottom', -fill => 'both'); $gui->{track_canvas}= $gui->{mw}->Scrolled('Canvas')->pack(-side => 'bottom', -fill => 'both'); configure_track_canvas(); $gui->{track_frame} = $gui->{track_canvas}->Frame; # ->pack(-fill => 'both'); #$gui->{track_frame} = $gui->{mw}->Frame; my $id2 = $gui->{track_canvas}->createWindow(0,0, -window => $gui->{track_frame}, -anchor => 'nw'); #$gui->{group_label} = $gui->{group_frame}->Menubutton(-text => "GROUP", # -tearoff => 0, # -width => 13)->pack(-side => 'left'); $gui->{add_frame} = $gui->{mw}->Frame->pack(-side => 'bottom', -fill => 'both'); $gui->{perl_frame} = $gui->{mw}->Frame->pack(-side => 'bottom', -fill => 'both'); $gui->{iam_frame} = $gui->{mw}->Frame->pack(-side => 'bottom', -fill => 'both'); $gui->{load_frame} = $gui->{mw}->Frame->pack(-side => 'bottom', -fill => 'both'); # my $blank = $gui->{mw}->Label->pack(-side => 'left'); $gui->{project_label} = $gui->{load_frame}->Label( -text => " Project name: " )->pack(-side => 'left'); $gui->{project_entry} = $gui->{load_frame}->Entry( -textvariable => \$gui->{_project_name}, -width => 25 )->pack(-side => 'left'); $gui->{load_project} = $gui->{load_frame}->Button->pack(-side => 'left');; $gui->{new_project} = $gui->{load_frame}->Button->pack(-side => 'left');; $gui->{quit} = $gui->{load_frame}->Button->pack(-side => 'left'); $gui->{save_project} = $gui->{load_frame}->Button->pack(-side => 'left'); $gui->{savefile_entry} = $gui->{load_frame}->Entry( -textvariable => \$gui->{_save_id}, -width => 15 )->pack(-side => 'left'); $gui->{load_savefile} = $gui->{load_frame}->Button->pack(-side => 'left'); $gui->{palette} = $gui->{load_frame}->Menubutton(-tearoff => 0) ->pack( -side => 'left'); $gui->{nama_palette} = $gui->{load_frame}->Menubutton(-tearoff => 0) ->pack( -side => 'left'); $gui->{add_track}->{label} = $gui->{add_frame}->Label( -text => "New track name: ")->pack(-side => 'left'); $gui->{add_track}->{text_entry} = $gui->{add_frame}->Entry( -textvariable => \$gui->{_track_name}, -width => 12 )->pack(-side => 'left'); $gui->{add_track}->{rec_label} = $gui->{add_frame}->Label( -text => "Input channel or client:" )->pack(-side => 'left'); $gui->{add_track}->{rec_text} = $gui->{add_frame}->Entry( -textvariable => \$gui->{_chr}, -width => 10 )->pack(-side => 'left'); $gui->{add_track}->{add_mono} = $gui->{add_frame}->Button->pack(-side => 'left');; $gui->{add_track}->{add_stereo} = $gui->{add_frame}->Button->pack(-side => 'left');; $gui->{load_project}->configure( -text => 'Load', -command => sub{ load_project( name => remove_spaces($gui->{_project_name}), )}); $gui->{new_project}->configure( -text => 'Create', -command => sub{ load_project( name => remove_spaces($gui->{_project_name}), create => 1)}); $gui->{save_project}->configure( -text => 'Save settings', -command => #sub { print "save_id: $gui->{_save_id}\n" }); sub {save_state($gui->{_save_id}) }); $gui->{load_savefile}->configure( -text => 'Recall settings', -command => sub {load_project (name => $project->{name}, # current project settings => $gui->{_save_id})}, ); $gui->{quit}->configure(-text => "Quit", -command => sub { stop_transport() if $this_engine->started; save_state($gui->{_save_id}); pager("Exiting... \n"); #$text->{term}->tkRunning(0); #$gui->{ew}->destroy; #$gui->{mw}->destroy; #Audio::Nama::nama_cmd('quit'); exit; }); $gui->{palette}->configure( -text => 'Palette', -relief => 'raised', ); $gui->{nama_palette}->configure( -text => 'Nama palette', -relief => 'raised', ); my @color_items = map { [ 'command' => $_, -command => colorset('mw', $_ ) ] } @{$gui->{_palette_fields}}; $gui->{palette}->AddItems( @color_items); @color_items = map { [ 'command' => $_, -command => namaset( $_ ) ] } @{$gui->{_nama_fields}}; $gui->{add_track}->{add_mono}->configure( -text => 'Add Mono Track', -command => sub { return if $gui->{_track_name} =~ /^\s*$/; add_track(remove_spaces($gui->{_track_name})) } ); $gui->{add_track}->{add_stereo}->configure( -text => 'Add Stereo Track', -command => sub { return if $gui->{_track_name} =~ /^\s*$/; add_track(remove_spaces($gui->{_track_name})); nama_cmd('stereo'); }); my @labels = qw(Track Name Version Status Source Send Volume Mute Unity Pan Center Effects); my @widgets; map{ push @widgets, $gui->{track_frame}->Label(-text => $_) } @labels; $widgets[0]->grid(@widgets[1..$#widgets]); } sub configure_waveform_window { my ($width, $height) = @_; return if not $config->{display_waveform}; $gui->{wwcanvas}->configure( -scrollregion =>[0,0,$width//$config->{waveform_canvas_x},$height//$config->{waveform_canvas_y}], -width => $width//$config->{waveform_canvas_x}, -height => $height//$config->{waveform_canvas_y}, ); } sub configure_effects_window { $gui->{fx_canvas}->configure( scrollregion =>[2,2,10000,10000], -width => 1200, -height => 700, ); } sub configure_track_canvas { $gui->{track_canvas}->configure( -scrollregion =>[2,2,400,9600], -width => 400, -height => 400, ); } sub wwgeometry { wh($gui->{wwcanvas}) } sub wh { my $widget = shift; $widget->update; my ($width,$height,$sign1,$xpos,$sign2,$ypos) = $widget->geometry =~ /(\d+)x(\d+)([+-])(\d+)([+-])(\d+)/; $width,$height } sub transport_gui { my $ui = shift; logsub("&transport_gui"); $gui->{engine_label} = $gui->{transport_frame}->Label( -text => 'TRANSPORT', -width => 12, )->pack(-side => 'left');; $gui->{engine_start} = $gui->{transport_frame}->Button->pack(-side => 'left'); $gui->{engine_stop} = $gui->{transport_frame}->Button->pack(-side => 'left'); $gui->{engine_stop}->configure(-text => "Stop", -command => sub { stop_transport(); } ); $gui->{engine_start}->configure( -text => "Start", -command => sub { return if $this_engine->started; my $color = engine_mode_color(); $ui->project_label_configure(-background => $color); start_transport(); }); #preview_button(); #mastering_button(); } sub time_gui { my $ui = shift; logsub("&time_gui"); my $time_label = $gui->{clock_frame}->Label( -text => 'TIME', -width => 12); #print "bg: $gui->{_nama_palette}->{ClockBackground}, fg:$gui->{_nama_palette}->{ClockForeground}\n"; $gui->{clock} = $gui->{clock_frame}->Label( -text => '0:00', -width => 8, -background => $gui->{_nama_palette}->{ClockBackground}, -foreground => $gui->{_nama_palette}->{ClockForeground}, ); my $length_label = $gui->{clock_frame}->Label( -text => 'LENGTH', -width => 10, ); $gui->{setup_length} = $gui->{clock_frame}->Label( # -width => 8, ); for my $w ($time_label, $gui->{clock}, $length_label, $gui->{setup_length}) { $w->pack(-side => 'left'); } $gui->{mark_frame} = $gui->{time_frame}->Frame->pack( -side => 'bottom', -fill => 'both'); $gui->{seek_frame} = $gui->{time_frame}->Frame->pack( -side => 'bottom', -fill => 'both'); # jump my $jump_label = $gui->{seek_frame}->Label(-text => q(JUMP), -width => 12); my @pluses = (1, 5, 10, 30, 60); my @minuses = map{ - $_ } reverse @pluses; my @fw = map{ my $d = $_; $gui->{seek_frame}->Button( -text => $d, -command => sub { jump($d) }, ) } @pluses ; my @rew = map{ my $d = $_; $gui->{seek_frame}->Button( -text => $d, -command => sub { jump($d) }, ) } @minuses ; my $beg = $gui->{seek_frame}->Button( -text => 'Beg', -command => \&to_start, ); my $end = $gui->{seek_frame}->Button( -text => 'End', -command => \&to_end, ); $gui->{seek_unit} = $gui->{seek_frame}->Button( -text => 'Sec', ); for my $w($jump_label, @rew, $beg, $gui->{seek_unit}, $end, @fw){ $w->pack(-side => 'left') } $gui->{seek_unit}->configure (-command => sub { &toggle_unit; &show_unit }); # Marks my $mark_label = $gui->{mark_frame}->Label( -text => q(MARK), -width => 12, )->pack(-side => 'left'); my $drop_mark = $gui->{mark_frame}->Button( -text => 'Place', -command => \&drop_mark, )->pack(-side => 'left'); $gui->{mark_remove} = $gui->{mark_frame}->Button( -text => 'Remove', -command => \&arm_mark_toggle, )->pack(-side => 'left'); } sub toggle_unit { if ($gui->{_seek_unit} == 1){ $gui->{_seek_unit} = 60; } else{ $gui->{_seek_unit} = 1; } } sub show_unit { $gui->{seek_unit}->configure( -text => ($gui->{_seek_unit} == 1 ? 'Sec' : 'Min') )} sub paint_button { my $ui = shift; my ($button, $color) = @_; $button->configure(-background => $color, -activebackground => $color); } sub engine_mode_color { if ( user_rec_tracks() ){ $gui->{_nama_palette}->{RecBackground} # live recording } elsif ( Audio::Nama::ChainSetup::really_recording() ){ $gui->{_nama_palette}->{Mixdown} # mixdown only } elsif ( user_play_tracks() ){ $gui->{_nama_palette}->{Play}; # just playback } else { $gui->{_old_bg} } } sub user_rec_tracks { some_user_tracks(REC) } sub user_play_tracks { some_user_tracks(PLAY) } sub some_user_tracks { my $which = shift; my @user_tracks = Audio::Nama::audio_tracks(); splice @user_tracks, 0, 2; # drop Main and Mixdown tracks return unless @user_tracks; my @selected_user_tracks = grep { $_->rec_status eq $which } @user_tracks; return unless @selected_user_tracks; map{ $_->n } @selected_user_tracks; } sub flash_ready { my $color = engine_mode_color(); logpkg(__FILE__,__LINE__,'debug', "flash color: $color"); $ui->length_display(-background => $color); $ui->project_label_configure(-background => $color) unless $mode->{preview}; # TODO update for preview mode $project->{events}->{heartbeat} = AE::timer(5, 0, \&reset_engine_mode_color_display); } sub reset_engine_mode_color_display { $ui->project_label_configure( -background => $gui->{_nama_palette}->{OffBackground} ) } sub set_engine_mode_color_display { $ui->project_label_configure(-background => engine_mode_color()) } sub group_gui { my $ui = shift; my $group = $bn{Main}; my $dummy = $gui->{track_frame}->Label(-text => ' '); $gui->{group_label} = $gui->{track_frame}->Label( -text => "G R O U P", -foreground => $gui->{_nama_palette}->{GroupForeground}, -background => $gui->{_nama_palette}->{GroupBackground}, ); $gui->{group_version} = $gui->{track_frame}->Menubutton( -text => q( ), -tearoff => 0, -foreground => $gui->{_nama_palette}->{GroupForeground}, -background => $gui->{_nama_palette}->{GroupBackground}, ); $gui->{group_rw} = $gui->{track_frame}->Menubutton( -text => $group->rw, -tearoff => 0, -foreground => $gui->{_nama_palette}->{GroupForeground}, -background => $gui->{_nama_palette}->{GroupBackground}, ); $gui->{group_rw}->AddItems([ 'command' => MON, -background => $gui->{_old_bg}, -command => sub { return if $this_engine->started(); $group->set(rw => MON); $gui->{group_rw}->configure(-text => MON); refresh(); Audio::Nama::reconfigure_engine() } ],[ 'command' => OFF, -background => $gui->{_old_bg}, -command => sub { return if $this_engine->started(); $group->set(rw => OFF); $gui->{group_rw}->configure(-text => OFF); refresh(); Audio::Nama::reconfigure_engine() } ]); $dummy->grid($gui->{group_label}, $gui->{group_version}, $gui->{group_rw}); #$ui->global_version_buttons; } sub global_version_buttons { my $version = $gui->{group_version}; $version and map { $_->destroy } $version->children; logpkg(__FILE__,__LINE__,'debug', "making global version buttons range: " , join ' ',1..$bn{Main}->last); $version->radiobutton( -label => (''), -value => 0, -command => sub { $bn{Main}->set(version => 0); $version->configure(-text => " "); Audio::Nama::reconfigure_engine(); refresh(); } ); for my $v (1..$bn{Main}->last) { # the highest version number of all tracks in the # $bn{Main} group my @user_track_indices = grep { $_ > 2 } map {$_->n} Audio::Nama::audio_tracks(); next unless grep{ grep{ $v == $_ } @{ $ti{$_}->versions } } @user_track_indices; $version->radiobutton( -label => ($v ? $v : ''), -value => $v, -command => sub { $bn{Main}->set(version => $v); $version->configure(-text => $v); Audio::Nama::reconfigure_engine(); refresh(); } ); } } sub track_gui { logsub("&track_gui"); my $ui = shift; my $n = shift; pager("track_gui already generated"), return if defined $gui->{tracks}->{$n} ; return if $ti{$n}->hide; logpkg(__FILE__,__LINE__,'debug', "found index: $n"); my @rw_items = @_ ? @_ : ( [ 'command' => "REC", -foreground => 'red', -command => sub { return if $this_engine->started(); $ti{$n}->set(rw => "REC"); $ui->refresh_track($n); #refresh_group(); Audio::Nama::reconfigure_engine(); }], [ 'command' => "PLAY", -command => sub { return if $this_engine->started(); $ti{$n}->set(rw => "PLAY"); $ui->refresh_track($n); #refresh_group(); Audio::Nama::reconfigure_engine(); }], [ 'command' => "MON", -foreground => 'red', -command => sub { return if $this_engine->started(); $ti{$n}->set(rw => "MON"); $ui->refresh_track($n); #refresh_group(); Audio::Nama::reconfigure_engine(); }], [ 'command' => "OFF", -command => sub { return if $this_engine->started(); $ti{$n}->set(rw => "OFF"); $ui->refresh_track($n); #refresh_group(); Audio::Nama::reconfigure_engine(); }], ); my ($number, $name, $version, $rw, $ch_r, $ch_m, $vol, $mute, $solo, $unity, $pan, $center); $number = $gui->{track_frame}->Label(-text => $n, -justify => 'left'); my $stub = " "; $stub .= $ti{$n}->version; $name = $gui->{track_frame}->Label( -text => $ti{$n}->name, -justify => 'left'); $version = $gui->{track_frame}->Menubutton( -text => $stub, # -relief => 'sunken', -tearoff => 0); my @versions = ''; #push @versions, @{$ti{$n}->versions} if @{$ti{$n}->versions}; my $ref = ref $ti{$n}->versions ; $ref =~ /ARRAY/ and push (@versions, @{$ti{$n}->versions}) or croak "chain $n, found unexpectedly $ref\n";; my $indicator; for my $v (@versions) { $version->radiobutton( -label => $v, -value => $v, -variable => \$indicator, -command => sub { $ti{$n}->set( version => $v ); return if $ti{$n}->rec; $version->configure( -text=> $ti{$n}->current_version ); Audio::Nama::reconfigure_engine(); } ); } $ch_r = $gui->{track_frame}->Menubutton( # -relief => 'groove', -tearoff => 0, ); my @range; push @range, 1..$config->{soundcard_channels} if $n > 2; # exclude Main/Mixdown for my $v (@range) { $ch_r->radiobutton( -label => $v, -value => $v, -command => sub { return if $this_engine->started(); # $ti{$n}->set(rw => REC); $ti{$n}->source($v); $ui->refresh_track($n) } ) } @range = (); push @range, "off" if $n > 2; push @range, 1..$config->{soundcard_channels} if $n != 2; # exclude Mixdown $ch_m = $gui->{track_frame}->Menubutton( -tearoff => 0, # -relief => 'groove', ); for my $v (@range) { $ch_m->radiobutton( -label => $v, -value => $v, -command => sub { return if $this_engine->started(); local $this_track = $ti{$n}; if( $v eq 'off' ) { nama_cmd('nosend') } else { $this_track->set_send($v) } $ui->refresh_track($n); Audio::Nama::reconfigure_engine(); } ) } $rw = $gui->{track_frame}->Menubutton( -text => $ti{$n}->rw, -tearoff => 0, # -relief => 'groove', ); map{$rw->AddItems($_)} @rw_items; my $p_num = 0; # needed when using parameter controllers # Volume if ( Audio::Nama::need_vol_pan($ti{$n}->name, "vol") ){ my $vol_id = $ti{$n}->vol; logpkg(__FILE__,__LINE__,'debug', "vol effect_id: $vol_id"); my %p = ( parent => \$gui->{track_frame}, chain => $n, type => 'ea', id => $vol_id, p_num => $p_num, length => 300, ); logpkg(__FILE__,__LINE__,'debug',sub{my %q = %p; delete $q{parent}; print "=============\n%p\n",json_out(\%q)}); $vol = make_scale ( \%p ); # Mute $mute = $gui->{track_frame}->Button( -command => sub { my $FX = fxn($vol_id); if ( $FX->params->[0] != $config->{mute_level}->{$FX->type} and $FX->params->[0] != $config->{fade_out_level}->{$FX->type} ) { # non-zero volume $ti{$n}->mute; $mute->configure(-background => $gui->{_nama_palette}->{Mute}); } else { $ti{$n}->unmute; $mute->configure(-background => $gui->{_nama_palette}->{OffBackground}) } } ); # Unity $unity = $gui->{track_frame}->Button( -command => sub { my $FX = fxn($vol_id); Audio::Nama::update_effect( $vol_id, 0, $config->{unity_level}->{$FX->type}); } ); } else { $vol = $gui->{track_frame}->Label; $mute = $gui->{track_frame}->Label; $unity = $gui->{track_frame}->Label; } if ( Audio::Nama::need_vol_pan($ti{$n}->name, "pan") ){ # Pan my $pan_id = $ti{$n}->pan; logpkg(__FILE__,__LINE__,'debug', "pan effect_id: $pan_id"); $p_num = 0; # first parameter my %q = ( parent => \$gui->{track_frame}, chain => $n, type => 'epp', id => $pan_id, p_num => $p_num, ); # logpkg(__FILE__,__LINE__,'debug',sub{ my %q = %p; delete $q{parent}; print "x=============\n%p\n",json_out(\%q) }); $pan = make_scale ( \%q ); # Center $center = $gui->{track_frame}->Button( -command => sub { Audio::Nama::update_effect($pan_id, 0, 50); } ); } else { $pan = $gui->{track_frame}->Label; $center = $gui->{track_frame}->Label; } my $effects = $gui->{fx_frame}->Frame->pack(-fill => 'both');; # effects, held by track_widget->n->effects is the frame for # all effects of the track @{ $gui->{tracks}->{$n} }{qw(name version rw ch_r ch_m mute effects)} = ($name, $version, $rw, $ch_r, $ch_m, $mute, \$effects);#a ref to the object #logpkg(__FILE__,__LINE__,'debug', "=============$gui->{tracks}\n",sub{json_out($gui->{tracks})}); my $independent_effects_frame = ${ $gui->{tracks}->{$n}->{effects} }->Frame->pack(-fill => 'x'); my $controllers_frame = ${ $gui->{tracks}->{$n}->{effects} }->Frame->pack(-fill => 'x'); # parents are the independent effects # children are controllers for various paramters $gui->{tracks}->{$n}->{parents} = $independent_effects_frame; $gui->{tracks}->{$n}->{children} = $controllers_frame; $independent_effects_frame ->Label(-text => uc $ti{$n}->name )->pack(-side => 'left'); #logpkg(__FILE__,__LINE__,'debug',"Number: $n"),MainLoop if $n == 2; my @tags = qw( EF P1 P2 L1 L2 L3 L4 ); my @starts = ( $fx_cache->{split}->{cop}{a}, $fx_cache->{split}->{preset}{a}, $fx_cache->{split}->{preset}{b}, $fx_cache->{split}->{ladspa}{a}, $fx_cache->{split}->{ladspa}{b}, $fx_cache->{split}->{ladspa}{c}, $fx_cache->{split}->{ladspa}{d}, ); my @ends = ( $fx_cache->{split}->{cop}{z}, $fx_cache->{split}->{preset}{b}, $fx_cache->{split}->{preset}{z}, $fx_cache->{split}->{ladspa}{b}-1, $fx_cache->{split}->{ladspa}{c}-1, $fx_cache->{split}->{ladspa}{d}-1, $fx_cache->{split}->{ladspa}{z}, ); my @add_effect; map{push @add_effect, effect_button($n, shift @tags, shift @starts, shift @ends)} 1..@tags; $number->grid($name, $version, $rw, $ch_r, $ch_m, $vol, $mute, $unity, $pan, $center, @add_effect); $gui->{tracks_remove}->{$n} = [ grep{ $_ } ( $number, $name, $version, $rw, $ch_r, $ch_m, $vol, $mute, $unity, $pan, $center, @add_effect, $effects, ) ]; $ui->refresh_track($n); } sub remove_track_gui { my $ui = shift; my $n = shift; logsub("&remove_track_gui"); return unless $gui->{tracks_remove}->{$n}; map {$_->destroy } @{ $gui->{tracks_remove}->{$n} }; delete $gui->{tracks_remove}->{$n}; delete $gui->{tracks}->{$n}; } sub paint_mute_buttons { map{ $gui->{tracks}->{$_}{mute}->configure( -background => $gui->{_nama_palette}->{Mute}, )} grep { $ti{$_}->old_vol_level}# muted tracks map { $_->n } Audio::Nama::audio_tracks(); # track numbers } sub create_master_and_mix_tracks { logsub("&create_master_and_mix_tracks"); my @rw_items = ( [ 'command' => "MON", -command => sub { return if $this_engine->started(); $tn{Main}->set(rw => "MON"); $ui->refresh_track($tn{Main}->n); }], [ 'command' => "OFF", -command => sub { return if $this_engine->started(); $tn{Main}->set(rw => "OFF"); $ui->refresh_track($tn{Main}->n); }], ); $ui->track_gui( $tn{Main}->n, @rw_items ); $ui->track_gui( $tn{Mixdown}->n); #$ui->group_gui('Main'); } sub update_version_button { my $ui = shift; my ($n, $v) = @_; carp ("no version provided \n") if ! $v; my $w = $gui->{tracks}->{$n}->{version}; $w->radiobutton( -label => $v, -value => $v, -command => sub { $gui->{tracks}->{$n}->{version}->configure(-text=>$v) unless $ti{$n}->rec } ); } sub add_effect_gui { logsub("&add_effect_gui"); my $ui = shift; my %p = %{shift()}; my ($n,$code,$id,$parent,$parameter) = @p{qw(chain type id parent parameter)}; my $i = $fx_cache->{full_label_to_index}->{$code}; my $FX = fxn($id); logpkg(__FILE__,__LINE__,'debug', sub{json_out(\%p)}); logpkg(__FILE__,__LINE__,'debug', "id: $id, parent: $parent"); # $id is determined by effect_init, which will return the # existing id if supplied # check display format, may be 'scale' 'field' or 'hidden' my $display_type = $FX->display; # individual setting defined $display_type or $display_type = $fx_cache->{registry}->[$i]->{display}; # template logpkg(__FILE__,__LINE__,'debug', "display type: $display_type"); return if $display_type eq q(hidden); my $frame ; if ( ! $parent ){ # independent effect $frame = $gui->{tracks}->{$n}->{parents}->Frame->pack( -side => 'left', -anchor => 'nw',) } else { # controller $frame = $gui->{tracks}->{$n}->{children}->Frame->pack( -side => 'top', -anchor => 'nw') } $gui->{fx}->{$id} = $frame; # we need a separate frame so title can be long # here add menu items for Add Controller, and Remove my $parentage = $fx_cache->{registry}->[ $fx_cache->{full_label_to_index}->{$FX->type} ]->{name}; $parentage and $parentage .= " - "; logpkg(__FILE__,__LINE__,'debug', "parentage: $parentage"); my $eff = $frame->Menubutton( -text => $parentage. $fx_cache->{registry}->[$i]->{name}, -tearoff => 0,); $eff->AddItems([ 'command' => "Remove", -command => sub { remove_effect($id) } ]); $eff->grid(); my @labels; my @sliders; # make widgets for my $p (0..$fx_cache->{registry}->[$i]->{count} - 1 ) { my @items; #logpkg(__FILE__,__LINE__,'debug', "p_first: $p_first, p_last: $p_last"); for my $j ($fx_cache->{split}->{ctrl}{a}..$fx_cache->{split}->{ctrl}{z}) { push @items, [ 'command' => $fx_cache->{registry}->[$j]->{name}, -command => sub { add_effect({ parent => $id, chain => $n, params => [ $p + 1 ], type => $fx_cache->{registry}->[$j]->{code} }) } ]; } push @labels, $frame->Menubutton( -text => $fx_cache->{registry}->[$i]->{params}->[$p]->{name}, -menuitems => [@items], -tearoff => 0, ); logpkg(__FILE__,__LINE__,'debug', "parameter name: ", $fx_cache->{registry}->[$i]->{params}->[$p]->{name}); my $v = # for argument vector { parent => \$frame, id => $id, p_num => $p, }; push @sliders,make_scale($v); } if (@sliders) { $sliders[0]->grid(@sliders[1..$#sliders]); $labels[0]->grid(@labels[1..$#labels]); } } sub project_label_configure{ my $ui = shift; $gui->{project_head}->configure( @_ ) } sub length_display{ my $ui = shift; $gui->{setup_length}->configure(@_)}; sub clock_config { my $ui = shift; $gui->{clock}->configure( @_ )} sub manifest { $gui->{ew}->deiconify() } sub destroy_widgets { map{ $_->destroy } map{ $_->children } $gui->{fx_frame}; #my @children = $gui->{group_frame}->children; #map{ $_->destroy } @children[1..$#children]; my @children = $gui->{track_frame}->children; # leave field labels (first row) map{ $_->destroy } @children[11..$#children]; # fragile %{$gui->{marks}} and map{ $_->destroy } values %{$gui->{marks}}; } sub remove_effect_gui { my $ui = shift; logsub("&remove_effect_gui"); my $id = shift; my $FX = fxn($id); my $n = $FX->chain; logpkg(__FILE__,__LINE__,'debug', "id: $id, chain: $n"); logpkg(__FILE__,__LINE__,'debug', "i have widgets for these ids: ", join " ",keys %{$gui->{fx}}); logpkg(__FILE__,__LINE__,'debug', "preparing to destroy: $id"); return unless defined $gui->{fx}->{$id}; $gui->{fx}->{$id}->destroy(); delete $gui->{fx}->{$id}; } sub effect_button { logsub("&effect_button"); my ($n, $label, $start, $end) = @_; logpkg(__FILE__,__LINE__,'debug', "chain $n label $label start $start end $end"); my @items; my $widget; my @indices = ($start..$end); if ($start >= $fx_cache->{split}->{ladspa}{a} and $start <= $fx_cache->{split}->{ladspa}{z}){ @indices = (); @indices = @{$fx_cache->{ladspa_sorted}}[$start..$end]; logpkg(__FILE__,__LINE__,'debug', "length sorted indices list: ",scalar @indices ); logpkg(__FILE__,__LINE__,'debug', "Indices: @indices"); } for my $j (@indices) { push @items, [ 'command' => "$fx_cache->{registry}->[$j]->{count} $fx_cache->{registry}->[$j]->{name}" , -command => sub { add_effect( {chain => $n, type => $fx_cache->{registry}->[$j]->{code} } ); $gui->{ew}->deiconify; # display effects window } ]; } $widget = $gui->{track_frame}->Menubutton( -text => $label, -tearoff =>0, # -relief => 'raised', -menuitems => [@items], ); $widget; } sub make_scale { logsub("&make_scale"); my $ref = shift; my %p = %{$ref}; # %p contains following: # id => operator id # parent => parent widget, i.e. the frame # p_num => parameter number, starting at 0 # length => length widget # optional my $id = $p{id}; my $FX = fxn($id); my $n = $FX->chain; my $code = $FX->type; my $p = $p{p_num}; my $i = $fx_cache->{full_label_to_index}->{$code}; logpkg(__FILE__,__LINE__,'debug', "id: $id code: $code"); # check display format, may be text-field or hidden, logpkg(__FILE__,__LINE__,'debug',"i: $i code: $fx_cache->{registry}->[$i]->{code} display: $fx_cache->{registry}->[$i]->{display}"); my $display_type = $FX->display; defined $display_type or $display_type = $fx_cache->{registry}->[$i]->{display}; logpkg(__FILE__,__LINE__,'debug', "display type: $display_type"); return if $display_type eq q(hidden); logpkg(__FILE__,__LINE__,'debug', "to: ", $fx_cache->{registry}->[$i]->{params}->[$p]->{end}) ; logpkg(__FILE__,__LINE__,'debug', "p: $p code: $code"); logpkg(__FILE__,__LINE__,'debug', "is_log_scale: ".is_log_scale($i,$p)); # set display type to individually specified value if it exists # otherwise to the default for the controller class if ($display_type eq q(scale) ) { # return scale type controller widgets my $frame = ${ $p{parent} }->Frame; #return ${ $p{parent} }->Scale( my $log_display; my $controller = $frame->Scale( -variable => \$FX->{params}->[$p], -orient => 'horizontal', -from => $fx_cache->{registry}->[$i]->{params}->[$p]->{begin}, -to => $fx_cache->{registry}->[$i]->{params}->[$p]->{end}, -resolution => resolution($i, $p), -width => 12, -length => $p{length} ? $p{length} : 100, -command => sub { Audio::Nama::update_ecasound_effect($id, $p, $FX->params->[$p]) }, -state => $FX->is_read_only($p) ? 'disabled' : 'normal', ); # auxiliary field for logarithmic display if ( is_log_scale($i, $p) ) # or $code eq 'ea') { my $log_display = $frame->Label( -text => exp $fx_cache->{registry}->[$i]->{params}->[$p]->{default}, -width => 5, ); $controller->configure( -variable => \$FX->{params_log}->[$p], -command => sub { $FX->params->[$p] = exp $FX->params_log->[$p]; Audio::Nama::update_ecasound_effect($id, $p, $FX->params->[$p]); $log_display->configure( -text => $fx_cache->{registry}->[$i]->{params}->[$p]->{name} =~ /hz|frequency/i ? int $FX->params->[$p] : dn($FX->params->[$p], 1) ); } ); $log_display->grid($controller); } else { $controller->grid; } return $frame; } elsif ($display_type eq q(field) ){ # then return field type controller widget return ${ $p{parent} }->Entry( -textvariable =>\$FX->params->[$p], -width => 6, # -command => sub { Audio::Nama::update_ecasound_effect($id, $p, $FX->params->[$p]) }, # doesn't work with Entry widget ); } else { croak "missing or unexpected display type: $display_type" } } sub is_log_scale { my ($i, $p) = @_; $fx_cache->{registry}->[$i]->{params}->[$p]->{hint} =~ /logarithm/ } sub resolution { my ($i, $p) = @_; my $res = $fx_cache->{registry}->[$i]->{params}->[$p]->{resolution}; return $res if $res; my $end = $fx_cache->{registry}->[$i]->{params}->[$p]->{end}; my $beg = $fx_cache->{registry}->[$i]->{params}->[$p]->{begin}; return 1 if abs($end - $beg) > 30; return abs($end - $beg)/100 } sub arm_mark_toggle { if ($gui->{_markers_armed}) { $gui->{_markers_armed} = 0; $gui->{mark_remove}->configure( -background => $gui->{_nama_palette}->{OffBackground}); } else{ $gui->{_markers_armed} = 1; $gui->{mark_remove}->configure( -background => $gui->{_nama_palette}->{MarkArmed}); } } sub marker { my $ui = shift; my $mark = shift; # Mark logpkg(__FILE__,__LINE__,'debug',"mark is ", ref $mark); my $pos = $mark->time; logpkg(__FILE__,__LINE__,'debug',$pos, " ", int $pos); $gui->{marks}->{$pos} = $gui->{mark_frame}->Button( -text => (join " ", colonize( int $pos ), $mark->name), -background => $gui->{_nama_palette}->{OffBackground}, -command => sub { Audio::Nama::mark($mark) }, )->pack(-side => 'left'); } sub restore_time_marks { my $ui = shift; map{ $ui->marker($_) } Audio::Nama::Mark::all() ; $gui->{seek_unit}->configure( -text => $gui->{_seek_unit} == 1 ? q(Sec) : q(Min) ) } sub destroy_marker { my $ui = shift; my $pos = shift; $gui->{marks}->{$pos}->destroy; } sub setup_playback_indicator { my $ui = shift; $project->{events}->{update_playback_position_display} = AE::timer(0, 0.1, \&update_indicator); } sub update_indicator { $gui->{wwcanvas}->delete('playback-indicator'); my $pos = Audio::Nama::ecasound_iam("getpos"); my $xpos = int( $pos * $config->{waveform_pixels_per_second} ); $gui->{wwcanvas}->createLine( $xpos,0, $xpos,$config->{waveform_canvas_y}, -fill => 'red', -width => 1, -tags => 'playback-indicator' ); } sub get_saved_colors { logsub("&get_saved_colors"); # aliases $gui->{_old_bg} = $gui->{_palette}{mw}{background}; $gui->{_old_abg} = $gui->{_palette}{mw}{activeBackground}; $gui->{_old_bg} //= '#d915cc1bc3cf'; #print "pb: $gui->{_palette}{mw}{background}\n"; my $pal = $file->gui_palette; $pal .= '.json' unless $pal =~ /\.json$/; logpkg(__FILE__,__LINE__,'debug',"pal $pal"); $pal = -f $pal ? scalar read_file($pal) : get_data_section('default_palette_json'); my $ref = decode($pal, 'json'); #say "palette file",json_out($ref); assign_singletons({ data => $ref }); $gui->{_old_abg} = $gui->{_palette}->{mw}{activeBackground}; $gui->{_old_abg} = $gui->{project_head}->cget('-activebackground') unless $gui->{_old_abg}; #print "1palette: \n", json_out( $gui->{_palette} ); #print "\n1namapalette: \n", json_out($gui->{_nama_palette}); my %setformat; map{ $setformat{$_} = $gui->{_palette}->{mw}{$_} if $gui->{_palette}->{mw}{$_} } keys %{$gui->{_palette}->{mw}}; #print "\nsetformat: \n", json_out(\%setformat); $gui->{mw}->setPalette( %setformat ); } sub colorset { my ($widgetid, $field) = @_; sub { my $widget = $gui->{$widgetid}; #print "ancestor: $widgetid\n"; my $new_color = colorchooser($field,$widget->cget("-$field")); if( defined $new_color ){ # install color in palette listing $gui->{_palette}->{$widgetid}{$field} = $new_color; # set the color my @fields = ($field => $new_color); push (@fields, 'background', $widget->cget('-background')) unless $field eq 'background'; #print "fields: @fields\n"; $widget->setPalette( @fields ); } }; } sub namaset { my ($field) = @_; sub { #print "f: $field np: $gui->{_nama_palette}->{$field}\n"; my $color = colorchooser($field,$gui->{_nama_palette}->{$field}); if ($color){ # install color in palette listing $gui->{_nama_palette}->{$field} = $color; # set those objects who are not # handled by refresh *rec = \$gui->{_nama_palette}->{RecBackground}; *mon = \$gui->{_nama_palette}->{MonBackground}; *off = \$gui->{_nama_palette}->{OffBackground}; $gui->{clock}->configure( -background => $gui->{_nama_palette}->{ClockBackground}, -foreground => $gui->{_nama_palette}->{ClockForeground}, ); $gui->{group_label}->configure( -background => $gui->{_nama_palette}->{GroupBackground}, -foreground => $gui->{_nama_palette}->{GroupForeground}, ); refresh(); } } } sub colorchooser { logsub("&colorchooser"); my ($field, $initialcolor) = @_; logpkg(__FILE__,__LINE__,'debug', "field: $field, initial color: $initialcolor"); my $new_color = $gui->{mw}->chooseColor( -title => $field, -initialcolor => $initialcolor, ); #print "new color: $new_color\n"; $new_color; } sub init_palettefields { @{$gui->{_palette_fields}} = qw[ foreground background activeForeground activeBackground selectForeground selectBackground selectColor highlightColor highlightBackground disabledForeground insertBackground troughColor ]; @{$gui->{_nama_fields}} = qw [ RecForeground RecBackground MonForeground MonBackground OffForeground OffBackground ClockForeground ClockBackground Capture Play Mixdown GroupForeground GroupBackground SendForeground SendBackground SourceForeground SourceBackground Mute MarkArmed ]; } sub save_palette { serialize ( file => $file->gui_palette, format => 'json', vars => [ qw( $gui->{_palette} $gui->{_nama_palette} ) ], class => 'Audio::Nama') } ### end ## refresh functions sub refresh_waveform_window { $gui->{wwcanvas}->delete('waveform',$_->name) for all_tracks(); my @playable = grep{ $_->play} user_tracks(); map{ $_->waveform->display() } @playable; configure_waveform_window(); generate_timeline( widget => $gui->{wwcanvas}, y_pos => 600, ); } sub height { $_[0] % 5 ? 5 : 10 } sub generate_timeline { my %args = @_; my $length = ecasound_iam('cs-get-length'); $length = int($length + 5.5); $args{seconds} = $length; my $pps = 10; # HARDCODED for (0..$args{seconds}) { my $xpos = $_ * $pps; if ($_ % 10 == 0) { $args{widget}->createText( $xpos, $args{y_pos} - 20, -font => 'lucidasanstypewriter-bold-14', -text => $_, ); } $args{widget}->createLine( $xpos, $args{y_pos} - height($_), $xpos, $args{y_pos}, -fill => 'black', -width => 1, -tags => 'timelime' ); } } sub set_widget_color { my ($widget, $status) = @_; my %rw_foreground = ( REC => $gui->{_nama_palette}->{RecForeground}, PLAY => $gui->{_nama_palette}->{MonForeground}, MON => $gui->{_nama_palette}->{MonForeground}, OFF => $gui->{_nama_palette}->{OffForeground}, ); my %rw_background = ( REC => $gui->{_nama_palette}->{RecBackground}, PLAY => $gui->{_nama_palette}->{MonBackground}, MON => $gui->{_nama_palette}->{MonBackground}, OFF => $gui->{_nama_palette}->{OffBackground}); $widget->configure( -background => $rw_background{$status} ); $widget->configure( -foreground => $rw_foreground{$status} ); } sub refresh_group { # main group, in this case we want to skip null group logsub("&refresh_group"); my $status; if ( grep{ $_->rec} map{ $tn{$_} } $bn{Main}->tracks ){ $status = REC }elsif( grep{ $_->play} map{ $tn{$_} } $bn{Main}->tracks ){ $status = PLAY }else{ $status = OFF } logit(__LINE__,'Audio::Nama::Refresh','debug', "group status: $status"); set_widget_color($gui->{group_rw}, $status); croak "some crazy status |$status|\n" if $status !~ m/rec|mon|off/i; #logit(__LINE__,'Audio::Nama::Refresh','debug', "attempting to set $status color: ", $take_color{$status}); set_widget_color( $gui->{group_rw}, $status) if $gui->{group_rw}; } sub refresh_track { my $ui = shift; my $n = shift; logsub("&refresh_track"); my $rec_status = $ti{$n}->rec_status; logit(__LINE__,'Audio::Nama::Refresh','debug', "track: $n rec_status: $rec_status"); return unless $gui->{tracks}->{$n}; # hidden track # set the text for displayed fields $gui->{tracks}->{$n}->{rw}->configure(-text => $rec_status); $gui->{tracks}->{$n}->{ch_r}->configure( -text => $n > 2 ? $ti{$n}->source : q() ); $gui->{tracks}->{$n}->{ch_m}->configure( -text => $ti{$n}->send); $gui->{tracks}->{$n}->{version}->configure(-text => $ti{$n}->current_version || ""); map{ set_widget_color( $gui->{tracks}->{$n}->{$_}, $rec_status) } qw(name rw ); set_widget_color( $gui->{tracks}->{$n}->{ch_r}, ($rec_status eq REC and $n > 2 ) ? REC : OFF); set_widget_color( $gui->{tracks}->{$n}->{ch_m}, $rec_status eq OFF ? OFF : $ti{$n}->send ? MON : OFF); } sub refresh { Audio::Nama::remove_riff_header_stubs(); map{ $ui->refresh_track($_) } map{$_->n} Audio::Nama::audio_tracks(); refresh_waveform_window() if $gui->{wwcanvas}; } ### end 1; __END__Audio-Nama-1.216/lib/Audio/Nama/EffectsRegistry.pm0000644000175000017500000004545013544212613020636 0ustar jrothjroth## -------------- Effects registry --------------- package Audio::Nama; use Modern::Perl; use Audio::Nama::Util qw(round); no warnings 'uninitialized'; ## register data about LADSPA plugins, and Ecasound effects and # presets (names, ids, parameters, hints) sub prepare_static_effects_data{ my $source = shift; logsub("&prepare_static_effects_data"); if (not is_test_script() ){ logpkg(__FILE__,__LINE__,'debug', join "\n", "newplugins:", new_plugins()); if (! $source and ($config->{opts}->{r} or new_plugins())){ rename $file->effects_cache, $file->effects_cache . ".bak"; print "Regenerating effects data cache\n"; } } # maybe $source if ($config->{opts}->{T} ) { logpkg(__FILE__,__LINE__,'debug', "using dummy effects data"); $source = $fx_cache->{fake}; } elsif (-f $file->effects_cache and ! $config->{opts}->{C}) { logpkg(__FILE__,__LINE__,'debug', "found effects cache: ",$file->effects_cache); $source = read_file($file->effects_cache); # scalar assign } if ($source) { assign( data => decode($source, 'json'), vars => [qw($fx_cache)], class => 'Audio::Nama' ); } else { logpkg(__FILE__,__LINE__,'debug', "reading in effects data, please wait..."); initialize_effect_index(); read_in_effects_data(); # cop-register, preset-register, ctrl-register, ladspa-register get_ladspa_hints(); get_lv2_hints() unless $config->{opts}->{J}; integrate_ladspa_hints(); integrate_cop_hints(); sort_ladspa_effects(); logpkg(__FILE__,__LINE__,'debug', "updating effects cache on disk: ",$file->effects_cache); serialize ( file => $file->effects_cache, vars => [qw($fx_cache)], class => 'Audio::Nama', format => 'json') unless is_test_script(); } generate_mappings_for_shortcuts(); } sub ladspa_plugin_list { my @plugins; my %seen; for my $dir ( split ':', ladspa_path()){ next unless -d $dir; opendir(my $dirh, $dir) or die "can't open directory $dir for read: $!"; push @plugins, map{"$dir/$_"} # full path grep{ ! $seen{$_} and ++$seen{$_}} # skip seen plugins grep{ /\.so$/} readdir $dirh; # get .so files closedir $dirh; } @plugins } sub lv2_plugin_list { my @plugins; my %seen; for my $dir ( split ':', lv2_path()){ next unless -d $dir; opendir(my $dirh, $dir) or die "can't open directory $dir for read: $!"; push @plugins, map{"$dir/$_"} # full path grep{ ! $seen{$_} and ++$seen{$_}} # skip seen plugins grep{ /\.lv2$/} readdir $dirh; # get .lv2 files closedir $dirh; } @plugins } sub new_plugins { my @filenames = ladspa_plugin_list(); push @filenames, lv2_plugin_list(); push @filenames, '/usr/local/share/ecasound/effect_presets', '/usr/share/ecasound/effect_presets', "$ENV{HOME}/.ecasound/effect_presets"; my $effects_cache_stamp = modified_stamp($file->effects_cache); my $latest; map{ my $mod = modified_stamp($_); $latest = $mod if $mod > $latest } @filenames; $latest > $effects_cache_stamp; } sub modified_stamp { # timestamp that file was modified my $filename = shift; #print "file: $filename\n"; my @s = stat $filename; $s[9]; } sub initialize_effect_index { $fx_cache->{partial_label_to_full} = {}; } sub generate_mappings_for_shortcuts { logsub("&generate_mappings_for_shortcuts"); map{ my $code = $_; # abbrevations for lv2: lv2-foo for elv2:http://something.com/other/foo if ( my ($suffix) = $code =~ /(?:elv2:).*?([^\/]+)$/ ) { $fx_cache->{partial_label_to_full}->{"lv2-$suffix"} = $code; } else { my ($short) = $code =~ /:([-\w]+)/; if ( $short ) { if ($fx_cache->{partial_label_to_full}->{$short}) { warn "name collision: $_\n" } else { $fx_cache->{partial_label_to_full}->{$short} = $code } } } $fx_cache->{partial_label_to_full}->{$code} = $code; } keys %{$fx_cache->{full_label_to_index}}; #print json_out $fx_cache->{partial_label_to_full}; } { my %dispatch = ( ctrl => \&generate_help, lv2 => \&generate_lv2_help, ladspa => \&generate_ladspa_help, cop => \&generate_help, preset => \&generate_help, ); sub extract_effects_data { logsub("&extract_effects_data"); my ($plugin_type, $lower, $upper, $regex, $separator, @lines) = @_; carp ("incorrect number of lines ", join ' ',$upper-$lower,scalar @lines) if $lower + @lines - 1 != $upper; logpkg(__FILE__,__LINE__,'debug',"lower: $lower upper: $upper separator: $separator"); logpkg(__FILE__,__LINE__,'debug', "lines: ". join "\n",@lines); logpkg(__FILE__,__LINE__,'debug', "regex: $regex"); my $j = $lower - 1; while(my $line = shift @lines){ $j++; $line =~ /$regex/ or carp("bad effect data line: $line\n", join " ", map{ ord($_) } split //, $line), next; my ($no, $name, $id, $rest) = ($1, $2, $3, $4); # $no is unimportant; it from the list numbering logpkg(__FILE__,__LINE__,'debug', "Number: $no Name: $name Code: $id Rest: $rest"); my @p_names = split $separator,$rest; map{s/'//g}@p_names; # remove leading and trailing q(') in ladspa strings logpkg(__FILE__,__LINE__,'debug', "Parameter names: @p_names"); $fx_cache->{registry}->[$j]={}; #$fx_cache->{registry}->[$j]->{number} = $no; $fx_cache->{registry}->[$j]->{code} = $id; $fx_cache->{registry}->[$j]->{name} = $name; $fx_cache->{registry}->[$j]->{count} = scalar @p_names; $fx_cache->{registry}->[$j]->{params} = []; $fx_cache->{registry}->[$j]->{display} = qq(field); $fx_cache->{registry}->[$j]->{plugin_type} = $plugin_type; $fx_cache->{user_help}->[$j] = $dispatch{$plugin_type}->($line); map{ push @{$fx_cache->{registry}->[$j]->{params}}, {name => $_} } @p_names if @p_names; } } } sub sort_ladspa_effects { logsub("&sort_ladspa_effects"); # print json_out($fx_cache->{split}); my $aa = $fx_cache->{split}->{ladspa}{a}; my $zz = $fx_cache->{split}->{ladspa}{z}; # print "start: $aa end $zz\n"; map{push @{$fx_cache->{ladspa_sorted}}, 0} ( 1 .. $aa ); # fills array slice [0..$aa-1] splice @{$fx_cache->{ladspa_sorted}}, $aa, 0, sort { $fx_cache->{registry}->[$a]->{name} cmp $fx_cache->{registry}->[$b]->{name} } ($aa .. $zz) ; logpkg(__FILE__,__LINE__,'debug', "sorted array length: ". scalar @{$fx_cache->{ladspa_sorted}}); } sub run_external_ecasound_cmd { my $cmd = shift; my $output = qx(sh -c 'echo $cmd | ecasound -c '); trim_output($output) } sub trim_output { my $output = shift; $output =~ s/\n\.{3} //g; $output =~ s/\r//; $output =~ s/^.+?Registered \w+ plugins:\s*//s; # XXX HARDCODED for plugins only $output =~ s/^ecasound .+?\Z//ms; my @output = split "\n",$output; #splice @output, 0,8; #splice @output, -3,3; join "\n",@output; } sub read_in_effects_data { logsub("&read_in_effects_data"); #### LADSPA my $lr = $config->{use_effects_bugfix} ? run_external_ecasound_cmd('ladspa-register') : ecasound_iam('ladspa-register'); logpkg(__FILE__,__LINE__,'debug',"ladpsa-register output:\n",$lr); my @ladspa = split "\n", $lr; # join the two lines of each entry my @lad = map { join " ", splice(@ladspa,0,2) } 1..@ladspa/2; #logpkg(__FILE__,__LINE__,'debug',join "\n","ladpsa-register processed output:",@lad); generate_ladspa_help($_) for @lad; #### LV2 my $lv2 = $config->{use_effects_bugfix} ? run_external_ecasound_cmd('lv2-register') : ecasound_iam('lv2-register'); # TODO test fake lv2-register # get_data_section('fake_lv2_register'); logpkg(__FILE__,__LINE__,'debug',"lv2-register output:\n",$lv2); # join pairs of lines $lv2 =~ s/\s+(?=-elv2)/ /g; # now we can handle similar to LADSPA # split on newlines my @lv2 = split /\n/,$lv2; generate_lv2_help($_) for @lv2; # logpkg(__FILE__,__LINE__,'debug',sub{ json_out(\@lv2) }); logpkg(__FILE__,__LINE__,'trace',sub{ json_out(\@lv2) }); my $preset = ecasound_iam("preset-register"); my @preset = grep {! /^\s*$/ } split "\n", $preset; generate_help($_) for @preset; logpkg(__FILE__,__LINE__,'debug',"preset-register output:\n",$preset); my $ctrl = ecasound_iam("ctrl-register"); my @ctrl = grep {! /^\s*$/ } split "\n", $ctrl; logpkg(__FILE__,__LINE__,'debug',"ctrl-register output:\n",$ctrl); generate_help($_) for @ctrl; my $cop = ecasound_iam("cop-register"); my @cop = grep {! /^\s*$/ } split "\n", $cop; generate_help($_) for @cop; logpkg(__FILE__,__LINE__,'debug',"cop-register output:\n",$cop); logpkg(__FILE__,__LINE__,'debug', "found ", scalar @cop, " Ecasound chain operators"); logpkg(__FILE__,__LINE__,'debug', "found ", scalar @preset, " Ecasound presets"); logpkg(__FILE__,__LINE__,'debug', "found ", scalar @ctrl, " Ecasound controllers"); logpkg(__FILE__,__LINE__,'debug', "found ", scalar @lad, " LADSPA effects"); logpkg(__FILE__,__LINE__,'debug', "found ", scalar @lv2, " LV2 effects"); # index boundaries we need to make effects list and menus $fx_cache->{split}->{cop}{a} = 1; $fx_cache->{split}->{cop}{z} = @cop; # scalar $fx_cache->{split}->{ladspa}{a} = $fx_cache->{split}->{cop}{z} + 1; $fx_cache->{split}->{ladspa}{b} = $fx_cache->{split}->{cop}{z} + int(@lad/4); $fx_cache->{split}->{ladspa}{c} = $fx_cache->{split}->{cop}{z} + 2*int(@lad/4); $fx_cache->{split}->{ladspa}{d} = $fx_cache->{split}->{cop}{z} + 3*int(@lad/4); $fx_cache->{split}->{ladspa}{z} = $fx_cache->{split}->{cop}{z} + @lad; $fx_cache->{split}->{preset}{a} = $fx_cache->{split}->{ladspa}{z} + 1; $fx_cache->{split}->{preset}{b} = $fx_cache->{split}->{ladspa}{z} + int(@preset/2); $fx_cache->{split}->{preset}{z} = $fx_cache->{split}->{ladspa}{z} + @preset; $fx_cache->{split}->{ctrl}{a} = $fx_cache->{split}->{preset}{z} + 1; $fx_cache->{split}->{ctrl}{z} = $fx_cache->{split}->{preset}{z} + @ctrl; $fx_cache->{split}->{lv2}{a} = $fx_cache->{split}->{ctrl}{z} + 1; $fx_cache->{split}->{lv2}{z} = $fx_cache->{split}->{ctrl}{z} + @lv2; my $cop_re = qr/ ^(\d+) # number \. # dot \s+ # spaces+ (\w.+?) # name, starting with word-char, non-greedy # (\w+) # name ,\s* # comma spaces* -(\w+) # effect_id :? # maybe colon (if parameters) (.*$) # rest /x; my $preset_re = qr/ ^(\d+) # number \. # dot \s+ # spaces+ (\w+) # name ,\s* # comma spaces* -(pn:\w+) # preset_id :? # maybe colon (if parameters) (.*$) # rest /x; my $ladspa_re = qr/ ^(\d+) # number \. # dot \s+ # spaces (.+?) # name, any non-greedy \s+ # spaces -(el:[-\w]+),? # ladspa_id maybe followed by comma (.*$) # rest /x; my $lv2_re = qr/ ^(\d+) # number \. # dot \s+ # spaces (.+?) # name, any non-greedy \s+ # space -(? # named captured named 'name' elv2: # prefix is -elv2: [^,]+ # URL: non-comma chars ), # comma (.*$) # rest /x; my $ctrl_re = qr/ ^(\d+) # number \. # dot \s+ # spaces (\w.+?) # name, starting with word-char, non-greedy ,\s* # comma, zero or more spaces -(k\w+):? # ktrl_id maybe followed by colon (.*$) # rest /x; extract_effects_data( 'cop', $fx_cache->{split}->{cop}{a}, $fx_cache->{split}->{cop}{z}, $cop_re, q(,), @cop, ); extract_effects_data( 'ladspa', $fx_cache->{split}->{ladspa}{a}, $fx_cache->{split}->{ladspa}{z}, $ladspa_re, q(','), @lad, ); extract_effects_data( 'lv2', $fx_cache->{split}->{lv2}{a}, $fx_cache->{split}->{lv2}{z}, $lv2_re, q(','), @lv2, ); extract_effects_data( 'preset', $fx_cache->{split}->{preset}{a}, $fx_cache->{split}->{preset}{z}, $preset_re, q(,), @preset, ); extract_effects_data( 'ctrl', $fx_cache->{split}->{ctrl}{a}, $fx_cache->{split}->{ctrl}{z}, $ctrl_re, q(,), @ctrl, ); for my $i (0..$#{$fx_cache->{registry}}){ $fx_cache->{full_label_to_index}->{ $fx_cache->{registry}->[$i]->{code} } = $i; logpkg(__FILE__,__LINE__,'debug', "i: $i code: $fx_cache->{registry}->[$i]->{code} display: $fx_cache->{registry}->[$i]->{display}"); } logpkg(__FILE__,__LINE__,'debug', sub{"$fx_cache->{registry}\n======\n", json_out($fx_cache->{registry})}); ; } sub integrate_cop_hints { my @cop_hints = @{ yaml_in( get_data_section('ecasound_chain_operator_hints_yml')) }; for my $hashref ( @cop_hints ){ #print "cop hints ref type is: ",ref $hashref, $/; my $code = $hashref->{code}; $fx_cache->{registry}->[ $fx_cache->{full_label_to_index}->{ $code } ] = $hashref; } } sub ladspa_path { $ENV{LADSPA_PATH} || q(/usr/lib/ladspa); } sub lv2_path { $ENV{LV2_PATH} || q(/usr/lib/lv2); } sub get_ladspa_hints{ logsub("&get_ladspa_hints"); my @dirs = split ':', ladspa_path(); my $data = ''; my %seen = (); my @plugins = ladspa_plugin_list(); #pager join $/, @plugins; # use these regexes to snarf data my $pluginre = qr/ Plugin\ Name: \s+ "([^"]+)" \s+ Plugin\ Label: \s+ "([^"]+)" \s+ Plugin\ Unique\ ID: \s+ (\d+) \s+ [^\x00]+(?=Ports) # swallow maximum up to Ports Ports: \s+ ([^\x00]+) # swallow all /x; my $paramre = qr/ "([^"]+)" # name inside quotes \s+ (.+) # rest /x; my $i; for my $file (@plugins){ my @stanzas = split "\n\n", qx(analyseplugin $file); for my $stanza (@stanzas) { my ($plugin_name, $plugin_label, $plugin_unique_id, $ports) = $stanza =~ /$pluginre/ or carp "*** couldn't match plugin stanza $stanza ***"; logpkg(__FILE__,__LINE__,'debug', "plugin label: $plugin_label $plugin_unique_id"); my @lines = grep{ /control/ } split "\n",$ports; my @params; # data my @names; for my $p (@lines) { next if $p =~ /^\s*$/; $p =~ s/\.{3}/10/ if $p =~ /amplitude|gain/i; $p =~ s/\.{3}/60/ if $p =~ /delay|decay/i; $p =~ s(\.{3})($config->{sample_rate}/2) if $p =~ /frequency/i; $p =~ /$paramre/; my ($name, $rest) = ($1, $2); my ($dir, $type, $range, $default, $hint) = split /\s*,\s*/ , $rest, 5; logpkg(__FILE__,__LINE__,'debug', join( "|",$name, $dir, $type, $range, $default, $hint) ); # if $hint =~ /logarithmic/; if ( $range =~ /toggled/i ){ $range = q(0 to 1); $hint .= q(toggled); } my %p; $p{name} = $name; $p{dir} = $dir; $p{hint} = $hint; my ($beg, $end, $default_val, $resolution) = range($name, $range, $default, $hint, $plugin_label); $p{begin} = $beg; $p{end} = $end; $p{default} = $default_val; $p{resolution} = $resolution; push @params, { %p }; } $plugin_label = "el:" . $plugin_label; $fx_cache->{ladspa_help}->{$plugin_label} = $stanza; $fx_cache->{ladspa_id_to_filename}->{$plugin_unique_id} = $file; $fx_cache->{ladspa_label_to_unique_id}->{$plugin_label} = $plugin_unique_id; $fx_cache->{ladspa_label_to_unique_id}->{$plugin_name} = $plugin_unique_id; $fx_cache->{ladspa_id_to_label}->{$plugin_unique_id} = $plugin_label; $fx_cache->{ladspa}->{$plugin_label}->{name} = $plugin_name; $fx_cache->{ladspa}->{$plugin_label}->{id} = $plugin_unique_id; $fx_cache->{ladspa}->{$plugin_label}->{params} = [ @params ]; $fx_cache->{ladspa}->{$plugin_label}->{count} = scalar @params; $fx_cache->{ladspa}->{$plugin_label}->{display} = 'scale'; } # pager( join "\n======\n", @stanzas); #last if ++$i > 10; } for (1..scalar @{ $fx_cache->{user_help}} ) { next if $fx_cache->{registry}->[$_]->{plugin_type} ne 'ladspa'; my $code = $fx_cache->{registry}->[$_]->{code}; my $id = $fx_cache->{ladspa_label_to_unique_id}->{$code}; $fx_cache->{user_help}->[$_] =~ s/^\d+/$id/; } logpkg(__FILE__,__LINE__,'debug', sub{json_out($fx_cache->{ladspa})}); } sub get_lv2_hints { my @plugins = split " ", qx(lv2ls); logpkg(__FILE__,__LINE__,'debug','No LV2 plugins found'), return unless @plugins; map { $fx_cache->{lv2_help}->{"elv2:$_"} = join '', Audio::Nama::AnalyseLV2::lv2_help($_) } @plugins; } sub srate_val { my $input = shift; my $val_re = qr/( [+-]? # optional sign \d+ # one or more digits (\.\d+)? # optional decimal (e[+-]?\d+)? # optional exponent )/ix; # case insensitive e/E my ($val) = $input =~ /$val_re/; # or carp "no value found in input: $input\n"; $val * ( $input =~ /srate/ ? $config->{sample_rate} : 1 ) } sub range { my ($name, $range, $default, $hint, $plugin_label) = @_; my $multiplier = 1;; my ($beg, $end) = split /\s+to\s+/, $range; $beg = srate_val( $beg ); $end = srate_val( $end ); $default = srate_val( $default ); $default = $default || $beg; logpkg(__FILE__,__LINE__,'debug', "beg: $beg, end: $end, default: $default"); if ( $name =~ /gain|amplitude/i ){ $beg = 0.01 unless $beg; $end = 0.01 unless $end; } my $resolution = ($end - $beg) / 100; if ($hint =~ /integer|toggled/i ) { $resolution = 1; } elsif ($hint =~ /logarithmic/ ) { $beg = round ( log $beg ) if $beg; $end = round ( log $end ) if $end; $resolution = ($end - $beg) / 100; $default = $default ? round (log $default) : $default; } $resolution = d2( $resolution + 0.002) if $resolution < 1 and $resolution > 0.01; $resolution = dn ( $resolution, 3 ) if $resolution < 0.01; $resolution = int ($resolution + 0.1) if $resolution > 1 ; ($beg, $end, $default, $resolution) } sub integrate_ladspa_hints { logsub("&integrate_ladspa_hints"); map{ my $i = $fx_cache->{full_label_to_index}->{$_}; # print("$_ not found\n"), if ($i) { $fx_cache->{registry}->[$i]->{params} = $fx_cache->{ladspa}->{$_}->{params}; # we revise the number of parameters read in from ladspa-register $fx_cache->{registry}->[$i]->{count} = scalar @{$fx_cache->{ladspa}->{$_}->{params}}; $fx_cache->{registry}->[$i]->{display} = $fx_cache->{ladspa}->{$_}->{display}; } } keys %{$fx_cache->{ladspa}}; my %L; my %M; map { $L{$_}++ } keys %{$fx_cache->{ladspa}}; map { $M{$_}++ } grep {/el:/} keys %{$fx_cache->{full_label_to_index}}; for my $k (keys %L) { $M{$k} or logpkg(__FILE__,__LINE__,'debug', "$k not found in ecasound listing"); } for my $k (keys %M) { $L{$k} or logpkg(__FILE__,__LINE__,'debug', "$k not found in ladspa listing"); } logpkg(__FILE__,__LINE__,'debug', sub {join "\n", sort keys %{$fx_cache->{ladspa}}}); logpkg(__FILE__,__LINE__,'debug', '-' x 60); logpkg(__FILE__,__LINE__,'debug', sub{join "\n", grep {/el:/} sort keys %{$fx_cache->{full_label_to_index}}}); #print json_out $fx_cache->{registry}; exit; } sub generate_help { my $line = shift; $line =~ s/^.*? //; # remove initial number $line .= "\n"; # add newline s/,/, /g; # to help line breaks $line; } sub generate_lv2_help { my $line = shift; if ($line =~ /elv2:/){ my $trimmed = $line; $trimmed =~ s/^\d+\.\s*//; $trimmed =~ s/\t/ /g; $trimmed =~ s/'//g; $trimmed =~ s/,/, /g; $trimmed = "LV2 $trimmed"; $line = $trimmed; } # remove Ecasound registry index No., if present $line =~ s/^\d+\.\s*//; $line } sub generate_ladspa_help { my $line = shift; my ($label) = $line =~ /-(el:[-\w]+)/; $line =~ s/^\s{2,} //g; # trim spaces $line =~ s/\t/ /g; # convert tabs to spaces $line =~ s/'//g; # remove apostrophes $line =~ s/,/, /g; # for nice linebreaks $line =~ s/\s+$/ /; # remove trailing spaces $line .="\n"; # add newline } 1;Audio-Nama-1.216/lib/Audio/Nama/IO.pm0000644000175000017500000003675313544212613016043 0ustar jrothjrothpackage Audio::Nama; our (%tn, $jack, $config); # ---------- IO ----------- # # IO objects for writing Ecasound chain setup file # # Object values can come from three sources: # # 1. As arguments to the constructor new() while walking the # routing graph: # + assigned by dispatch: chain_id, loop_id, track, etc. # + override by graph node (higher priority) # + override by graph edge (highest priority) # 2. (sub)class methods called as $object->method_name # + defined as _method_name (access via AUTOLOAD, overrideable by constructor) # + defined as method_name (not overrideable) # 3. AUTOLOAD # + any other method calls are passed to the the associated track # + illegal track method call generate an exception package Audio::Nama::IO; use Modern::Perl; use Carp; use Data::Dumper::Concise; our $VERSION = 1.0; # provide following vars to all packages our ($config, $jack, %tn); our (%by_name); # index for $by_name{trackname}->{input} = $object use Audio::Nama::Globals qw($config $jack %tn $setup :trackrw); use Try::Tiny; sub initialize { %by_name = () } # we will use the following to map from graph node names # to IO class names our %io_class = qw( null_in Audio::Nama::IO::from_null null_out Audio::Nama::IO::to_null soundcard_in Audio::Nama::IO::from_soundcard soundcard_out Audio::Nama::IO::to_soundcard soundcard_device_in Audio::Nama::IO::from_alsa_soundcard_device soundcard_device_out Audio::Nama::IO::to_alsa_soundcard_device wav_in Audio::Nama::IO::from_wav wav_out Audio::Nama::IO::to_wav loop_source Audio::Nama::IO::from_loop loop_sink Audio::Nama::IO::to_loop jack_manual_in Audio::Nama::IO::from_jack_port jack_manual_out Audio::Nama::IO::to_jack_port jack_ports_list_in Audio::Nama::IO::from_jack_port jack_ports_list_out Audio::Nama::IO::to_jack_port jack_multi_in Audio::Nama::IO::from_jack_multi jack_multi_out Audio::Nama::IO::to_jack_multi jack_client_in Audio::Nama::IO::from_jack_multi jack_client_out Audio::Nama::IO::to_jack_multi bus_in Audio::Nama::IO::from_bus ); #bus_out Audio::Nama::IO::to_bus # ### class descriptions # === CLASS Audio::Nama::IO::from_jack_port === # # is triggered by source_type codes: # # + jack_manual_in # + jack_ports_list_in # # For track 'piano', the class creates an input similar to: # # -i:jack,,piano_in # # which receives input from JACK node: # # + Nama:piano_in, # # If piano is stereo, the actual ports will be: # # + Nama:piano_in_1 # + Nama:piano_in_2 # (CLASS Audio::Nama::IO::to_jack_port is similar) ### class definition our $AUTOLOAD; # add underscore to field names so that regular method # access will go through AUTOLOAD # we add an underscore to each key use Audio::Nama::Object qw(track_ chain_id_ endpoint_ format_ format_template_ width_ ecs_extra_ direction_ device_id_); sub new { my $class = shift; my %vals = @_; my @args = map{$_."_", $vals{$_}} keys %vals; # add underscore to key # note that we won't check for illegal fields # so we can pass any value and allow AUTOLOAD to # check the hash for it. my $self = bless {@args}, $class; my $direction = $self->direction; # input or output # join IO objects to graph my $name; try{ $name = $self->name } catch {}; # we do nothing { no warnings 'uninitialized'; Audio::Nama::logpkg(__FILE__,__LINE__,'debug',"I belong to track $name\n", sub{Dumper($self)} ); } if($name){ $by_name{$name}->{$direction} = $self; } $self } # latency stubs sub capture_latency { my $self = shift; return unless $self->client; Audio::Nama::jack_port_latency('input', $self->client); } sub playback_latency { my $self = shift; return unless $self->client; Audio::Nama::jack_port_latency('output', $self->client); } # we need at least stubs for subclasses' methods # for AUTOLOAD to be happy - so we include sub client {} #### JACK related methods # inherited by all, the methods defined below are called in # these classes: # # to_jack_multi, # from_jack_multi # to_jack_client # from_jack_client # # They have no function in other classes. sub target_id { my $self = shift; $self->direction eq 'input' ? $self->source_id : $self->send_id; } sub target_type { my $self = shift; $self->direction eq 'input' ? $self->source_type : $self->send_type; } sub target_channel { my $self = shift; $self->target_id =~ /^(\d+)$/ ? $1 : 1 } sub ports { my $self = shift; my $client_direction = $self->direction eq 'input' ? 'output' : 'input'; Audio::Nama::IO::jack_multi_ports( $self->client, $client_direction, $self->target_channel, $self->width, try{$self->name} ) if $self->client } sub ecs_string { my $self = shift; my @parts; push @parts, '-f:'.$self->format if $self->format; push @parts, '-'.$self->io_prefix.':'.$self->device_id; join ' ',@parts; } ## the format() method generates the correct Ecasound format string, ## (e.g. -f:f32_le,2,48000) if the _format_template() method ## returns a signal format template (e.g. f32_le,N,48000) sub _format { my $self = shift; Audio::Nama::signal_format($self->format_template, $self->width) if $self->format_template and $self->width } sub _format_template {} # the leading underscore allows override # by a method without the underscore sub _ecs_extra {} # allow override sub direction { (ref $_[0]) =~ /::from/ ? 'input' : 'output' } sub io_prefix { substr $_[0]->direction, 0, 1 } # 'i' or 'o' sub AUTOLOAD { my $self = shift; # get tail of method call my ($call) = $AUTOLOAD =~ /([^:]+)$/; my $result = q(); my $field = "$call\_"; my $method = "_$call"; return $self->{$field} if exists $self->{$field}; return $self->$method if $self->can($method); { no warnings 'uninitialized'; if ( my $track = $tn{$self->{track_}} ){ return $track->$call if $track->can($call) # ->can is reliable here because Track has no AUTOLOAD } # XX Suppress exceptions on objects that don't have an # associated track return undef if $call eq 'channel_ops'; return undef if $call eq 'name' or $call eq 'surname'; } my $msg = "Autoload fell through. Object type: ". (ref $self). " illegal method call: $call\n"; Audio::Nama::throw($msg,$self->dump); croak } sub DESTROY {} sub _mono_to_stereo{ # copy mono track to stereo if we have a pan control and a mono source # Truth table #REC status, Track width stereo: null #REC status, Track width mono: chcopy #PLAY status, WAV width mono: chcopy #PLAY status, WAV width stereo: null #Higher channel count (WAV or Track): null my $self = shift; my $status = $self->rec_status(); my $copy = "-chcopy:1,2"; my $nocopy = ""; my $is_mono_track = sub { $self->width == 1 }; my $is_mono_wav = sub { Audio::Nama::channels($self->wav_format) == 1}; if ( ($self->track and $tn{$self->track}->pan) and ( $status =~ /REC|MON/ and $is_mono_track->() or $status eq PLAY and $is_mono_wav->() ) ) { $copy } else { $nocopy } } sub _playat_output { my $track = shift; return unless $track->shifted_playat_time; # or $track->latency_offset; join ',',"playat" , $track->shifted_playat_time # + $track->latency_offset } sub _select_output { my $track = shift; no warnings 'uninitialized'; my $start = $track->shifted_region_start_time + $config->hardware_latency(); my $end = $track->shifted_region_end_time; return unless $config->hardware_latency() or defined $start and defined $end; my $setup_length; # CASE 1: a region is defined if ($end) { $setup_length = $end - $start; } # CASE 2: only hardware latency else { $setup_length = $track->wav_length - $start } join ',',"select", $start, $setup_length } ### utility subroutines sub get_class { my ($type,$direction) = @_; Audio::Nama::Graph::is_a_loop($type) and return $io_class{ $direction eq 'input' ? "loop_source" : "loop_sink"}; $io_class{$type} or croak "unrecognized IO type: $type" } sub soundcard_input_type_string { $jack->{jackd_running} ? 'jack_multi_in' : 'soundcard_device_in' } sub soundcard_output_type_string { $jack->{jackd_running} ? 'jack_multi_out' : 'soundcard_device_out' } sub soundcard_input_device_string { $jack->{jackd_running} ? 'system' : $config->{alsa_capture_device} } sub soundcard_output_device_string { $jack->{jackd_running} ? 'system' : $config->{alsa_playback_device} } sub jack_multi_route { my (@ports) = @_; join q(,),q(jack_multi), map{quote_jack_port($_)} @ports } sub jack_multi_ports { my ($client, $direction, $start, $width, $trackname) = @_; no warnings 'uninitialized'; Audio::Nama::logpkg(__FILE__,__LINE__,'debug',"trackname: $trackname, client $client, direction $direction, start: $start, width $width"); # can we route to these channels? my $end = $start + $width - 1; # the following logic avoids deferencing undef for a # non-existent client, and correctly handles # the case of a portname (containing colon) my $channel_count = scalar @{$jack->{clients}->{$client}{$direction}}; my $source_or_send = $direction eq 'input' ? 'send' : 'source'; die(qq( Track $trackname: $source_or_send would cover channels $start - $end, out of bounds for JACK client "$client" ($channel_count channels max). Change $source_or_send setting, or set track OFF.)) if $end > $channel_count and $config->{enforce_channel_bounds}; return @{$jack->{clients}->{$client}{$direction}}[$start-1..$end-1] if $jack->{clients}->{$client}{$direction}; } #sub one_port { $jack->{clients}->{$client}->{$direction}->[$start-1] } sub quote_jack_port { my $port = shift; ($port =~ /\s/ and $port !~ /^"/) ? qq("$port") : $port } sub rectified { # client name from number $_[0] =~ /^\d+$/ ? 'system' : $_[0] } ### subclass definitions ### method names with a preceding underscore ### can be overridded by the object constructor { package Audio::Nama::IO::from_null; use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO'; sub _device_id { 'null' } } { package Audio::Nama::IO::to_null; use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO'; sub _device_id { 'null' } } { package Audio::Nama::IO::from_rtnull; use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO'; sub _device_id { 'rtnull' } } { package Audio::Nama::IO::to_rtnull; use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO'; sub _device_id { 'rtnull' } } { package Audio::Nama::IO::from_wav; use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO'; sub device_id { my $self = shift; my @modifiers; push @modifiers, $self->playat_output if $self->playat_output; push @modifiers, $self->select_output if $self->select_output; push @modifiers, split " ", $self->modifiers if $self->modifiers; push @modifiers, $self->full_path; join(q[,],@modifiers); } sub _format { $Audio::Nama::setup->{wav_info}->{$_[0]->full_path}->{format} }; sub ecs_extra { $_[0]->mono_to_stereo} sub client { 'system' if $jack->{jackd_running} } sub ports { 'system:capture_1' } } { package Audio::Nama::IO::to_wav; use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO'; sub device_id { $_[0]->full_path } sub _format_template { $config->{raw_to_disk_format} } } { package Audio::Nama::IO::from_loop; use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO'; sub new { my $class = shift; my %vals = @_; $class->SUPER::new( %vals, device_id => "loop,$vals{endpoint}"); } sub _format { my $self = shift; return if $Audio::Nama::config->{opts}->{T}; # XX don't break tests Audio::Nama::signal_format($self->format_template, $config->{loop_chain_channel_width}); } sub _format_template { $config->{cache_to_disk_format} } } { package Audio::Nama::IO::to_loop; use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO::from_loop'; } { package Audio::Nama::IO::from_soundcard; use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO'; sub new { shift; # throw away class my $class = $io_class{Audio::Nama::IO::soundcard_input_type_string()}; $class->new(@_); } } { package Audio::Nama::IO::to_soundcard; use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO'; sub new { shift; # throw away class my $class = $io_class{Audio::Nama::IO::soundcard_output_type_string()}; $class->new(@_); } } { package Audio::Nama::IO::to_jack_multi; use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO'; sub client { my $self = shift; # say "to_jack_multi: target_id: ",$self->target_id; # say "to_jack_multi: rectified target_id: ",Audio::Nama::IO::rectified($self->target_id); Audio::Nama::IO::rectified($self->target_id) } sub device_id { my $self = shift; Audio::Nama::IO::jack_multi_route($self->ports) } } { package Audio::Nama::IO::from_jack_multi; use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO::to_jack_multi'; sub ecs_extra { $_[0]->mono_to_stereo } } { package Audio::Nama::IO::to_jack_port; use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO'; sub format_template { $config->{devices}->{jack}->{signal_format} } sub device_id { 'jack,,'.$_[0]->port_name.'_out' } sub ports { $config->{ecasound_jack_client_name}. ":".$_[0]->port_name. '_out_1' } # at least this one port } { package Audio::Nama::IO::from_jack_port; use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO::to_jack_port'; sub device_id { 'jack,,'.$_[0]->port_name.'_in' } sub ecs_extra { $_[0]->mono_to_stereo } sub ports { $config->{ecasound_jack_client_name}.":".$_[0]->port_name. '_in_1' } # at least this one port } { package Audio::Nama::IO::to_jack_client; use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO'; sub device_id { "jack," . Audio::Nama::IO::quote_jack_port($_[0]->send_id); } sub client { Audio::Nama::IO::rectified($_[0]->send_id) } } { package Audio::Nama::IO::from_jack_client; use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO'; sub device_id { 'jack,'. Audio::Nama::IO::quote_jack_port($_[0]->source_id); } sub ecs_extra { $_[0]->mono_to_stereo} sub client { Audio::Nama::IO::rectified($_[0]->source_id) } } { package Audio::Nama::IO::from_alsa_soundcard_device; use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO'; sub ecs_extra { join ' ', $_[0]->rec_route, $_[0]->mono_to_stereo } sub device_id { $config->{devices}->{$config->{alsa_capture_device}}->{ecasound_id} } sub input_channel { $_[0]->source_id } sub rec_route { # works for mono/stereo only! no warnings qw(uninitialized); my $self = shift; # needed only if input channel is greater than 1 return '' if ! $self->input_channel or $self->input_channel == 1; my $route = "-chmove:" . $self->input_channel . ",1"; if ( $self->width == 2){ $route .= " -chmove:" . ($self->input_channel + 1) . ",2"; } return $route; } } { package Audio::Nama::IO::to_alsa_soundcard_device; use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO'; sub device_id { $config->{devices}->{$config->{alsa_playback_device}}{ecasound_id} } sub ecs_extra {route($_[0]->width,$_[0]->output_channel) } sub output_channel { $_[0]->send_id } sub route2 { my ($from, $to, $width) = @_; } sub route { # routes signals (1..$width) to ($dest..$dest+$width-1 ) my ($width, $dest) = @_; return '' if ! $dest or $dest == 1; # print "route: width: $width, destination: $dest\n\n"; my $offset = $dest - 1; my $route ; for my $c ( map{$width - $_ + 1} 1..$width ) { $route .= " -chmove:$c," . ( $c + $offset); } $route; } } { package Audio::Nama::IO::from_bus; use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO'; sub new { my $class = shift; my %vals = @_; print "from_bus: ", Audio::Nama::Dumper \%vals; #$class->SUPER::new( %vals, device_id => "loop,$vals{endpoint}"); } } { package Audio::Nama::IO::any; use Modern::Perl; use vars qw(@ISA); @ISA = 'Audio::Nama::IO'; } 1; __END__Audio-Nama-1.216/Makefile.PL0000644000175000017500000000225313544211573014455 0ustar jrothjrothuse inc::Module::Install; # Define metadata name 'Audio-Nama'; version_from 'lib/Audio/Nama.pm'; all_from 'script/nama'; license 'gpl'; install_script 'script/nama'; keywords 'audio', 'recording', 'mixing', 'digital', 'workstation', 'effects', 'editing'; repository 'https://github.com/bolangi/nama'; # Specific dependencies requires qw( autodie 0 AnyEvent 5.0 AnyEvent::TermKey 0 Data::Dumper::Concise 0 Data::Section::Simple 0 Event 0 File::Find::Rule 0 File::Copy 0 File::Copy::Link 0 File::HomeDir 0 File::Slurp 0 File::Temp 0 Git::Repository 0 Graph 0 IO::Socket 0 IO::Select 0 JSON::XS 0 IPC::Open3 0 List::Util 0 List::MoreUtils 0 Log::Log4perl 0 Modern::Perl 0 Module::Load::Conditional 0 Parse::RecDescent 0 Role::Tiny 0 SUPER 0 Term::ReadLine::Gnu 0 Test2::Bundle::More 0 Text::Diff 0 Text::Format 0 Text::Template 0 Time::HiRes 0 Try::Tiny 0 YAML::Tiny 0 ); WriteAll; Audio-Nama-1.216/Changes0000644000175000017500000001054013544211573013774 0ustar jrothjrothChanges to Audio::Nama 1.214 Sep 26, 2019 * numerous updates (1,040 commits since 1.205) 1.205 Dec 31, 2015 1.110 Jan 1, 2014 * basic implementation of sequences/clips * use git is now default: retain project history, with tagging and branching of significant snapshots * add undo/redo * add remote control interface * add multi engine support * add hotkey mode * do not delete marks that fades depend on * retain playback position on project save/load * wrap seeks in fades for smooth transitions * many fixes - git repository is the authoritative source 1.102 March 3, 2013 * new .namarc option 'use_git' for project management + many internal changes to support git + save/get commands handle branches similar to save/get files * drop backwards compatibility and project conversion (checkout Rosetta for this) * auto mp3/ogg encoding of mixdown files * name mixdown files using project or branch name * cleanup variable declarations * dismantle eager mode * internal latency compensation option 1.100 March 9, 2012 * New serialization format, prefer json * convert_project_format() + archives project state.yml files + converts them to new format state.json + run as nama> eval convert_project_format * New class EffectChain used to specify presets, store bypass parameters * Replace hated and maligned bus MON mode filter. (Use 'rerecord' to toggle to previous REC setup.) * Eager mode - get sound to soundcard ASAP. + Declare as eager_mode: doodle in .namarc + Two varieties of eager: preview and doodle - doodle: monitor "live" inputs only - preview: monitor "live" inputs while playing back WAV files + Issue the 'arm' command when ready to record. * Optional use of Git for managing state files and therefore project development. * Rename source files * Replace 225 global variables by 16 "singletons", which are still global variables * support for multiple serialization formats * Miscellaneous fixes 1.078 August 19, 2011 * new commands + view_waveform launches Mhwaveedit on current track/version/waveform + edit_waveform launches Audacity on current track/version/waveform 1.077 August 12, 2011 * fix: nosolo doesn't restore previous state (umutes all tracks) * incorporate whatis.patch from debian 1.076 August 8, 2011 * numerous fixes (consult git log for details) 1.073 April 9, 2011 * fix bug with inserts * move entire man page to executable 1.070 March 27, 2011 New features * track edits (non-destructive punch-in style recording) * track comments * version comments * project templates * autosave * support jack.plumbing and jack_connect for JACK client connections * user-defined commands (custom.pl) Other changes * expanded test coverage * initial support for Midish MIDI sequencer and filter * separate code into multiple modules * many fixes and improvements 1.064 August 6, 2010 * fix Bug rt.cpan.org #60024: Audio::Nama::Assign::expand_tilde() 1.063 August 2, 2010 * various minor improvements * fix Bug#591166 (Debian build support) 1.052 March 20, 2010 (summary) * prompt displays current bus and current track * configurable use of ea or eadb for volume control * Ladish Level 1 support * big speedup by caching results of Track and Wav methods * generate setup using graph representation and IO objects * rewrite send- and sub-buses * track caching (track freezing) * post-fader track inserts (send/receive) with wet/dry control * more flexible track input + manual connection to JACK port + auto connect to list of JACK ports * track normalize and fixdc commands * automix (normalized mixdown) command * import audio: copy files unless resampling or converting format * region shifting, with multiple regions per track * effect chains (presets) * effect profiles (effect chains over multiple tracks) * non-recording preview and doodle modes * unified event code for GUI and text modes * rewrite support for Ecasound controllers * test coverage for signal routing functions * new debugging options Audio-Nama-1.216/README0000644000175000017500000000774413544211573013375 0ustar jrothjroth=head1 NAME Nama - digital audio workstation =head1 DESCRIPTION Nama is a digital audio workstation. It is suitable for multitrack recording, effects-processing, editing, mixing, and other audio tasks. Nama uses Ecasound, developed by Kai Vehmanen, for audio processing. Nama hosts LADSPA and LV2 plugins, Ecasound effects and controllers. It works well under JACK and ALSA. New projects begin with a mixer, and may include tracks (multiple takes), buses, effects, sends, inserts, marks, regions, fades, edits, sequences and submixes, with mixdown to wav, ogg, mp3, etc. Nama has a full-featured command interpreter with TAB completion, keyword help and command history; a hotkey mode for tweaking effect parameters, a Tk-based GUI, and project management (history, branching, tags) based on git. Users can define command aliases, custom commands, and key bindings for the hotkey mode. The help system provides searchable access to documentation for all Nama commands and shortcuts, and for LADSPA, LV2 and Ecasound effects. In addition to executing its own commands, Nama will pass commands to Ecasound, Midish, the perl interpreter and the shell. Nama has several templating options for project reuse: Effect chains are presets for one or more effects. Effect profiles (used to create Nama's mastering network) are templates for placing effects on multiple tracks. User scripting provides another way to reuse functionality. Nama's GUI will display if Tk is available. Nama can spawn Audacity or MHWaveedit to view/edit selected waveforms. =head2 Project management Project state is serialized as JSON files and the entire project history is managed by Git. Projects can be branched, tagged, and easily restored to earlier states. =head2 Project reuse Three types of templates are available to reuse project components: effect chains, effect profiles, and project templates. An effect chain is a series of effects with parameters. An effect profile includes one or more tracks with their effects and inserts. Nama's mastering network is stored as an effect profile. Project templates duplicate an entire project without audio files. =head1 INSTALLATION =head2 Installing Nama and its Perl Dependencies from CPAN The following command will install Nama, automatically pulling in all Perl modules required to run Nama in text mode: cpanm Audio::Nama However if you don't have cpanm, this should still work: PERL_MM_USE_DEFAULT=1 cpan Audio::Nama To use the GUI, you will need to install Tk: cpanm Tk You may optionally install Audio::Ecasound to run Ecasound via libecasoundc: cpanm Audio::Ecasound You can browse the sources or download a tarball via: http://search.cpan.org/dist/Audio-Nama =head2 Building from Source If you want to inspect or modify Nama's internals, or keep up with new developments, you can pull the source code as follows: git clone git://github.com/bolangi/nama.git Consult the F file for build instructions. =head2 Non-Perl Dependencies The Ecasound audio processing libraries should be installed. Ecasound should be compiled with support for LADSPA, libsndfile, libsamplerate and JACK. Ecasound may be obtained from http://ecasound.seul.org/ecasound/ or as precompiled binary package for your Un*x distribution. The LADSPA SDK is required to autosense LADSPA plugins and scaling hints. It is available at: http://www.ladspa.org/ladspa_sdk/download.html In particular the utility program 'analyseplugin' must be installed in a directory in your execution PATH. Nama's mastering mode uses a number of LADSPA plugins in a reasonably flat starting configuration. provided that the user installs the plugins listed in the default configuration file .namarc. Git is required to utilize Nama's project management and and undo features. =head2 COPYRIGHT Unless specified otherwise, this code is 2003 - 2014, copyright Joel Roth All rights are reserved except as provided by the Gnu Public License, version 3, as detailed in the file COPYING provided with this distribution. Audio-Nama-1.216/MYMETA.yml0000644000175000017500000000223213544212614014214 0ustar jrothjroth--- abstract: unknown author: - 'Joel Roth, ' build_requires: ExtUtils::MakeMaker: '6.59' configure_requires: ExtUtils::MakeMaker: '0' dynamic_config: 0 generated_by: 'ExtUtils::MakeMaker version 7.1, CPAN::Meta::Converter version 2.150001' license: open_source meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: Audio-Nama no_index: directory: - t - inc requires: AnyEvent: '5.0' AnyEvent::TermKey: '0' Data::Dumper::Concise: '0' Data::Section::Simple: '0' Event: '0' File::Copy: '0' File::Copy::Link: '0' File::Find::Rule: '0' File::HomeDir: '0' File::Slurp: '0' File::Temp: '0' Git::Repository: '0' Graph: '0' IO::Select: '0' IO::Socket: '0' IPC::Open3: '0' JSON::XS: '0' List::MoreUtils: '0' List::Util: '0' Log::Log4perl: '0' Modern::Perl: '0' Module::Load::Conditional: '0' Parse::RecDescent: '0' Role::Tiny: '0' SUPER: '0' Term::ReadLine::Gnu: '0' Test2::Bundle::More: '0' Text::Diff: '0' Text::Format: '0' Text::Template: '0' Time::HiRes: '0' Try::Tiny: '0' YAML::Tiny: '0' autodie: '0' perl: '5.010001' version: '1.216'