././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1685549294.7022057
hvcc-0.13.3/LICENSE 0000644 0000000 0000000 00000076405 14435670357 010506 0 ustar 00 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
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1739952989.4286463
hvcc-0.13.3/README.md 0000644 0000000 0000000 00000016130 14755311535 010740 0 ustar 00 # Heavy Compiler Collection (hvcc)
[](https://github.com/Wasted-Audio/hvcc/actions)
[](https://pypi.python.org/pypi/hvcc)
[](https://pypi.python.org/pypi/hvcc)
`hvcc` is a python-based dataflow audio programming language compiler that generates C/C++ code and a variety of specific framework wrappers.
## Background
Originaly created by Enzien Audio, the need for `hvcc` arose from running against performance limitations while creating interactive music and sound products for the iPhone. [Pure Data](https://puredata.info) (libpd) was the only real choice for a design tool as it was embeddable and provided a high enough abstraction level that musicians or sound designers could be creative.
The goal was to leverage Pure Data as a design interface and statically interpret the resultant patches to generate a low-level, portable and optimised C/C++ program that would be structured to take advantage of modern hardware whilst still generating the same behaviour and audio output as Pure Data.
It has since then been expanded to provide further support for many different platforms and frameworks, targeting game audio design, daw plugins and embedded production tools. In 2021 Wasted Audio took over maintenance of the project.
## Documentation
* [Introduction](/docs/01.introduction.md)
* [What is heavy?](/docs/01.introduction.md#what-is-heavy)
* [Supported patch formats](/docs/01.introduction.md#supported-patch-formats)
* [Supported platforms](/docs/01.introduction.md#supported-platforms)
* [Supported frameworks](/docs/01.introduction.md#supported-frameworks)
* [Licensing](/docs/01.introduction.md#licensing)
* [Getting Started](/docs/02.getting_started.md)
* [Generators](/docs/03.generators.md)
* [MIDI](/docs/04.midi.md)
* [C API](/docs/05.c.md)
* [C++ API](/docs/06.cpp.md)
* [Heavy Lang Info](/docs/07.heavy_lang.md)
* [Heavy IR Info](/docs/08.heavy_ir_lang.md)
* [Supported vanilla objects](/docs/09.supported_vanilla_objects.md)
* [Unsupported vanilla objects](/docs/10.unsupported_vanilla_objects.md)
## Integrations
hvcc has been integrated into several projects and services. This allows to easily compile patches without having to install hvcc manually.
* [plugdata](https://plugdata.org/) - Modern interface for Pure Data. Includes a full cross-platform toolchain and targets Daisy, DPF and PD Externals.
* [mod-cloud-builder](https://github.com/moddevices/mod-cloud-builder) - Online service for building LV2 plugins for the MOD platform.
* [OWL Patch Library](https://www.rebeltech.org/patch-library) - Online service for building OWL plugins (uses an old fork).
## Requirements
Python 3.8 up to 3.12
* `jinja2` (for generator templating)
* `importlib_resources` (for reading static resources)
* `json2daisy` (for daisy integration)
For tests:
* `tox` (python install)
* `numpy/scipy` (requirements-dev)
* `midifile` (git submodule)
* `tinywav` (git submodule)
* `clang/clang++` (system install)
## Installation
hvcc is available from pypi.org and can be installed using python3 pip:
`$ pip3 install hvcc`
If you want to develop hvcc you can install it from the source directory:
`$ git clone https://github.com/Wasted-Audio/hvcc.git`
`$ cd hvcc/`
`$ pip3 install -e .`
## Usage
`hvcc` requires at least one argument that determines the top-level patch file to be loaded.
Generate a C/C++ program from `input.pd` and place the files in `~/myProject/`
`$ hvcc ~/myProject/_main.pd`
This command will generate the following directories:
* `~/myProject/hv` heavylang representation of the input pd patch(es)
* `~/myProject/ir` heavyir representation of the heavylang patch
* `~/myProject/c` final generated C/C++ source files (this is what you would use in your project)
### `-o` Select output directory
As seen in the above command, typical output of `hvcc` is split into several directories that contain the intermediate files used by the compiler itself, the final generated source files, and any additional framework specific files and projects.
The `-o` or `--out_dir` parameter will specify where the output files are placed after a successful compile.
For example:
`$ hvcc ~/myProject/_main.pd -o ~/Desktop/somewhere/else/`
Will place all the generated files in `~/Desktop/somewhere/else/`.
### `-n` Specify Patch Name
The `-n` or `--name` parameter can be used to easily namespace the generated code so that there are no conflicts when integrating multiple patches into the same project.
`$ hvcc ~/myProject/_main.pd -o ~/Desktop/somewhere/else/ -n mySynth`
### `-g` Generators
Once `hvcc` has generated internal information about the patch the `-g` or `--gen` parameter can be used to specify the output files it should generate. By default it will always include `c` for the C/C++ source files and additional generators can specified for certain framework targets.
For example:
`$ hvcc ~/myProject/_main.pd -o ~/Desktop/somewhere/else/ -n mySynth -g unity`
Will also generate a `unity` section in the output directory contain all the build projects and source files to compile a Unity plugin.
It is also possible to pass a list of generators:
`$ hvcc ~/myProject/_main.pd -o ~/Desktop/somewhere/else/ -n mySynth -g unity wwise js`
A list of available generator options can be found [here](/docs/03.generators.md)
### `-p` Search Paths
`hvcc` will iterate through various directories when resolving patch objects and abstractions. The `-p` or `--search_paths` argument can be used to add additional folders for `hvcc` to look in. Note that this argument is not needed for abstractions in the same folder as the top-level patch.
This can be handy when using a third-party patch library like [heavylib](https://github.com/Wasted-Audio/heavylib) for example.
Simply append any folder paths after the `-p` flag like so:
`$ hvcc ~/myProject/_main.pd -o ~/Desktop/somewhere/else/ -n mySynth -p ~/Workspace/Projects/Enzien/heavylib/ ~/Desktop/myLib/`
### `-m` Meta Data
`hvcc` can take extra meta-data via a supplied json file. It depends on the generator which fields are supported.
### `--copyright` User Copyright
By default all the generated source files via `hvcc` will have the following copyright text applied to the top of the file:
`Copyright (c) 2018 Enzien Audio, Ltd.`
This can be changed with `--copyright` parameter
`$ hvcc ~/myProject/_main.pd -o ~/Desktop/somewhere/else/ -n mySynth --copyright "Copyright (c) Los Pollos Hermanos 2019"`
### `--help`
Displays all the available parameters and options for hvcc.
## Contact
There are several places where heavy/hvcc conversation is happening:
* [Discord](https://discord.gg/fmxJveg)
* [IRC](https://web.libera.chat/#hvcc)
* A number of forums:
* [Bela](https://forum.bela.io/?q=hvcc)
* [Rebel Technology](https://community.rebeltech.org/tags/puredata)
* [Daisy](https://forum.electro-smith.com/c/integrations/pure-data/32)
* [MOD](https://forum.moddevices.com/c/developers/pure-data/56)
Or you can use the [discussions](https://github.com/Wasted-Audio/hvcc/discussions) tab of this repository
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1739952822.569362
hvcc-0.13.3/hvcc/__init__.py 0000644 0000000 0000000 00000000126 14755311267 012515 0 ustar 00 from hvcc.compiler import compile_dataflow # noqa
from hvcc.main import main # noqa
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1739952822.569362
hvcc-0.13.3/hvcc/compiler.py 0000644 0000000 0000000 00000031135 14755311267 012574 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2021-2024 Wasted Audio
#
# 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 .
import importlib
import inspect
import json
import os
import re
import sys
from typing import Any, List, Dict, Optional
from hvcc.interpreters.pd2hv import pd2hv
from hvcc.core.hv2ir import hv2ir
from hvcc.generators.ir2c import ir2c
from hvcc.generators.ir2c import ir2c_perf
from hvcc.generators.c2js import c2js
from hvcc.generators.c2daisy import c2daisy
from hvcc.generators.c2dpf import c2dpf
from hvcc.generators.c2owl import c2owl
from hvcc.generators.c2pdext import c2pdext
from hvcc.generators.c2wwise import c2wwise
from hvcc.generators.c2unity import c2unity
from hvcc.types.compiler import (
CompilerResults, CompilerResp, CompilerNotif, CompilerMsg, Generator,
ExternInfo, ExternMemoryPool, ExternMidi, ExternEvents, ExternParams
)
from hvcc.types.IR import IRGraph
from hvcc.types.meta import Meta
def add_error(
results: CompilerResults,
error: str
) -> CompilerResults:
if "hvcc" in results.root:
results.root["hvcc"].notifs.errors.append(CompilerMsg(message=error))
else:
results.root["hvcc"] = CompilerResp(
stage="hvcc",
notifs=CompilerNotif(
has_error=True,
errors=[CompilerMsg(message=error)]
)
)
return results
def check_extern_name_conflicts(extern_type: str, extern_list: List, results: CompilerResults) -> None:
""" In most of the generator code extern names become capitalised when used
as enums. This method makes sure that there are no cases where two unique
keys become the same after being capitalised.
Note(joe): hvcc is probably the best place to check as at this point we
have a list of all extern names.
"""
for i, v in enumerate(extern_list):
for j, u in enumerate(extern_list[i + 1:]):
if v[0].upper() == u[0].upper():
add_error(results,
f"Conflicting {extern_type} names '{v[0]}' and '{u[0]}', make sure that "
"capital letters are not the only difference.")
def count_midi_objects(hvir: IRGraph) -> Dict[str, List[str]]:
in_midi = []
out_midi = []
midi_in_objs = [
'__hv_bendin',
'__hv_ctlin',
'__hv_midiin',
'__hv_midirealtimein',
'__hv_notein',
'__hv_pgmin',
'__hv_polytouchin',
'__hv_touchin',
]
midi_out_objs = [
'__hv_bendout',
'__hv_ctlout',
'__hv_midiout',
'__hv_midioutport',
'__hv_noteout',
'__hv_pgmout',
'__hv_polytouchout',
'__hv_touchout',
]
for recv in hvir.control.receivers.keys():
if recv in midi_in_objs:
in_midi.append(recv)
for msg in hvir.control.sendMessage:
if msg.name in midi_out_objs:
out_midi.append(msg.name)
return {
'in': in_midi,
'out': out_midi
}
def filter_midi_from_out_parameters(output_parameter_list: List, midi_out_objects: List) -> List:
new_out_list = []
for item in output_parameter_list:
if not item[0] in midi_out_objects:
new_out_list.append(item)
return new_out_list
def generate_extern_info(hvir: IRGraph, results: CompilerResults) -> ExternInfo:
""" Simplifies the receiver/send and table lists by only containing values
externed with @hv_param, @hv_event or @hv_table
"""
# Exposed input parameters
in_parameter_list = [(k, v) for k, v in hvir.control.receivers.items() if v.extern == "param"]
in_parameter_list.sort(key=lambda x: x[0])
check_extern_name_conflicts("input parameter", in_parameter_list, results)
# Exposed input events
in_event_list = [(k, v) for k, v in hvir.control.receivers.items() if v.extern == "event"]
in_event_list.sort(key=lambda x: x[0])
check_extern_name_conflicts("input event", in_event_list, results)
# Exposed output parameters
out_parameter_list = [(v.name, v) for v in hvir.control.sendMessage if v.extern == "param"]
# remove duplicate output parameters/events
# NOTE(joe): is the id argument important here? We'll only take the first one in this case.
out_parameter_list = list(dict(out_parameter_list).items())
out_parameter_list.sort(key=lambda x: x[0])
check_extern_name_conflicts("output parameter", out_parameter_list, results)
# Exposed output events
out_event_list = [(v.name, v) for v in hvir.control.sendMessage if v.extern == "event"]
out_event_list = list(dict(out_event_list).items())
out_event_list.sort(key=lambda x: x[0])
check_extern_name_conflicts("output event", out_event_list, results)
# Exposed tables
table_list = [(k, v) for k, v in hvir.tables.items() if v.extern]
table_list.sort(key=lambda x: x[0])
check_extern_name_conflicts("table", table_list, results)
# Exposed midi objects
midi_objects = count_midi_objects(hvir)
# filter midi objects from the output parameters list
out_parameter_list = filter_midi_from_out_parameters(out_parameter_list, midi_objects['out'])
return ExternInfo(
parameters=ExternParams(
inParam=in_parameter_list,
outParam=out_parameter_list
),
events=ExternEvents(
inEvent=in_event_list,
outEvent=out_event_list
),
midi=ExternMidi(
inMidi=midi_objects['in'],
outMidi=midi_objects['out']
),
tables=table_list,
# generate patch heuristics to ensure enough memory allocated for the patch
memoryPoolSizesKb=ExternMemoryPool(
internal=10, # TODO(joe): should this increase if there are a lot of internal connections?
inputQueue=max(2, int(
len(in_parameter_list) +
(len(in_event_list) / 4) +
len(midi_objects['in']) # TODO(dreamer): should this depend on the MIDI type?
)),
outputQueue=max(2, int(
len(out_parameter_list) +
(len(out_event_list) / 4) +
len(midi_objects['out'])
))
)
)
def load_ext_generator(module_name: str, verbose: bool) -> Optional[Generator]:
try:
module = importlib.import_module(module_name)
for _, member in inspect.getmembers(module):
if inspect.isclass(member) and not inspect.isabstract(member) and issubclass(member, Generator):
return member()
if verbose:
print(f"---> Module {module_name} does not contain a class derived from hvcc.types.Compiler")
return None
except ModuleNotFoundError:
return None
def compile_dataflow(
in_path: str,
out_dir: str,
patch_name: str = "heavy",
patch_meta_file: Optional[str] = None,
search_paths: Optional[List[str]] = None,
generators: Optional[List[str]] = None,
ext_generators: Optional[List[str]] = None,
verbose: bool = False,
copyright: Optional[str] = None,
nodsp: Optional[bool] = False
) -> CompilerResults:
results = CompilerResults(root={})
patch_meta = Meta()
# basic error checking on input
if os.path.isfile(in_path):
if not in_path.endswith((".pd")):
return add_error(results, "Can only process Pd files.")
elif os.path.isdir(in_path):
if not os.path.basename("c"):
return add_error(results, "Can only process c directories.")
else:
return add_error(results, f"Unknown input path {in_path}")
# meta-data file
if patch_meta_file:
if os.path.isfile(patch_meta_file):
with open(patch_meta_file) as json_file:
try:
patch_meta_json = json.load(json_file)
patch_meta = Meta(**patch_meta_json)
except Exception as e:
return add_error(results, f"Unable to open json_file: {e}")
patch_name = patch_meta.name or patch_name
generators = ["c"] if generators is None else [x.lower() for x in generators]
if verbose:
print("--> Generating C")
results.root["pd2hv"] = pd2hv.pd2hv.compile(
pd_path=in_path,
hv_dir=os.path.join(out_dir, "hv"),
search_paths=search_paths,
verbose=verbose)
# check for errors
response: CompilerResp = list(results.root.values())[0]
if response.notifs.has_error:
return results
subst_name = re.sub(r'\W', '_', patch_name)
results.root["hv2ir"] = hv2ir.hv2ir.compile(
hv_file=os.path.join(response.out_dir, response.out_file),
# ensure that the ir filename has no funky characters in it
ir_file=os.path.join(out_dir, "ir", f"{subst_name}.heavy.ir.json"),
patch_name=patch_name,
verbose=verbose)
# check for errors
if results.root["hv2ir"].notifs.has_error:
return results
# get the hvir data
hvir = results.root["hv2ir"].ir
assert hvir is not None
patch_name = hvir.name.escaped
externs = generate_extern_info(hvir, results)
# get application path
if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'):
application_path = os.path.join(sys._MEIPASS, 'hvcc')
elif __file__:
application_path = os.path.dirname(__file__)
c_src_dir = os.path.join(out_dir, "c")
results.root["ir2c"] = ir2c.ir2c.compile(
hv_ir_path=os.path.join(results.root["hv2ir"].out_dir, results.root["hv2ir"].out_file),
static_dir=os.path.join(application_path, "generators/ir2c/static"),
output_dir=c_src_dir,
externs=externs,
copyright=copyright,
nodsp=nodsp)
# check for errors
if results.root["ir2c"].notifs.has_error:
return results
# ir2c_perf
results.root["ir2c_perf"] = CompilerResp(
stage="ir2c_perf",
obj_perf=ir2c_perf.ir2c_perf.perf(hvir, verbose=verbose),
in_dir=results.root["hv2ir"].out_dir,
in_file=results.root["hv2ir"].out_file,
)
# run the c2x generators, merge the results
num_input_channels = hvir.signal.numInputBuffers
num_output_channels = hvir.signal.numOutputBuffers
gen_args: Dict[str, Any] = {
'c_src_dir': c_src_dir,
'out_dir': out_dir,
'patch_name': patch_name,
'patch_meta': patch_meta,
'num_input_channels': num_input_channels,
'num_output_channels': num_output_channels,
'externs': externs,
'copyright': copyright,
'verbose': verbose
}
if "js" in generators:
if verbose:
print("--> Generating Javascript")
results.root["c2js"] = c2js.c2js.compile(**gen_args)
if "daisy" in generators:
if verbose:
print("--> Generating Daisy module")
results.root["c2daisy"] = c2daisy.c2daisy.compile(**gen_args)
if "dpf" in generators:
if verbose:
print("--> Generating DPF plugin")
results.root["c2dpf"] = c2dpf.c2dpf.compile(**gen_args)
if "owl" in generators:
if verbose:
print("--> Generating OWL plugin")
results.root["c2owl"] = c2owl.c2owl.compile(**gen_args)
if "pdext" in generators:
if verbose:
print("--> Generating Pd external")
results.root["c2pdext"] = c2pdext.c2pdext.compile(**gen_args)
if "unity" in generators:
if verbose:
print("--> Generating Unity plugin")
results.root["c2unity"] = c2unity.c2unity.compile(**gen_args)
if "wwise" in generators:
if verbose:
print("--> Generating Wwise plugin")
results.root["c2wwise"] = c2wwise.c2wwise.compile(**gen_args)
if ext_generators:
for module_name in ext_generators:
generator = load_ext_generator(module_name, verbose)
if generator is not None:
if verbose:
print(f"--> Executing custom generator from module {module_name}")
results.root[module_name] = generator.compile(**gen_args)
return results
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1685549294.7462065
hvcc-0.13.3/hvcc/core/__init__.py 0000644 0000000 0000000 00000000000 14435670357 013437 0 ustar 00 ././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1727976067.2595525
hvcc-0.13.3/hvcc/core/hv2ir/BufferPool.py 0000644 0000000 0000000 00000010215 14677551203 015002 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023 Wasted Audio
#
# 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 .
from collections import defaultdict
from typing import Dict, List, Optional, Tuple
from .HeavyException import HeavyException
class BufferPool:
def __init__(self) -> None:
# the idea is that the same buffer is reused as quickly as possible so that it doesn't need
# to be moved around in the cache. It does not give substantially different results
# from a Counter-based implementation, but it is more consistent and predictable.
self.pool: Dict = {
"~f>": defaultdict(list),
"~i>": defaultdict(list)
}
def num_buffers(self, connection_type: Optional[str] = None) -> int:
""" Returns the number of buffers with the given retain count. By default returns
the size of the entire pool. Number of buffers per connection type can also be retrieved.
"""
if connection_type is None:
return self.num_buffers("~f>") + self.num_buffers("~i>")
elif connection_type in self.pool:
return sum(len(v) for v in self.pool[connection_type].values())
else:
raise HeavyException(f"Unknown connection type: \"{connection_type}\"")
def get_buffer(self, connection_type: str, count: int = 1, excludeSet: Optional[set] = None) -> Tuple[str, int]:
""" Returns a currently unused buffer. The buffer can be assigned a retain count. An optional
exclude set can also be supplied, ensuring that the returned buffer is not one of them.
"""
excludeSet = excludeSet if excludeSet is not None else set()
pool = self.pool[connection_type]
# get the most recently used, unused buffer
b = next((b for b in reversed(pool[0]) if b not in excludeSet), None)
if b is not None:
pool[0].remove(b)
else:
# if we get here, then no available buffer was found. Create a new one.
b = (connection_type, self.num_buffers(connection_type)) # new buffer index for the given type
pool[count].append(b)
return b
def retain_buffer(self, b: List, count: int = 1) -> int:
""" Increases the retain count of the buffer. Returns the new count.
"""
# adc~ and ZERO_BUFFER are special. They cannot be retained.
if b[0] in {"zero", "input"}:
return 0
else:
pool = self.pool[b[0]]
for k, v in pool.items():
if b in v:
v.remove(b)
pool[k + count].append(b)
return k + count # return the new retain count
raise HeavyException(f"{b} not found in BufferPool!")
def release_buffer(self, b: List, count: int = 1) -> int:
""" Reduces the retain count of the buffer. Returns the new count.
"""
# adc~, ZERO_BUFFER, send~ buffers are special. They can not be released.
# if the buffer is otherwise unknown (as may be in the case that objects provide their own),
# they cannot be released
if b[0] in {"zero", "input"}:
return 0
else:
pool = self.pool[b[0]]
for k, v in pool.items():
if b in v:
v.remove(b)
pool[k - count].append(b)
return k - count # return the new retain count
raise HeavyException(f"{b} not found in BufferPool!")
def __repr__(self) -> str:
return self.pool["~f>"].__repr__() + self.pool["~i>"].__repr__()
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1727976067.2595525
hvcc-0.13.3/hvcc/core/hv2ir/Connection.py 0000644 0000000 0000000 00000006213 14677551203 015041 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023 Wasted Audio
#
# 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 .
from typing import Optional, TYPE_CHECKING
if TYPE_CHECKING:
from .HeavyIrObject import HeavyIrObject
class Connection:
""" A Connection describes a connection between two objects.
"""
def __init__(
self,
from_object: 'HeavyIrObject',
outlet_index: int,
to_object: 'HeavyIrObject',
inlet_index: int,
conn_type: str
) -> None:
self.from_object = from_object
self.outlet_index = outlet_index
self.to_object = to_object
self.inlet_index = inlet_index
self.type = conn_type
# cache the hash of this object
self.__hash = hash((
self.from_object, self.outlet_index,
self.to_object, self.inlet_index,
self.type))
def copy(
self,
from_object: Optional['HeavyIrObject'] = None,
outlet_index: Optional[int] = None,
to_object: Optional['HeavyIrObject'] = None,
inlet_index: Optional[int] = None,
type: Optional[str] = None
) -> 'Connection':
""" Create a new connection based on the existing one, changing the given values.
"""
return Connection(from_object=self.from_object if from_object is None else from_object,
outlet_index=self.outlet_index if outlet_index is None else outlet_index,
to_object=self.to_object if to_object is None else to_object,
inlet_index=self.inlet_index if inlet_index is None else inlet_index,
conn_type=self.type if type is None else type)
@property
def is_signal(self) -> bool:
return self.is_signal_type(self.type)
@property
def is_control(self) -> bool:
return self.type == "-->"
@property
def is_float_signal(self) -> bool:
return self.type == "~f>"
@property
def is_integer_signal(self) -> bool:
return self.type == "~i>"
@property
def is_mixed(self) -> bool:
return self.type == "-~>"
@classmethod
def is_signal_type(cls, type: Optional[str]) -> bool:
return type in {"~i>", "~f>"}
def __eq__(self, other: object) -> bool:
return self.__hash == other.__hash__() if isinstance(other, Connection) else False
def __hash__(self) -> int:
return self.__hash
def __repr__(self) -> str:
return f"[{self.from_object}:{self.outlet_index}] {self.type} [{self.to_object}:{self.inlet_index}]"
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1735754044.258823
hvcc-0.13.3/hvcc/core/hv2ir/HIrConvolution.py 0000644 0000000 0000000 00000003272 14735300474 015663 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023-2024 Wasted Audio
#
# 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 .
from typing import Dict, Optional, List, Set, Tuple
from .HeavyIrObject import HeavyIrObject
from .HeavyGraph import HeavyGraph
class HIrConvolution(HeavyIrObject):
""" __conv~f
"""
def __init__(
self,
obj_type: str,
args: Optional[Dict] = None,
graph: Optional[HeavyGraph] = None,
annotations: Optional[Dict] = None
) -> None:
assert obj_type == "__conv~f"
super().__init__(obj_type, args=args, graph=graph, annotations=annotations)
def reduce(self) -> Optional[Tuple[Set, List]]:
if self.graph is not None:
table_obj = self.graph.resolve_object_for_name(
self.args["table"],
["table", "__table"])
if table_obj is not None:
self.args["table_id"] = table_obj.id
return ({self}, [])
else:
self.add_error(f"Cannot find table named \"{self.args['table']}\" for object {self}.")
return None
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1735754044.258823
hvcc-0.13.3/hvcc/core/hv2ir/HIrInlet.py 0000644 0000000 0000000 00000004004 14735300474 014411 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023-2024 Wasted Audio
#
# 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 .
from typing import Dict, Optional
from .HeavyException import HeavyException
from .HeavyIrObject import HeavyIrObject
from .HeavyGraph import HeavyGraph
from hvcc.types.Lang import LangLetType
class HIrInlet(HeavyIrObject):
""" A specific implementation of the inlet object.
"""
def __init__(
self,
obj_type: str,
args: Optional[Dict] = None,
graph: Optional[HeavyGraph] = None,
annotations: Optional[Dict] = None
) -> None:
super().__init__("__inlet", args=args, graph=graph, annotations=annotations)
def _resolved_outlet_type(self, outlet_index: int = 0) -> Optional[LangLetType]:
if self.graph is not None:
connections = self.graph.inlet_connections[self.args["index"]]
connection_type_set = {c.type for c in connections}
if len(connection_type_set) == 0:
# object has no incident connections.
return "-->" # outlet type defaults to control (-->)
elif len(connection_type_set) == 1:
return list(connection_type_set)[0]
else:
raise HeavyException(
f"{self} has multiple incident connections of differing type. "
"The outlet type cannot be explicitly resolved.")
return None
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1727976067.2605526
hvcc-0.13.3/hvcc/core/hv2ir/HIrLorenz.py 0000644 0000000 0000000 00000002267 14677551203 014623 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023 Wasted Audio
#
# 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 .
from typing import Dict, Optional
from .HeavyIrObject import HeavyIrObject
from .HeavyGraph import HeavyGraph
class HIrLorenz(HeavyIrObject):
""" __lorenz~f
"""
def __init__(
self,
obj_type: str,
args: Optional[Dict] = None,
graph: Optional[HeavyGraph] = None,
annotations: Optional[Dict] = None
) -> None:
assert obj_type == "__lorenz~f"
super().__init__(obj_type, args=args, graph=graph, annotations=annotations)
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1735754044.258823
hvcc-0.13.3/hvcc/core/hv2ir/HIrOutlet.py 0000644 0000000 0000000 00000003007 14735300474 014614 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023-2024 Wasted Audio
#
# 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 .
from typing import Dict, List, Optional
from .HeavyIrObject import HeavyIrObject
from .HeavyGraph import HeavyGraph
from hvcc.types.IR import IROnMessage
class HIrOutlet(HeavyIrObject):
""" A specific implementation of the outlet object.
"""
def __init__(
self,
obj_type: str,
args: Optional[Dict] = None,
graph: Optional[HeavyGraph] = None,
annotations: Optional[Dict] = None
) -> None:
super().__init__("__outlet", args=args, graph=graph, annotations=annotations)
def get_ir_on_message(self, inlet_index: int = 0) -> List[IROnMessage]:
x = []
if self.graph is not None:
for c in self.graph.outlet_connections[self.args["index"]]:
x.extend(c.to_object.get_ir_on_message(c.inlet_index))
return x
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1727976067.2605526
hvcc-0.13.3/hvcc/core/hv2ir/HIrPack.py 0000644 0000000 0000000 00000002527 14677551203 014227 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023 Wasted Audio
#
# 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 .
from typing import Dict, Optional
from .HeavyIrObject import HeavyIrObject
from .HeavyGraph import HeavyGraph
class HIrPack(HeavyIrObject):
def __init__(
self,
obj_type: str,
args: Optional[Dict] = None,
graph: Optional[HeavyGraph] = None,
annotations: Optional[Dict] = None
) -> None:
if args is not None:
num_inlets = len(args["values"])
else:
num_inlets = 0
super().__init__("__pack", args=args, graph=graph,
num_inlets=num_inlets,
num_outlets=1,
annotations=annotations)
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1735754044.258823
hvcc-0.13.3/hvcc/core/hv2ir/HIrReceive.py 0000644 0000000 0000000 00000003245 14735300474 014726 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023-2024 Wasted Audio
#
# 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 .
import re
from typing import Dict, Optional, TYPE_CHECKING
from .HeavyIrObject import HeavyIrObject
if TYPE_CHECKING:
from .HeavyGraph import HeavyGraph
class HIrReceive(HeavyIrObject):
""" A specific implementation of the __receive object.
"""
def __init__(
self,
obj_type: str,
args: Optional[Dict] = None,
graph: Optional['HeavyGraph'] = None,
annotations: Optional[Dict] = None
) -> None:
super().__init__("__receive", args=args, graph=graph, annotations=annotations)
if args is not None and args["extern"]:
# externed receivers must contain only alphanumeric characters or underscores,
# so that the names can be easily and transparently turned into code
if re.search(r"\W", args["name"]):
self.add_error(f"Parameter and Event names may only contain \
alphanumeric characters or underscore: '{args['name']}'")
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1735754044.258823
hvcc-0.13.3/hvcc/core/hv2ir/HIrSend.py 0000644 0000000 0000000 00000004663 14735300474 014242 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023-2024 Wasted Audio
#
# 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 .
import re
from typing import Dict, List, Optional
from .HeavyIrObject import HeavyIrObject
from .HeavyGraph import HeavyGraph
from hvcc.types.IR import IRSendMessage
class HIrSend(HeavyIrObject):
""" A specific implementation of the __send object.
"""
def __init__(
self,
obj_type: str,
args: Optional[Dict] = None,
graph: Optional[HeavyGraph] = None,
annotations: Optional[Dict] = None
) -> None:
super().__init__("__send", args=args, graph=graph, annotations=annotations)
if args is not None and args["extern"]:
# output parameters must contain only alphanumeric characters or underscores,
# so that the names can be easily and transparently turned into code
if re.search(r"\W", args["name"]):
self.add_error(f"Parameter and Event names may only contain \
alphanumeric characters or underscore: '{args['name']}'")
def get_ir_control_list(self) -> List[IRSendMessage]:
if self.graph is not None and self.name is not None:
receive_objs = self.graph.resolve_objects_for_name(self.name, "__receive")
on_message_list = [x for o in receive_objs for x in o.get_ir_on_message(inlet_index=0)]
return [IRSendMessage(
id=self.id,
type="send",
onMessage=[on_message_list],
extern=self.args["extern"],
attributes=self.args["attributes"],
hash=self.args["hash"],
display=self.args["name"],
name=((f"_{self.args['name']}") if re.match(r"\d", self.args["name"]) else self.args["name"])
)]
else:
return []
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1727976067.2615526
hvcc-0.13.3/hvcc/core/hv2ir/HIrSwitchcase.py 0000644 0000000 0000000 00000002733 14677551203 015445 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023 Wasted Audio
#
# 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 .
from typing import Dict, Optional
from .HeavyIrObject import HeavyIrObject
from .HeavyGraph import HeavyGraph
class HIrSwitchcase(HeavyIrObject):
""" A specific implementation of the __switchcase object.
"""
def __init__(
self,
obj_type: str,
args: Optional[Dict] = None,
graph: Optional[HeavyGraph] = None,
annotations: Optional[Dict] = None
) -> None:
if args is not None:
num_cases = len(args["cases"])
else:
num_cases = 0
super().__init__("__switchcase",
args=args,
graph=graph,
num_inlets=1,
num_outlets=num_cases + 1,
annotations=annotations)
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1735754044.258823
hvcc-0.13.3/hvcc/core/hv2ir/HIrTabhead.py 0000644 0000000 0000000 00000003313 14735300474 014670 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023-2024 Wasted Audio
#
# 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 .
from typing import Dict, Optional, List, Set, Tuple
from .HeavyIrObject import HeavyIrObject
from .HeavyGraph import HeavyGraph
class HIrTabhead(HeavyIrObject):
""" __tabhead~f and __tabhead
"""
def __init__(
self,
obj_type: str,
args: Optional[Dict] = None,
graph: Optional[HeavyGraph] = None,
annotations: Optional[Dict] = None
) -> None:
assert obj_type in {"__tabhead~f", "__tabhead"}
super().__init__(obj_type, args=args, graph=graph, annotations=annotations)
def reduce(self) -> Optional[Tuple[Set, List]]:
if self.graph is not None:
table_obj = self.graph.resolve_object_for_name(
self.args["table"],
["table", "__table"])
if table_obj is not None:
self.args["table_id"] = table_obj.id
return ({self}, [])
else:
self.add_error(f"Can't find table with name \"{self.args['table']}\".")
return None
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1735754044.2598228
hvcc-0.13.3/hvcc/core/hv2ir/HIrTabread.py 0000644 0000000 0000000 00000003405 14735300474 014704 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023-2024 Wasted Audio
#
# 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 .
from typing import Dict, Optional, List, Set, Tuple
from .HeavyIrObject import HeavyIrObject
from .HeavyGraph import HeavyGraph
class HIrTabread(HeavyIrObject):
""" __tabread~if
"""
def __init__(
self,
obj_type: str,
args: Optional[Dict] = None,
graph: Optional[HeavyGraph] = None,
annotations: Optional[Dict] = None
) -> None:
assert obj_type in {"__tabread~if", "__tabread~f", "__tabread_stoppable~f", "__tabreadu~f", "__tabread"}
super().__init__(obj_type, args=args, graph=graph, annotations=annotations)
def reduce(self) -> Optional[Tuple[Set, List]]:
if self.graph is not None:
table_obj = self.graph.resolve_object_for_name(
self.args["table"],
["table", "__table"])
if table_obj is not None:
self.args["table_id"] = table_obj.id
return ({self}, [])
else:
self.add_error(f"Cannot find table named \"{self.args['table']}\" for object {self}.")
return None
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1735754044.2598228
hvcc-0.13.3/hvcc/core/hv2ir/HIrTabwrite.py 0000644 0000000 0000000 00000003332 14735300474 015122 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023-2024 Wasted Audio
#
# 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 .
from typing import Dict, Optional, List, Set, Tuple
from .HeavyIrObject import HeavyIrObject
from .HeavyGraph import HeavyGraph
class HIrTabwrite(HeavyIrObject):
""" __tabwrite~f
"""
def __init__(
self,
obj_type: str,
args: Optional[Dict] = None,
graph: Optional[HeavyGraph] = None,
annotations: Optional[Dict] = None
) -> None:
assert obj_type in {"__tabwrite~f", "__tabwrite_stoppable~f", "__tabwrite"}
super().__init__(obj_type, args=args, graph=graph, annotations=annotations)
def reduce(self) -> Optional[Tuple[Set, List]]:
if self.graph is not None:
table_obj = self.graph.resolve_object_for_name(
self.args["table"],
["table", "__table"])
if table_obj is not None:
self.args["table_id"] = table_obj.id
return ({self}, [])
else:
self.add_error(f"Can't find table with name \"{self.args['table']}\".")
return None
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1735754044.2598228
hvcc-0.13.3/hvcc/core/hv2ir/HLangAdc.py 0000644 0000000 0000000 00000004322 14735300474 014337 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023-2024 Wasted Audio
#
# 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 .
from typing import Optional, Dict
from .HeavyIrObject import HeavyIrObject
from .HeavyLangObject import HeavyLangObject
from .HeavyGraph import HeavyGraph
from hvcc.types.Lang import LangLetType
class HLangAdc(HeavyLangObject):
""" adc
"""
def __init__(
self,
obj_type: str,
args: Dict,
graph: 'HeavyGraph',
annotations: Optional[Dict] = None
) -> None:
assert obj_type == "adc"
super().__init__(obj_type, args, graph,
num_inlets=0,
num_outlets=len(args[self._HEAVY_LANG_DICT[obj_type].args[0].name]),
annotations=annotations)
def _resolved_outlet_type(self, outlet_index: int = 0) -> LangLetType:
return "~f>"
def reduce(self) -> tuple:
objects = set()
connections = []
# reduce a HeavyLang adc to a number of individual HeavyIR __inlet objects
for i, channel_index in enumerate(self.args["channels"]):
if len(self.outlet_connections[i]) > 0: # if there are any connections to this inlet
x = HeavyIrObject("__inlet", args={
"index": 127 + channel_index
})
x.outlet_buffers[0] = ("input", channel_index - 1) # channel indicies are one-indexed
objects.add(x)
for c in self.outlet_connections[i]:
connections.append((c, [c.copy(from_object=x, outlet_index=0)]))
return (objects, connections)
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1727976067.2615526
hvcc-0.13.3/hvcc/core/hv2ir/HLangBinop.py 0000644 0000000 0000000 00000025160 14677551203 014725 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023 Wasted Audio
#
# 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 .
from typing import Optional, Dict
from .Connection import Connection
from .HeavyLangObject import HeavyLangObject
from .HeavyIrObject import HeavyIrObject
from .HeavyGraph import HeavyGraph
class HLangBinop(HeavyLangObject):
__HEAVY_DICT = {
"+": ["__add", "__add_k", "__add~f", "__add~i"],
"-": ["__sub", "__sub_k", "__sub~f", "__sub~i"],
"*": ["__mul", "__mul_k", "__mul~f", "__mul~i"],
"/": ["__div", "__div_k", "__div~f", "__div~i"],
"max": ["__max", "__max_k", "__max~f", "__max~i"],
"min": ["__min", "__min_k", "__min~f", "__min~i"],
">": ["__gt", "__gt_k", "__gt~f", "__gt~i"],
">=": ["__gte", "__gte_k", "__gte~f", "__gte~i"],
"==": ["__eq", "__eq_k", "__eq~f", "__eq~i"],
"!=": ["__neq", "__neq_k", "__neq~f", "__neq~i"],
"<": ["__lt", "__lt_k", "__lt~f", "__lt~i"],
"<=": ["__lte", "__lte_k", "__lte~f", "__lte~i"],
"&": ["__and", "__and_k", "__and~f", "__and~i"], # binary and
"&&": ["__logand", "__logand_k"], # logical or
"&!": ["__andnot", "__andnot~f", "__andnot~i"],
"|": ["__or", "__or_k", "__or~f", "__or~i"], # binary or
"||": ["__logor", "__logor_k"], # logical or
"pow": ["__pow", "__pow_k", "__pow~f", "__pow~i"],
"atan2": ["__atan2", "__atan2_k", "__atan2~f"],
"mod": ["__unimod", "__unimod_k"],
"%": ["__bimod", "__bimod_k"],
">>": ["__shiftright", "__shiftright_k"], # binary right shift
"<<": ["__shiftleft", "__shiftleft_k"] # binary left shift
}
def __init__(
self,
obj_type: str,
args: Optional[Dict] = None,
graph: Optional['HeavyGraph'] = None,
annotations: Optional[Dict] = None
) -> None:
super().__init__(obj_type, args, graph,
num_inlets=2,
num_outlets=1,
annotations=annotations)
@classmethod
def handles_type(cls, obj_type: str) -> bool:
"""Returns True if this class handles the given object type. False otherwise.
"""
return obj_type in HLangBinop.__HEAVY_DICT
def reduce(self) -> Optional[tuple]:
if self.has_inlet_connection_format("__") or \
self.has_inlet_connection_format("_c") or \
self.has_inlet_connection_format("_f") or \
self.has_outlet_connection_format("_"):
# binary operator objects must have a left inlet or outlet connection.
return (set(), []) # remove this object
if self.has_inlet_connection_format("ff"):
ir_type = [x for x in HLangBinop.__HEAVY_DICT[self.type] if x.endswith("~f")][0]
x = HeavyIrObject(ir_type)
return ({x}, self.get_connection_move_list(x))
elif self.has_inlet_connection_format("fc"):
ir_type = [x for x in HLangBinop.__HEAVY_DICT[self.type] if x.endswith("~f")][0]
x = HeavyIrObject(ir_type)
y = HeavyIrObject("__var~f", {"k": self.args[self.name_for_arg()]})
z = HeavyIrObject("__varread~f", {"var_id": y.id})
connections = [(None, [Connection(z, 0, x, 1, "~f>")])]
for c in self.inlet_connections[0]: # left inlet to ir_type
connections.append((c, [c.copy(to_object=x)]))
for c in self.inlet_connections[1]: # right inlet to __var~f
connections.append((c, [c.copy(to_object=y, inlet_index=0)]))
for c in self.outlet_connections[0]: # ir_type outlet
connections.append((c, [c.copy(from_object=x)]))
return ({x, y, z}, connections)
elif self.has_inlet_connection_format("f_"):
ir_type = [x for x in HLangBinop.__HEAVY_DICT[self.type] if x.endswith("~f")][0]
# handle identity-operations
if (self.type == "+" and self.args["k"] == 0.0) or \
(self.type == "-" and self.args["k"] == 0.0) or \
(self.type == "*" and self.args["k"] == 1.0) or \
(self.type == "/" and self.args["k"] == 1.0):
if len(self.inlet_connections[0]) == 0:
return (set(), []) # remove this object
elif len(self.inlet_connections[0]) == 1:
# there is only one connection, pass through the connection
# and remove this object
c = self.inlet_connections[0][0]
connections = [(c, [c.copy(to_object=v.to_object, inlet_index=v.inlet_index) for
v in self.outlet_connections[0]])]
return (set(), connections)
else: # len(self.inlet_connections[0]) > 1
# there are multiple connections to the left inlet
# create a __add~f, move one connection to the left inlet
# and the remainder to the right inlet
x = HeavyIrObject("__add~f")
c = self.inlet_connections[0][0]
connections = [(c, [c.copy(to_object=x, inlet_index=0)])]
for c in self.inlet_connections[0][1:]:
connections.append((c, [c.copy(to_object=x, inlet_index=1)]))
for c in self.outlet_connections[0]:
connections.append((c, [c.copy(from_object=x, outlet_index=0)]))
return ({x}, connections)
if self.type == "*":
# self.args["k"] == 1.0 case handled above
if self.args["k"] == 0.0:
# this object does nothing at all. Remove it.
return (set(), [])
if self.args["k"] == -1.0:
x = HeavyIrObject("__neg~f")
return ({x}, self.get_connection_move_list(x))
x = HeavyIrObject(ir_type)
# add constant generator
y = HeavyIrObject(
"__var_k~f",
args={"k": self.args[self.name_for_arg()]})
connections = self.get_connection_move_list(x)
connections.append((None, [Connection(y, 0, x, 1, "~f>")]))
return ({x, y}, connections)
elif self.has_inlet_connection_format("ii"):
ir_type = [x for x in HLangBinop.__HEAVY_DICT[self.type] if x.endswith("~i")][0]
x = HeavyIrObject(ir_type)
return ({x}, self.get_connection_move_list(x))
elif self.has_inlet_connection_format("ic"):
ir_type = [x for x in HLangBinop.__HEAVY_DICT[self.type] if x.endswith("~i")][0]
x = HeavyIrObject(ir_type)
y = HeavyIrObject("__var~i", {"k": self.args[self.name_for_arg()]})
z = HeavyIrObject("__varread~i", {"var_id": y.id})
connections = [(None, [Connection(z, 0, x, 1, "~f>")])]
for c in self.inlet_connections[0]:
connections.append((c, [c.copy(to_object=x)]))
for c in self.inlet_connections[1]:
connections.append((c, [c.copy(to_object=y, inlet_index=0)]))
for c in self.outlet_connections[0]:
connections.append((c, [c.copy(from_object=x)]))
return ({x, y, z}, connections)
elif self.has_inlet_connection_format("i_"):
ir_type = [x for x in HLangBinop.__HEAVY_DICT[self.type] if x.endswith("~i")][0]
x = HeavyIrObject(ir_type)
# add constant generator
y = HeavyIrObject(
"__var_k~i",
args={"k": self.args[self.name_for_arg()]})
connections = [(None, [Connection(y, 0, x, 1, "~i>")])]
for c in self.inlet_connections[0]:
connections.append((c, [c.copy(to_object=x)]))
for c in self.inlet_connections[1]:
connections.append((c, [c.copy(to_object=y, inlet_index=0)]))
for c in self.outlet_connections[0]:
connections.append((c, [c.copy(from_object=x)]))
return ({x, y}, connections)
elif self.has_inlet_connection_format("cc"):
ir_type = HLangBinop.__HEAVY_DICT[self.type][0]
# standardise the operation's constant argument name to "k"
args = {"k": self.args[self.name_for_arg()]}
x = HeavyIrObject(ir_type, args)
return ({x}, self.get_connection_move_list(x))
elif self.has_inlet_connection_format("c_"):
# if the right inlet is empty, generate a constant variation of the object
ir_type = [x for x in HLangBinop.__HEAVY_DICT[self.type] if x.endswith("_k")][0]
# standardise the operation's constant argument name to "k"
args = {"k": self.args[self.name_for_arg()]}
x = HeavyIrObject(ir_type, args)
return ({x}, self.get_connection_move_list(x))
elif self.has_inlet_connection_format("cf"):
ir_type = [x for x in HLangBinop.__HEAVY_DICT[self.type] if x.endswith("~f")][0]
x = HeavyIrObject(ir_type)
v = HeavyIrObject("__var~f")
vr = HeavyIrObject("__varread~f", args={"var_id": v.id})
obj_set = set([x, v, vr])
connections = [(None, [Connection(vr, 0, x, 0, "~f>")])]
for c in self.inlet_connections[0]:
connections.append((c, [c.copy(to_object=v, inlet_index=0)]))
for c in self.inlet_connections[1]:
connections.append((c, [c.copy(to_object=x, inlet_index=1)]))
for c in self.outlet_connections[0]:
connections.append((c, [c.copy(from_object=x, outlet_index=0)]))
return (obj_set, connections)
else:
fmt = self._get_connection_format(self.inlet_connections)
if "m" in fmt:
self.add_error(
"A binary operator cannot have both a signal and control "
f"connection to the same inlet: {fmt}")
else:
self.add_error(f"Unknown inlet configuration: {fmt}")
return None
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1727976067.2615526
hvcc-0.13.3/hvcc/core/hv2ir/HLangBiquad.py 0000644 0000000 0000000 00000004007 14677551203 015060 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023 Wasted Audio
#
# 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 .
import re
from typing import Optional, Dict
from .HeavyException import HeavyException
from .HeavyIrObject import HeavyIrObject
from .HeavyLangObject import HeavyLangObject
from .HeavyGraph import HeavyGraph
class HLangBiquad(HeavyLangObject):
""" Translates HeavyLang object biquad to HeavyIR biquad~.
"""
# used to detect valid connection configuration formats
__re_fmt_k = re.compile("[f_][c_]+")
__re_fmt_f = re.compile("[f_]+")
def __init__(
self,
obj_type: str,
args: Dict,
graph: 'HeavyGraph',
annotations: Optional[Dict] = None
) -> None:
assert obj_type == "biquad"
super().__init__(obj_type, args, graph,
num_inlets=6,
num_outlets=1,
annotations=annotations)
def reduce(self) -> tuple:
fmt = self._get_connection_format(self.inlet_connections)
if self.__re_fmt_k.search(fmt):
x = HeavyIrObject("__biquad_k~f", self.args)
return {x}, self.get_connection_move_list(x)
elif self.__re_fmt_f.search(fmt):
x = HeavyIrObject("__biquad~f", self.args)
return {x}, self.get_connection_move_list(x)
else:
raise HeavyException(f"Unsupported connection format to biquad: {fmt}")
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1735754044.2598228
hvcc-0.13.3/hvcc/core/hv2ir/HLangDac.py 0000644 0000000 0000000 00000004151 14735300474 014337 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023-2024 Wasted Audio
#
# 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 .
from typing import Optional, Dict
from .HeavyIrObject import HeavyIrObject
from .HeavyLangObject import HeavyLangObject
from .HeavyGraph import HeavyGraph
class HLangDac(HeavyLangObject):
""" Translates HeavyLang [dac] to HeavyIR [__add~f].
"""
def __init__(
self,
obj_type: str,
args: Dict,
graph: 'HeavyGraph',
annotations: Optional[Dict] = None
) -> None:
assert obj_type == "dac"
super().__init__(obj_type, args, graph,
num_inlets=len(args[self._HEAVY_LANG_DICT[obj_type].args[0].name]),
num_outlets=0,
annotations=annotations)
def reduce(self) -> tuple:
objects = set()
connections = []
# reduce a HeavyLang dac to a number of individual HeavyIR __add~f objects
for i, channel_index in enumerate(self.args["channels"]):
if len(self.inlet_connections[i]) > 0: # if there are any connections to this inlet
x = HeavyIrObject("__add~f")
x.inlet_buffers[1] = ("output", channel_index - 1) # channel indicies are one-indexed
x.outlet_buffers[0] = ("output", channel_index - 1)
objects.add(x)
for c in self.inlet_connections[i]:
connections.append((c, [c.copy(to_object=x, inlet_index=0)]))
return (objects, connections)
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1727976067.2615526
hvcc-0.13.3/hvcc/core/hv2ir/HLangDelay.py 0000644 0000000 0000000 00000002763 14677551203 014720 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023 Wasted Audio
#
# 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 .
from typing import Optional, Dict
from .HeavyIrObject import HeavyIrObject
from .HeavyLangObject import HeavyLangObject
from .HeavyGraph import HeavyGraph
class HLangDelay(HeavyLangObject):
""" Handle the delay object.
"""
def __init__(
self,
obj_type: str,
args: Optional[Dict] = None,
graph: Optional['HeavyGraph'] = None,
annotations: Optional[Dict] = None
) -> None:
assert obj_type == "delay"
super().__init__("delay", args, graph,
num_inlets=2,
num_outlets=1,
annotations=annotations)
def reduce(self) -> tuple:
x = HeavyIrObject("__delay", self.args, annotations=self.annotations)
return ({x}, self.get_connection_move_list(x))
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1685549294.7482066
hvcc-0.13.3/hvcc/core/hv2ir/HLangIf.py 0000644 0000000 0000000 00000003732 14435670357 014221 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
#
# 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 .
# moved to HeavyParser.py because of circular dependency
# from .HeavyException import HeavyException
# from .HeavyIrObject import HeavyIrObject
# from .HeavyLangObject import HeavyLangObject
# class HLangIf(HeavyLangObject):
# """ Translates HeavyLang object [if] to HeavyIR [if] or [if~].
# """
# def __init__(self, obj_type, args, graph, annotations=None):
# HeavyLangObject.__init__(self, "if", args, graph,
# num_inlets=2,
# num_outlets=2,
# annotations=annotations)
# def reduce(self):
# if self.has_inlet_connection_format(["cc", "_c", "c_", "__"]):
# x = HeavyIrObject("__if", self.args)
# elif self.has_inlet_connection_format("ff"):
# # TODO(mhroth): implement this
# x = HeavyParser.graph_from_file("./hvlib/if~f.hv.json")
# elif self.has_inlet_connection_format("ii"):
# # TODO(mhroth): implement this
# x = HeavyParser.graph_from_file("./hvlib/if~i.hv.json")
# else:
# fmt = self._get_connection_format(self.inlet_connections)
# raise HeavyException(f"Unhandled connection configuration to object [if]: {fmt}")
# return ({x}, self.get_connection_move_list(x))
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1727976067.2625525
hvcc-0.13.3/hvcc/core/hv2ir/HLangLine.py 0000644 0000000 0000000 00000003307 14677551203 014544 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023 Wasted Audio
#
# 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 .
from typing import Optional, Dict
from .HeavyIrObject import HeavyIrObject
from .HeavyLangObject import HeavyLangObject
from .HeavyGraph import HeavyGraph
class HLangLine(HeavyLangObject):
""" Translates HeavyLang object [line] to HeavyIR [__line] or [__line~f].
"""
def __init__(
self,
obj_type: str,
args: Dict,
graph: 'HeavyGraph',
annotations: Optional[Dict] = None
) -> None:
assert obj_type == "line"
super().__init__("line", args, graph,
num_inlets=2,
num_outlets=1,
annotations=annotations)
def reduce(self) -> tuple:
if self.has_outlet_connection_format("f"):
x = HeavyIrObject("__line~f", self.args)
elif self.has_outlet_connection_format("c"):
x = HeavyIrObject("__line", self.args)
else:
self.add_error("Unknown inlet configuration.")
return ({x}, self.get_connection_move_list(x))
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1727976067.2625525
hvcc-0.13.3/hvcc/core/hv2ir/HLangMessage.py 0000644 0000000 0000000 00000002711 14677551203 015237 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023 Wasted Audio
#
# 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 .
from typing import Optional, Dict
from .HeavyLangObject import HeavyLangObject
from .HeavyIrObject import HeavyIrObject
from .HeavyGraph import HeavyGraph
class HLangMessage(HeavyLangObject):
""" Handles the HeavyLang "message" object.
"""
def __init__(
self,
obj_type: str,
args: Dict,
graph: 'HeavyGraph',
annotations: Optional[Dict] = None
) -> None:
assert obj_type == "message"
super().__init__(obj_type, args, graph,
num_inlets=1,
num_outlets=1,
annotations=annotations)
def reduce(self) -> tuple:
x = HeavyIrObject("__message", self.args)
return ({x}, self.get_connection_move_list(x))
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1727976067.2625525
hvcc-0.13.3/hvcc/core/hv2ir/HLangNoise.py 0000644 0000000 0000000 00000003250 14677551203 014727 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
#
# 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 .
# moved to HeavyParser.py because of circular dependency
# import os
# import random
# from .HeavyLangObject import HeavyLangObject
# from .HeavyParser import HeavyParser
# class HLangNoise(HeavyLangObject):
# """ Handles the HeavyLang "noise" object.
# """
# def __init__(self, obj_type, args, graph, annotations=None):
# assert obj_type == "noise"
# HeavyLangObject.__init__(self, "noise", args, graph,
# num_inlets=1,
# num_outlets=1,
# annotations=annotations)
# def reduce(self):
# seed = int(random.uniform(1, 2147483647)) # assign a random 32-bit seed
# noise_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "./hvlib/noise.hv.json")
# x = HeavyParser.graph_from_file(noise_path, graph_args={"seed": seed})
# x.reduce()
# # TODO(mhroth): deal with control input
# return ({x}, self.get_connection_move_list(x))
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1727976067.2625525
hvcc-0.13.3/hvcc/core/hv2ir/HLangPhasor.py 0000644 0000000 0000000 00000003070 14677551203 015106 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023 Wasted Audio
#
# 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 .
from typing import Optional, Dict
from .HeavyIrObject import HeavyIrObject
from .HeavyLangObject import HeavyLangObject
from .HeavyGraph import HeavyGraph
class HLangPhasor(HeavyLangObject):
""" Translates HeavyLang object [phasor] to HeavyIR [phasor~].
"""
def __init__(
self,
obj_type: str,
args: Dict,
graph: 'HeavyGraph',
annotations: Optional[Dict] = None
) -> None:
assert obj_type == "phasor"
super().__init__(obj_type, args, graph, annotations=annotations)
def reduce(self) -> tuple:
if self.has_inlet_connection_format(["f_", "fc"]):
x = HeavyIrObject("__phasor~f", self.args)
return ({x}, self.get_connection_move_list(x))
else:
x = HeavyIrObject("__phasor_k~f", self.args)
return ({x}, self.get_connection_move_list(x))
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1727976067.2625525
hvcc-0.13.3/hvcc/core/hv2ir/HLangPrint.py 0000644 0000000 0000000 00000002701 14677551203 014746 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023 Wasted Audio
#
# 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 .
from typing import Optional, Dict
from .HeavyLangObject import HeavyLangObject
from .HeavyIrObject import HeavyIrObject
from .HeavyGraph import HeavyGraph
class HLangPrint(HeavyLangObject):
""" Handles the HeavyLang "print" object.
"""
def __init__(
self,
obj_type: str,
args: Dict,
graph: 'HeavyGraph',
annotations: Optional[Dict] = None
) -> None:
assert obj_type == "print"
super().__init__(obj_type, args, graph,
num_inlets=1,
num_outlets=0,
annotations=annotations)
def reduce(self) -> tuple:
x = HeavyIrObject("__print", self.args)
return ({x}, self.get_connection_move_list(x))
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1727976067.2625525
hvcc-0.13.3/hvcc/core/hv2ir/HLangRandom.py 0000644 0000000 0000000 00000003033 14677551203 015071 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023 Wasted Audio
#
# 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 .
import random
from typing import Optional, Dict
from .HeavyLangObject import HeavyLangObject
from .HeavyIrObject import HeavyIrObject
from .HeavyGraph import HeavyGraph
class HLangRandom(HeavyLangObject):
""" Handles the HeavyLang "print" object.
"""
def __init__(
self,
obj_type: str,
args: Dict,
graph: 'HeavyGraph',
annotations: Optional[Dict] = None
) -> None:
assert obj_type == "random"
super().__init__(obj_type, args, graph,
num_inlets=2,
num_outlets=1,
annotations=annotations)
def reduce(self) -> tuple:
self.args["seed"] = int(random.uniform(-2147483647, 2147483648))
x = HeavyIrObject("__random", self.args)
return ({x}, self.get_connection_move_list(x))
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1727976067.2625525
hvcc-0.13.3/hvcc/core/hv2ir/HLangReceive.py 0000644 0000000 0000000 00000004212 14677551203 015233 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023 Wasted Audio
#
# 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 .
from typing import Optional, Dict
from .HeavyLangObject import HeavyLangObject
from .HIrReceive import HIrReceive
from .HeavyGraph import HeavyGraph
class HLangReceive(HeavyLangObject):
""" Translates HeavyLang object [receive] to HeavyIR [__receive if control,
otherwise for signal connections it will remove the send~/receive~ objects
and reorder the graph.
"""
def __init__(
self,
obj_type: str,
args: Dict,
graph: 'HeavyGraph',
annotations: Optional[Dict] = None
) -> None:
assert obj_type == "receive"
super().__init__(obj_type, args, graph, annotations=annotations)
def reduce(self) -> Optional[tuple]:
if self.has_outlet_connection_format(["c"]):
x = HIrReceive("__receive", self.args, annotations=self.annotations)
return ({x}, self.get_connection_move_list(x))
elif self.has_outlet_connection_format(["f"]):
# this case should be handled already in HeavyGraph.remap_send_receive()
# clean up just in case
return (set(), [])
elif self.has_outlet_connection_format("_"):
self.add_warning("receive~ object doesn't have any outputs, this one is being removed.")
return (set(), [])
else:
fmt = self._get_connection_format(self.outlet_connections)
self.add_error(f"Unknown outlet configuration: {fmt}")
return None
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1727976067.2625525
hvcc-0.13.3/hvcc/core/hv2ir/HLangSend.py 0000644 0000000 0000000 00000004701 14677551203 014545 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023 Wasted Audio
#
# 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 .
from typing import Optional, Dict
from .HeavyLangObject import HeavyLangObject
from .HIrSend import HIrSend
from .HeavyGraph import HeavyGraph
class HLangSend(HeavyLangObject):
""" Translates HeavyLang object [send] to HeavyIR [__send] if control,
otherwise for signal connections it will remove the send~/receive~ objects
and reorder the graph.
"""
def __init__(
self,
obj_type: str,
args: Dict,
graph: 'HeavyGraph',
annotations: Optional[Dict] = None
) -> None:
assert obj_type == "send"
super().__init__(obj_type, args, graph, annotations=annotations)
def reduce(self) -> Optional[tuple]:
if self.has_inlet_connection_format("c"):
ir_args = dict(self.args)
ir_args["hash"] = f"0x{HeavyLangObject.get_hash(ir_args['name']):X}"
x = HIrSend("__send", ir_args, annotations=self.annotations)
return ({x}, self.get_connection_move_list(x))
elif self.has_inlet_connection_format("f"):
# this case should be handled already in HeavyGraph.remap_send_receive()
# clean up just in case
return (set(), [])
elif self.has_inlet_connection_format("_"):
self.add_warning("send~ object doesn't have any inputs, this one is being removed.")
return (set(), [])
elif self.has_inlet_connection_format("m"):
self.add_error(
"Inlet can support either control or signal connections, "
"but not both at the same time.")
return None
else:
fmt = self._get_connection_format(self.inlet_connections)
self.add_error(f"Unknown inlet configuration: {fmt}")
return None
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1735754044.2598228
hvcc-0.13.3/hvcc/core/hv2ir/HLangSequence.py 0000644 0000000 0000000 00000006361 14735300474 015425 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023-2024 Wasted Audio
#
# 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 .
from typing import Optional, Dict, List
from .Connection import Connection
from .HeavyException import HeavyException
from .HeavyIrObject import HeavyIrObject
from .HeavyLangObject import HeavyLangObject
from .HeavyGraph import HeavyGraph
class HLangSequence(HeavyLangObject):
def __init__(
self,
obj_type: str,
args: Dict,
graph: 'HeavyGraph',
annotations: Optional[Dict] = None
) -> None:
# get the number of outlets that this object has
num_outlets = len(args[self._HEAVY_LANG_DICT[obj_type].args[0].name])
super().__init__(obj_type, args, graph,
num_inlets=1,
num_outlets=num_outlets,
annotations=annotations)
def reduce(self) -> tuple:
cast_objs: List = []
for a in self.args[self.name_for_arg()]:
if a in {"a", "anything", "l", "list"}:
cast_objs.append(None) # pass through
elif a in {"b", "bang"}:
cast_objs.append(HeavyIrObject("__cast_b", {}))
elif a in {"f", "float"}:
cast_objs.append(HeavyIrObject("__cast_f", {}))
elif a in {"s", "symbol"}:
cast_objs.append(HeavyIrObject("__cast_s", {}))
else:
raise HeavyException(f"Unsupported cast type '{a}'.")
connections = []
# establish connections from inlet objects to the cast objects
for ci in self.inlet_connections[0]:
c_list = []
for i in range(self.num_outlets - 1, -1, -1):
if cast_objs[i] is not None:
c_list.append(Connection.copy(ci, to_object=cast_objs[i], inlet_index=0))
else:
# for anything casts, establish a connection directly to the target
for co in self.outlet_connections[i]:
c_list.append(Connection.copy(ci, to_object=co.to_object, inlet_index=co.inlet_index))
connections.append((ci, c_list))
# establish connections from the cast objects
for i in range(self.num_outlets - 1, -1, -1):
if cast_objs[i] is not None:
for c in self.outlet_connections[i]:
connections.append((c, [c.copy(from_object=cast_objs[i], outlet_index=0)]))
# remove None objects from cast_objs. It was only used as a placeholder
# to indicate that a cast passthrough
objects = set(cast_objs)
objects.discard(None)
return (objects, connections)
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1727976067.2625525
hvcc-0.13.3/hvcc/core/hv2ir/HLangSlice.py 0000644 0000000 0000000 00000002701 14677551203 014711 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023 Wasted Audio
#
# 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 .
from typing import Optional, Dict
from .HeavyLangObject import HeavyLangObject
from .HeavyIrObject import HeavyIrObject
from .HeavyGraph import HeavyGraph
class HLangSlice(HeavyLangObject):
""" Handles the HeavyLang "slice" object.
"""
def __init__(
self,
obj_type: str,
args: Dict,
graph: 'HeavyGraph',
annotations: Optional[Dict] = None
) -> None:
assert obj_type == "slice"
super().__init__(obj_type, args, graph,
num_inlets=3,
num_outlets=2,
annotations=annotations)
def reduce(self) -> tuple:
x = HeavyIrObject("__slice", self.args)
return ({x}, self.get_connection_move_list(x))
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1727976067.2625525
hvcc-0.13.3/hvcc/core/hv2ir/HLangSystem.py 0000644 0000000 0000000 00000002705 14677551203 015142 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023 Wasted Audio
#
# 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 .
from typing import Optional, Dict
from .HeavyLangObject import HeavyLangObject
from .HeavyIrObject import HeavyIrObject
from .HeavyGraph import HeavyGraph
class HLangSystem(HeavyLangObject):
""" Handles the HeavyLang "system" object.
"""
def __init__(
self,
obj_type: str,
args: Dict,
graph: 'HeavyGraph',
annotations: Optional[Dict] = None
) -> None:
assert obj_type == "system"
super().__init__(obj_type, args, graph,
num_inlets=1,
num_outlets=1,
annotations=annotations)
def reduce(self) -> tuple:
x = HeavyIrObject("__system", self.args)
return ({x}, self.get_connection_move_list(x))
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1735754044.2598228
hvcc-0.13.3/hvcc/core/hv2ir/HLangTable.py 0000644 0000000 0000000 00000004167 14735300474 014706 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023-2024 Wasted Audio
#
# 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 .
import re
from typing import Optional, Dict
from .HeavyLangObject import HeavyLangObject
from .HeavyIrObject import HeavyIrObject
from .HeavyGraph import HeavyGraph
class HLangTable(HeavyLangObject):
""" Handles the HeavyLang "table" object.
"""
def __init__(
self,
obj_type: str,
args: Dict,
graph: 'HeavyGraph',
annotations: Optional[Dict] = None
) -> None:
assert obj_type == "table"
super().__init__(obj_type, args, graph, annotations=annotations)
# the values argument overrides the size argument
if len(self.args.get("values", [])) > 0:
self.args["size"] = len(self.args["values"])
if self.args["extern"]:
# externed tables must contain only alphanumeric characters or underscores,
# so that the names can be easily and transparently turned into code
if re.search(r"\W", args["name"]):
self.add_error(f"Table names may only contain alphanumeric characters \
or underscore: '{args['name']}'")
def reduce(self) -> tuple:
x = HeavyIrObject("__table", self.args)
# ensure that __table object maintains the same id as the original
# table object. The latter is referenced by id from other objects.
# Consistency must be maintained.
x.id = self.id
return ({x}, self.get_connection_move_list(x))
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1727976067.2625525
hvcc-0.13.3/hvcc/core/hv2ir/HLangUnop.py 0000644 0000000 0000000 00000007711 14677551203 014601 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023 Wasted Audio
#
# 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 .
from typing import Optional, Dict
from .HeavyLangObject import HeavyLangObject
from .HeavyIrObject import HeavyIrObject
from .HeavyGraph import HeavyGraph
class HLangUnop(HeavyLangObject):
__HEAVY_DICT = {
"log": ["__log"],
"log10": ["__log10"],
"log2": ["__log2", "__log2~f"],
"cos": ["__cos", "__cos~f"],
"acos": ["__acos", "__acos~f"],
"cosh": ["__cosh", "__cosh~f"],
"acosh": ["__acosh", "__acosh~f"],
"sin": ["__sin", "__sin~f"],
"asin": ["__asin", "__asin~f"],
"sinh": ["__sinh", "__sinh~f"],
"asinh": ["__asinh", "__asin~f"],
"tan": ["__tan", "__tan~f"],
"atan": ["__atan", "__atan~f"],
"tanh": ["__tanh", "__tanh~f"],
"atanh": ["__atanh", "__atanh~f"],
"exp": ["__exp", "__exp~f"],
"sqrt": ["__sqrt", "__sqrt~f"],
"abs": ["__abs", "__abs~f", "__abs~i"],
"floor": ["__floor", "__floor~f"],
"ceil": ["__ceil", "__ceil~f"],
"cast_fi": ["__cast~fi"],
"cast_if": ["__cast~if"]
}
def __init__(
self,
obj_type: str,
args: Dict,
graph: 'HeavyGraph',
annotations: Optional[Dict] = None
) -> None:
assert self.handles_type(obj_type)
super().__init__(obj_type, args, graph,
num_inlets=1,
num_outlets=1,
annotations=annotations)
@classmethod
def handles_type(cls, obj_type: str) -> bool:
""" Returns True if this class handles the given object type. False otherwise.
"""
return obj_type in HLangUnop.__HEAVY_DICT
def reduce(self) -> Optional[tuple]:
if len(self.inlet_connections[0]) == 0:
self.add_error("Unary operator objects must have an input.")
# TODO(mhroth): there is no general consensus on how to handle incoming
# and outgoing connections of different types, given the local let-type
# definitions.
if self.type == "cast_fi":
x = HeavyIrObject(self.type) # is this correct? no idea what x is otherwise
return ({HeavyIrObject("__cast~fi", {})}, self.get_connection_move_list(x))
elif self.type == "cast_if":
x = HeavyIrObject(self.type) # is this correct? no idea what x is otherwise
return ({HeavyIrObject("__cast~if", {})}, self.get_connection_move_list(x))
elif self.has_inlet_connection_format("f"):
ir_type = f"__{self.type}~f"
assert ir_type in HLangUnop.__HEAVY_DICT[self.type]
x = HeavyIrObject(ir_type)
return ({x}, self.get_connection_move_list(x, "~f>"))
elif self.has_inlet_connection_format("i"):
ir_type = f"__{self.type}~i"
assert ir_type in HLangUnop.__HEAVY_DICT[self.type]
x = HeavyIrObject(ir_type)
return ({x}, self.get_connection_move_list(x, "~i>"))
elif self.has_inlet_connection_format("c"):
ir_type = f"__{self.type}"
assert ir_type in HLangUnop.__HEAVY_DICT[self.type]
x = HeavyIrObject(ir_type)
return ({x}, self.get_connection_move_list(x, "-->"))
else:
self.add_error("Unknown inlet configuration.")
return None
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1727976067.2625525
hvcc-0.13.3/hvcc/core/hv2ir/HLangVar.py 0000644 0000000 0000000 00000005557 14677551203 014416 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023 Wasted Audio
#
# 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 .
from typing import Optional, Dict
from .HeavyIrObject import HeavyIrObject
from .HeavyLangObject import HeavyLangObject
from .HeavyGraph import HeavyGraph
class HLangVar(HeavyLangObject):
def __init__(
self,
obj_type: str,
args: Dict,
graph: 'HeavyGraph',
annotations: Optional[Dict] = None
) -> None:
assert obj_type == "var"
super().__init__(obj_type, args, graph, annotations=annotations)
def reduce(self) -> tuple:
if self.has_inlet_connection_format("_") and self.has_outlet_connection_format("f"):
x = HeavyIrObject("__var_k~f", self.args)
elif self.has_inlet_connection_format("_") and self.has_outlet_connection_format("i"):
x = HeavyIrObject("__var_k~i", self.args)
elif self.has_inlet_connection_format(["_", "c"]) and self.has_outlet_connection_format("f"):
x = HeavyIrObject("__var~f", self.args)
x.id = self.id
y = HeavyIrObject("__varread~f", {"var_id": self.id})
move_list = []
for c in [c for cc in self.inlet_connections for c in cc]:
move_list.append((c, [c.copy(to_object=x)]))
for c in [c for cc in self.outlet_connections for c in cc]:
move_list.append((c, [c.copy(from_object=y)]))
return ({x, y}, move_list)
elif self.has_inlet_connection_format(["_", "c"]) and self.has_outlet_connection_format("i"):
x = HeavyIrObject("__var~i", self.args)
x.id = self.id
y = HeavyIrObject("__varread~i", {"var_id": self.id})
move_list = []
for c in [c for cc in self.inlet_connections for c in cc]:
move_list.append((c, [c.copy(to_object=x)]))
for c in [c for cc in self.outlet_connections for c in cc]:
move_list.append((c, [c.copy(from_object=y)]))
return ({x, y}, move_list)
elif self.has_inlet_connection_format(["_", "c"]) and self.has_outlet_connection_format(["_", "c"]):
x = HeavyIrObject("__var", self.args)
else:
raise Exception()
return ({x}, self.get_connection_move_list(x))
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1727976067.2635524
hvcc-0.13.3/hvcc/core/hv2ir/HLangVario.py 0000644 0000000 0000000 00000005054 14677551203 014736 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023 Wasted Audio
#
# 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 .
from typing import Optional, Dict
from .HeavyException import HeavyException
from .HeavyIrObject import HeavyIrObject
from .HeavyLangObject import HeavyLangObject
from .HeavyGraph import HeavyGraph
class HLangVario(HeavyLangObject):
def __init__(
self,
obj_type: str,
args: Dict,
graph: 'HeavyGraph',
annotations: Optional[Dict] = None
) -> None:
assert obj_type == "vario"
super().__init__(obj_type, args, graph, annotations=annotations)
def reduce(self) -> Optional[tuple]:
if self.graph is not None and self.name is not None:
var_obj = self.graph.resolve_object_for_name(self.name, "var")
if var_obj is None:
raise HeavyException(
f"No corresponding \"var\" object found for object {self} in file {self.graph.file}"
)
if self.has_inlet_connection_format("f") and self.has_outlet_connection_format("_"):
x = HeavyIrObject("__varwrite~f", {"var_id": var_obj.id})
return {x}, self.get_connection_move_list(x)
elif self.has_inlet_connection_format("i") and self.has_outlet_connection_format("_"):
x = HeavyIrObject("__varwrite~i", {"var_id": var_obj.id})
return {x}, self.get_connection_move_list(x)
elif self.has_inlet_connection_format("_") and self.has_outlet_connection_format("f"):
x = HeavyIrObject("__varread~f", {"var_id": var_obj.id})
return {x}, self.get_connection_move_list(x)
elif self.has_inlet_connection_format("_") and self.has_outlet_connection_format("i"):
x = HeavyIrObject("__varread~i", {"var_id": var_obj.id})
return {x}, self.get_connection_move_list(x)
else:
raise Exception()
return None
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1735754044.2598228
hvcc-0.13.3/hvcc/core/hv2ir/HeavyException.py 0000644 0000000 0000000 00000002100 14735300474 015661 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023-2024 Wasted Audio
#
# 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 .
from hvcc.types.compiler import CompilerNotif
class HeavyException(Exception):
""" This exception is raised if anything goes wrong during the hv2ir compilation process.
"""
def __init__(self, message: str = "") -> None:
super(Exception, self).__init__(message)
self.message = message
self.notes: CompilerNotif = CompilerNotif()
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1735983617.1410897
hvcc-0.13.3/hvcc/core/hv2ir/HeavyGraph.py 0000644 0000000 0000000 00000125652 14736201001 014771 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023-2024 Wasted Audio
#
# 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 .
import os
import re
from collections import Counter
from typing import Optional, Union, Dict, List, Set, Tuple
from .BufferPool import BufferPool
from .Connection import Connection
from .LocalVars import LocalVars
from .HeavyException import HeavyException
from .HeavyIrObject import HeavyIrObject
from .HIrReceive import HIrReceive
from .HeavyLangObject import HeavyLangObject
from hvcc.types.compiler import CompilerNotif
from hvcc.types.IR import (
IRObjectdict, IRGraph, IRName, IRInit, IRControl, IRReceiver,
IRSendMessage, IROnMessage, IRSignalList, IRSignal, IRTable, IRNumTempBuffer,
)
from hvcc.types.Lang import LangLetType
class HeavyGraph(HeavyIrObject):
""" Represents a graph. Subclasses HeavyIrObject for functionality.
"__graph" does exist as a HeavyIR object, though it has no functionality
and should not appear in any IR output.
"""
def __init__(
self,
graph: Optional['HeavyGraph'] = None,
graph_args: Optional[Dict] = None,
file: str = "",
xname: str = "heavy"
) -> None:
# zero inlets and outlets until inlet/outlet objects are declared
super().__init__("__graph", graph_args, graph, 0, 0)
# the heavy file which defines this graph
self.file = file
# a user-defined name of this graph
self.xname = xname
# the dictionary of all objects in the graph
self.objs: Dict = {}
# set the local arguments
if graph_args is not None:
self.args = graph_args
else:
self.args = {}
# initialise the local variables
self.local_vars = LocalVars()
# the list of all constituent inlet and outlet objects
# graphs always start with no inlet/outlets
self.inlet_objs: List = []
self.outlet_objs: List = []
# the set of input/output channels that this graph writes to
self.input_channel_set: set = set()
self.output_channel_set: set = set()
# an ordered list of signal objects to process
self.signal_order: List = []
# a pool of signal buffers for use during signal ordering and buffer assignment
self.buffer_pool: Optional[BufferPool] = None
def resolve_arguments(self, obj_args: Dict) -> Dict:
""" Resolves the object arguments based on values from the local graph.
"""
args = dict(obj_args) # make a copy of the input arguments
for key, value in args.items():
# do any of them reference a variable input?
if isinstance(value, str):
if value.find("$") > -1:
# is there a value provided for that key in the graph arguments?
for k in self.args:
# replace all instances of $k with the argument value
# do this for all keys in the graph's argument dictionary
dollar_key = f"${k}"
if value.find(dollar_key) > -1:
if value == dollar_key:
value = self.args[k] # maintain the original data type
break # break because all dollar arguments have by definition been replaced
else:
# otherwise assume a string value
value = value.replace(dollar_key, str(self.args[k]))
# continue around the loop in case value has multiple dollar args
if isinstance(value, str) and value.find("$") > -1:
# if the variable cannot be resolved, leave it alone.
# When the arguments are passed to the object, they will be
# set to the default value. These unset variables are simply
# removed from the argument dictionary
args.pop(key, None)
self.add_warning(f"Variable \"{key}\":\"{value}\" could not be resolved.")
else:
args[key] = value
return args
def connect_objects(self, c: Connection, require_intra_graph_connection: bool = True) -> None:
""" Establish a connection from one object to another.
Typically a connection must be made within a single graph, though
this requirement may be subpressed.
"""
if require_intra_graph_connection:
assert c.from_object.graph is c.to_object.graph, \
f"Conection established between two objects not in the same graph: {c}"
# add the connection from the parent to the child
c.from_object.add_connection(c)
# add the connection to the child from the parent
c.to_object.add_connection(c)
def disconnect_objects(self, c: Connection) -> None:
""" Remove a connection from one object to another.
"""
# remove the connection from the parent to the child
c.from_object.remove_connection(c)
# remove the connection to the child from the parent
c.to_object.remove_connection(c)
def update_connection(self, c: Connection, n_list: List) -> None:
""" Update the old connection (c) with the new connections (n_list).
Connection order is maintained. If c is None, this method acts
as connect_objects(). If n is empty, this method acts as disconnect_objects().
"""
if c is not None and len(n_list) > 0:
if all([c.from_object is n.from_object for n in n_list]):
# connections start at the same point
c.from_object.replace_connection(c, n_list)
c.to_object.remove_connection(c)
for n in n_list:
n.to_object.add_connection(n)
elif all([c.to_object is n.to_object for n in n_list]):
# connections end at the same point
c.to_object.replace_connection(c, n_list)
c.from_object.remove_connection(c)
for n in n_list:
n.from_object.add_connection(n)
else:
raise HeavyException(f"Connections must have a common endpoint: {c} / {n_list}")
elif c is not None and len(n_list) == 0:
self.disconnect_objects(c) # remove connection c
elif c is None and len(n_list) > 0:
for n in n_list:
self.connect_objects(n) # add connection n
def add_object(
self,
obj: HeavyIrObject,
obj_id: Optional[Union[int, str]] = None
) -> None:
""" Add an object to the graph based on the given id.
Per-object bookkeeping is performed.
"""
obj_id = obj_id or obj.id
if obj_id not in self.objs:
obj.graph = self
self.objs[obj_id] = obj
# some object needs to be specially handled when added to the graph
if obj.type in {"inlet", "__inlet"}:
self.inlet_connections.append([])
self.inlet_buffers.append(("zero", 0))
self.inlet_objs.append(obj)
# sort inlet objects according to their index, ascending
self.inlet_objs.sort(key=lambda o: o.args["index"])
elif obj.type in {"outlet", "__outlet"}:
self.outlet_connections.append([])
self.outlet_buffers.append(("zero", 0))
self.outlet_objs.append(obj)
self.outlet_objs.sort(key=lambda o: o.args["index"])
elif obj.type in {"adc"}:
self.input_channel_set.update(obj.args["channels"])
elif obj.type in {"dac"}:
# if the object is a dac, keep track of the output channels
# that this graph is writing to
self.output_channel_set.update(obj.args["channels"])
elif obj.type in {"receive", "__receive", "send", "__send"}:
self.__register_named_object(obj, obj.name)
elif obj.type in {"table", "__table"}:
self.__register_named_object(
obj,
obj.name,
static=obj.static,
unique=True)
elif obj.type in {"var"} and obj.name is not None:
self.__register_named_object(
obj,
obj.name,
static=obj.static,
unique=True)
else:
self.add_error(f"Duplicate object id {obj_id} found in graph.")
def __register_named_object(
self,
obj: HeavyIrObject,
name: Optional[str] = None,
static: bool = False,
unique: bool = False
) -> None:
""" Register a named object at the appropriate graph level.
"""
name = name or obj.name
if name is not None:
if obj.scope == "private" and not static:
# stay at this level
self.local_vars.register_object(obj, name, static, unique)
elif obj.scope == "protected":
# go up one level
g = self.graph if self.graph is not None else self
g.local_vars.register_object(obj, name, static, unique)
elif obj.scope == "public" or static:
self.get_root_graph().local_vars.register_object(obj, name, static, unique)
else:
raise HeavyException(f"Unknown scope \"{obj.scope}\" for object {obj}.")
else:
raise HeavyException(f"Unknown scope \"{obj.scope}\" for object {obj}.")
def __unregister_named_object(
self,
obj: HeavyIrObject,
name: Optional[str] = None,
static: bool = False,
unique: bool = False
) -> None:
""" Unregister a named object at the appropriate graph level.
"""
name = name or obj.name
if name is not None:
if obj.scope == "private" and not static:
self.local_vars.unregister_object(obj, name)
elif obj.scope == "protected":
# go up one level
g = self.graph if self.graph is not None else self
g.local_vars.unregister_object(obj, name)
elif obj.scope == "public" or static:
self.get_root_graph().local_vars.unregister_object(obj, name)
else:
raise HeavyException(f"Unknown scope \"{obj.scope}\" for object {obj}.")
else:
raise HeavyException(f"Unknown scope \"{obj.scope}\" for object {obj}.")
def resolve_objects_for_name(
self,
name: str,
obj_types: Union[List, str],
local_graph: Optional['HeavyGraph'] = None
) -> List:
""" Returns all objects with the given name and type that are visible
from this graph. Returns an empty list if no objects could be found.
The results are otherwise ordered from most local/private first,
and the most global last.
"""
obj_types = obj_types if isinstance(obj_types, list) else [obj_types]
local_graph = local_graph or self
obj_list = [o for o in self.local_vars.get_objects_for_name(name, obj_types)
if not (o.scope == "private" and o.graph is not local_graph)]
if self.graph is not None:
obj_list_from_super_graph = self.graph.resolve_objects_for_name(name, obj_types, local_graph)
obj_list.extend(obj_list_from_super_graph)
return obj_list
def resolve_object_for_name(
self,
name: str,
obj_types: Union[List, str],
local_graph: Optional['HeavyGraph'] = None
) -> Optional[HeavyIrObject]:
""" Returns the first object with the given name and type that is visible
from this graph. Returns None if no objects are available.
This is a convenience method.
"""
objs = self.resolve_objects_for_name(name, obj_types, local_graph)
return objs[0] if len(objs) > 0 else None
def is_root_graph(self) -> bool:
""" Returns true if this is the top-level (i.e. root) graph. False otherwise.
"""
return self.graph is None
def get_root_graph(self) -> 'HeavyGraph':
"""Returns the top-level graph.
"""
if self.is_root_graph():
return self
elif self.graph is not None:
return self.graph.get_root_graph()
else:
# NOTE(dromer): we should never get here
raise Exception
def find_path_for_abstraction(self, obj_type: str) -> Optional[str]:
""" Travels up the graph heirarchy looking for a file path to an abstraction.
Returns None if no abstraction is found.
"""
file_path = self.local_vars.find_path_for_abstraction(obj_type)
if file_path is not None:
return file_path
else:
return self.graph.find_path_for_abstraction(obj_type) if self.graph is not None else None
def remove_object(self, o: HeavyIrObject, obj_id: Optional[str] = None) -> None:
""" Removes an object and all of its connections from the graph.
A custom id for the object to be removed can be given.
"""
for connections in o.inlet_connections:
for c in list(connections): # make copy of connections as it will be mutated
self.disconnect_objects(c)
for connections in o.outlet_connections:
for c in list(connections): # make copy
self.disconnect_objects(c)
for k, v in self.objs.items():
if o is v:
del self.objs[k]
break
if o.type in {"receive", "__receive", "send", "__send"}:
self.__unregister_named_object(o, o.name)
def get_inlet_object(self, index: int) -> HeavyIrObject:
""" Returns the indexed inlet object of this graph.
"""
return self.inlet_objs[index]
def get_outlet_object(self, index: int) -> HeavyIrObject:
""" Returns the indexed outlet object of this graph.
"""
return self.outlet_objs[index]
def get_input_channel_set(self, recursive: bool = False) -> set:
""" Returns the set of input channels that this graph writes to.
Optionally includes all subgraphs as well.
"""
if recursive:
channels = set(self.input_channel_set) # copy the output channel set
for o in [o for o in self.objs.values() if o.type == "__graph"]:
channels.update(o.get_input_channel_set(recursive=True))
return channels
else:
return self.input_channel_set
def get_output_channel_set(self, recursive: bool = False) -> set:
""" Returns the set of output channels that this graph writes to.
Optionally includes all subgraphs as well.
"""
if recursive:
channels = set(self.output_channel_set) # copy the output channel set
for o in [o for o in self.objs.values() if o.type == "__graph"]:
channels.update(o.get_output_channel_set(recursive=True))
return channels
else:
return self.output_channel_set
def get_notices(self) -> CompilerNotif:
notices = HeavyLangObject.get_notices(self)
for o in self.objs.values():
n = o.get_notices()
notices.warnings.extend(n.warnings)
notices.errors.extend(n.errors)
return notices
def get_objects_for_type(self, obj_type: str, recursive: bool = False) -> List:
""" Returns a list of all objects of a given type in this graph.
The optional parameter "recursive" also includes all objects from subgraphs.
"""
obj_list = []
for o in self.objs.values():
if o.type == "__graph" and recursive:
obj_list.extend(o.get_objects_for_type(obj_type, recursive))
elif o.type == obj_type:
obj_list.append(o)
return obj_list
def get_object_counter(self, recursive: bool = False) -> Counter:
""" Returns a counter of all object types in this graph.
Graph objects are explicitly removed. "__inlet" and "__outlet"
objects are renamed to "inlet" and "outlet", for clarity.
An optional recursive argument includes all subgraphs.
"""
c: Counter = Counter()
for o in self.objs.values():
if o.type == "__graph" and recursive:
c += o.get_object_counter(recursive=True)
elif o.type in {"__inlet", "__outlet"}:
c[o.type[2:]] += 1 # remove leading "__"
else:
c[o.type] += 1
return c
def prepare(self) -> None:
""" Prepares a graph to be exported. Must be called from a root graph.
"""
assert self.is_root_graph()
try:
# apply graph transformations when all graphs have been read
# All transformations must be recursive.
# resolve all connection types such that all HeavyLang objects can
# be correctly reduced
self._resolve_connection_types()
# TODO(mhroth): prune unnecessary objects and connections
# All unnecessary objects and connections should be removed before the
# graph is reduced. Reduction generally takes into account the existence
# of connections when deciding on what HeavyIR objects to used.
self._remove_unused_inlet_connections()
# reconnect all send~/receive~ objects and remove them from the graph
self.remap_send_receive()
# now that the basic graph has been constructed,
# objects are reduced to their well-defined low-level types.
# Some objects may be replaced by graphs containing only low-level objects.
# The graph is changed in-place and consists of only low-level
# objects and subgraphs. All invalid connections are pruned.
self.reduce()
# +~~ expansion
# ensures that there is at most one signal connection at any inlet
self.cascade_expansion()
# fma replacement
# convert [__mul~f ~f> __add~f] into [__fma~f]
self.fma_replacement()
# group all control receivers with the same name under one logical receiver
self.group_control_receivers()
# assign signal buffers to signal objects
# Buffer assignment only takes place at the very end,
# and does so recursively over all objects and subgraphs.
# All objects are ordered before buffers are assigned.
self.assign_signal_buffers()
except HeavyException as e:
e.notes = self.get_notices()
e.notes.has_error = True
e.notes.exception = e
raise e
def _remove_unused_inlet_connections(self) -> None:
""" Remove connections from inlet object, if there are no incoming
connections to it. This is a basic approach to pruning unused or
unnecessary connections, which allow further optimisation later
in the pipeline.
"""
for i, cc in enumerate(self.inlet_connections):
if len(cc) == 0:
# make copy of outlet_connections list because it will be changed
for c in list(self.inlet_objs[i].outlet_connections[0]):
self.disconnect_objects(c)
# the recursive bit
for o in [o for o in self.objs.values() if (o.type == "__graph")]:
o._remove_unused_inlet_connections()
def _resolved_outlet_type(self, outlet_index: int = 0) -> LangLetType:
# a graph's outlet type depends on the connections incident on the
# corresponding outlet object
connection_type_set = {c.type for c in self.outlet_objs[outlet_index].inlet_connections[0]}
if len(connection_type_set) == 0:
return "-->" # object has no incident connections, default to -->
elif len(connection_type_set) == 1:
return list(connection_type_set)[0]
else:
raise HeavyException(f"{self} has multiple incident connections of differing type.\
The outlet type cannot be explicitly resolved.")
def _resolve_connection_types(self, obj_stack: Optional[set] = None) -> None:
""" Resolves the type of all connections before reduction to IR object types.
If connections incident on an object are incompatible, they are either
resolved, potentially by inserting conversion objects, or pruned.
If a viable resolution cannot be found, an exception may be raised.
"""
obj_stack = obj_stack or set()
if self in obj_stack:
return # ensure that each object is only resolved once (no infinite loops)
else:
obj_stack.add(self)
# start from all roots and resolve all connections downwards
for o in [o for o in self.objs.values() if o.is_root()]:
o._resolve_connection_types(obj_stack)
# when all internal connections have been resolved,
# continue the recursion and resolve all outgoing connections
for c in [c for cc in self.outlet_connections for c in cc]:
if c.is_mixed:
# resolve connection type and update it
connection_type = self._resolved_outlet_type(c.outlet_index)
self.update_connection(c, [c.copy(type=connection_type)])
c.to_object._resolve_connection_types(obj_stack) # turtle on down
def remap_send_receive(self) -> None:
""" Recursively finds all signal send/receive objects and for each unique
send name, get all of the sends, and all of the corresponding receives.
Reconnect all incoming connections to a __add~f, and fan out the results
to all of receivers' connections. At most one __add~f must
be added, and a lot of connections remapped.
"""
sends_dict = self.local_vars.get_registered_objects_for_type("send")
receives_dict = self.local_vars.get_registered_objects_for_type("receive")
for (name, send_objs) in sends_dict.items():
# unconnected send/receives will be removed in the reduce step,
# if necessary (depending on control or signal functionality).
# All signal sends will also be removed, regardless of whether they
# have corresponding receives or not. So here we really only need to
# be concerned with connecting "normal" signal send/receives.
# get all signal sends for a name
s_list = [o for o in send_objs if o.has_inlet_connection_format("f")]
# assume that all similarly named receives will also be signal format
r_list = [o for o in receives_dict[name]]
if len(s_list) == 0:
continue # there are no signals going into this send
elif len(s_list) == 1:
s = s_list[0]
c = s.inlet_connections[0][0] # the connection
ir_var = HeavyIrObject("__var~f")
ir_varset = HeavyIrObject("__varwrite~f", {"var_id": ir_var.id})
s.graph.add_object(ir_var)
s.graph.add_object(ir_varset)
# move all connection to send object, to ir_varset
for c in list(s.inlet_connections[0]):
s.graph.update_connection(c, [c.copy(to_object=ir_varset)])
# move connections from receivers, to be from __var~f
for o in r_list:
ir_varread = HeavyIrObject("__varread~f", {"var_id": ir_var.id})
o.graph.add_object(ir_varread)
for x in list(o.outlet_connections[0]):
o.graph.update_connection(x, [x.copy(from_object=ir_varread)])
else:
ir_vars = [HeavyIrObject("__var~f") for s in s_list]
for i, o in enumerate(s_list):
ir_varset = HeavyIrObject("__varwrite~f", {"var_id": ir_vars[i].id})
o.graph.add_object(ir_vars[i])
o.graph.add_object(ir_varset)
for c in list(o.inlet_connections[0]):
o.graph.update_connection(c, [c.copy(to_object=ir_varset, inlet_index=0)])
for r in r_list:
ir_add = HeavyIrObject("__add~f")
r.graph.add_object(ir_add)
for i, s in enumerate(s_list):
ir_varread = HeavyIrObject("__varread~f", {"var_id": ir_vars[i].id})
r.graph.add_object(ir_varread)
r.graph.connect_objects(Connection(
from_object=ir_varread,
outlet_index=0,
to_object=ir_add,
inlet_index=(0 if i == 0 else 1),
conn_type="~f>"
))
for c in list(r.outlet_connections[0]):
r.graph.update_connection(c, [c.copy(from_object=ir_add)])
# when all is said and done, remove the send and receive objects
for o in s_list:
o.graph.remove_object(o)
for o in r_list:
o.graph.remove_object(o)
def reduce(self) -> Tuple[Set, List]:
""" Breaks this object into low-level objects. This method returns either
the object that it is called on, or a graph. In case of a graph, it contains
only low-level objects. Unnecessary connections are pruned. Because
this method is called on graphs while they are being constructed, returned
graphs contains no sub-graphs. Thus this method does not process subgraphs.
"""
# refactor all constituent objects
for (obj_id, o) in self.objs.copy().items():
# use items and not iteritems so that object dictionary remains mutable
# break the object into atomic (i.e. low-level) objects and
# update connections. Replace the new representation with the old
# one in the graph.
objects, connections = o.reduce()
# if x is the original object (the case with low-level objects),
# then no change must be made
if (len(objects) == 1) and (o in objects):
continue
# add the new objects
for x in objects:
self.add_object(x)
# replace existing connections
# control connection order is maintained
for c in connections:
# c is a tuple containing the old and new connections
# the new connection is a possibly empty list of new connections,
# indicating that there is no replacement
self.update_connection(c[0], c[1])
# remove the old object (and any remaining connections) from the graph
self.remove_object(o, obj_id=obj_id)
# o.id may not be the same as obj_id if the object comes from an abstraction
# a graph is reduced in-place and does not change any connections
return ({self}, [])
def cascade_expansion(self) -> None:
""" Turns implicit +~ into explicit cascading +~ trees.
It is assumed that this simplification operation is run on a reduced graph.
"""
for o in self.objs.copy().values(): # all items in the graph
for i in range(len(o.inlet_connections)): # for each inlet
# if there is more than one signal connection
# get all of the signal connections to an object at this inlet
cc = [c for c in o.inlet_connections[i] if c.is_signal]
if len(cc) > 1:
oL = HeavyIrObject("__add~f")
self.add_object(oL)
self.update_connection(
cc[0],
[Connection.copy(cc[0], to_object=oL, inlet_index=0)])
self.update_connection(
cc[1],
[Connection.copy(cc[1], to_object=oL, inlet_index=1)])
for j in range(2, len(cc)):
x = HeavyIrObject("__add~f")
self.add_object(x)
self.connect_objects(Connection(
from_object=oL,
outlet_index=0,
to_object=x,
inlet_index=0,
conn_type="~f>"))
self.update_connection(
cc[j],
[Connection.copy(cc[j], to_object=x, inlet_index=1)])
oL = x
# add a connection from the last +~ to this inlet
self.connect_objects(Connection(
from_object=oL,
outlet_index=0,
to_object=o,
inlet_index=i,
conn_type="~f>"))
if o.type == "__graph":
o.cascade_expansion()
def fma_replacement(self) -> None:
""" Replace:
[__mul~f] ~f> [__add~f] with [__fma~f] or
[__mul~f] ~f> [__sub~f] with [__fms~f]
"""
# for all __mul~f objects
for o in self.objs.copy().values():
if o.type == "__mul~f" \
and len(o.inlet_connections[0]) == 1 \
and len(o.inlet_connections[1]) == 1 \
and len(o.outlet_connections[0]) == 1 \
and (o.outlet_connections[0][0].to_object.type == "__add~f"
or (o.outlet_connections[0][0].to_object.type == "__sub~f"
and o.outlet_connections[0][0].inlet_index == 0)) \
and len(o.outlet_connections[0][0].to_object.inlet_connections[0]) == 1 \
and len(o.outlet_connections[0][0].to_object.inlet_connections[1]) == 1 \
and len(o.outlet_connections[0][0].to_object.outlet_connections[0]) > 0:
fma_type = "__fma~f" if o.outlet_connections[0][0].to_object.type == "__add~f" else "__fms~f"
fma = HeavyIrObject(fma_type)
self.add_object(fma)
# move connection to left inlet of fma~
c = o.inlet_connections[0][0]
self.update_connection(c, [c.copy(to_object=fma)])
# move connection to right inlet of fma~
c = o.inlet_connections[1][0]
self.update_connection(c, [c.copy(to_object=fma)])
o_add = o.outlet_connections[0][0].to_object
# move connection to third inlet of fma~ from +~ (not connected to *~)
i = o.outlet_connections[0][0].inlet_index # i is either 0 or 1
# i ^ 1 # use the other inlet; 0 > 1, 1 > 0
c = o_add.inlet_connections[i ^ 1][0]
self.update_connection(c, [c.copy(to_object=fma, inlet_index=2)])
# move all +~ outlet connections to fma~ outlet
for c in o_add.outlet_connections[0]:
self.connect_objects(c.copy(from_object=fma))
# remove old *~ and +~ objects from the graph
# (along with any remaining connections)
o.graph.remove_object(o) # *~
o_add.graph.remove_object(o_add) # +~
elif o.type == "__graph":
o.fma_replacement() # recurse through all subgraphs
def group_control_receivers(self) -> None:
""" Group all control receivers with the same name under one receiver
with that name. This way only one message must be scheduled to hit
all receivers.
"""
for name, receivers in self.local_vars.get_registered_objects_for_type("__receive").items():
# ensure that all receiver arguments are consistent
extern = list(set([r.args["extern"] for r in receivers]) - set([None]))
attributes = [r.args["attributes"] for r in receivers if len(r.args["attributes"]) > 0]
scope = list(set([r.annotations["scope"] for r in receivers]))
if len(extern) > 1:
self.add_error(f"Parameter \"{name}\" has conflicting extern types: {extern[0]} != {extern[1]}")
# NOTE(mhroth): not checking for conflicting scope types right now. Not sure what to do with it.
if not all(attributes[0] == a for a in attributes):
self.add_error(f"Conflicting min/max/default values for parameter \"{name}\"")
# create a new receiver
recv = HIrReceive("__receive",
args={
"name": name,
"extern": extern[0] if len(extern) > 0 else None,
"attributes": attributes[0] if len(attributes) > 0 else {},
},
annotations={"scope": scope[0]})
# add new receiver to the top level graph
self.add_object(recv)
# sort receivers by priority
receivers.sort(key=lambda x: x.args["priority"], reverse=True)
# move all connections to new receiver
for r in receivers:
for c in r.outlet_connections[0]:
self.connect_objects(c.copy(from_object=recv), require_intra_graph_connection=False)
# remove old receivers
for r in receivers:
r.graph.remove_object(r)
@property
def does_process_signal(self) -> bool:
# this graph processes a signal if it contains any adc~ (__inlet
# objects with index > 127)
return any(o.does_process_signal for o in self.objs.values()) or \
any(o.args["index"] > 127 for o in self.inlet_objs)
def order_signal_objects(self) -> None:
""" Places the signal objects in the correct order to be processed.
Only the objects in this graph are ordered, stopping at the inlet objects.
"""
# for all leaves of graph, get the parent objects and combine the lists
self.signal_order = [] # ordered objects
for o in [o for o in self.objs.values() if o.is_leaf()]:
self.signal_order.extend(o.get_parent_order())
# retain only objects that process a signal
self.signal_order = [o for o in self.signal_order if o.does_process_signal]
def assign_signal_buffers(self, buffer_pool: Optional[BufferPool] = None) -> None:
# the top-level graph owns the buffer pool
if buffer_pool is not None:
self.buffer_pool = buffer_pool
else:
self.buffer_pool = BufferPool()
# before signal buffers can be assigned, the objects must be ordered
self.order_signal_objects()
for c_list in self.inlet_connections:
c_list = [c for c in c_list if c.is_signal] # only consider signal connections to inlet
if len(c_list) == 0:
continue # no connections at this inlet, it already contains the zero buffer
if len(c_list) == 1:
c = c_list[0] # get the connection
# get the buffer at the outlet of the connected object
buf = c.from_object.outlet_buffers[c.outlet_index]
# assign the buffer to the outlet of the corresponding inlet object
inlet_obj = self.inlet_objs[c.inlet_index]
inlet_obj.outlet_buffers[0] = buf
# ensure that the retain count is accurately reflected by the inlet's connections
# some inlets are mixed with both signal and control connections, count only the signal connections
self.buffer_pool.retain_buffer(buf,
len([c for c in inlet_obj.outlet_connections[0] if c.is_signal]) - 1)
else:
raise HeavyException(
f"Object {inlet_obj} in graph {inlet_obj.graph.file} has {len(c_list)} (> 1) signal inputs.")
# for all objects in the signal order
for o in self.signal_order:
o.assign_signal_buffers(self.buffer_pool)
# assign the output buffers
for i, outlet_obj in enumerate(self.outlet_objs):
# only assign signal buffers to outlets that have incoming signal connections
c_list = [c for c in outlet_obj.inlet_connections[0] if c.is_signal]
if len(c_list) == 0:
continue
if len(c_list) == 1:
c = c_list[0] # get the connection
buf = c.from_object.outlet_buffers[c.outlet_index]
self.outlet_buffers[i] = buf
self.buffer_pool.retain_buffer(buf, len(self.outlet_connections[i]) - 1)
else:
raise HeavyException(
f"Object {outlet_obj} in graph {outlet_obj.graph.file} has {len(c_list)} (> 1) signal inputs.")
def __repr__(self) -> str:
if self.xname is not None:
# TODO(mhroth): does not handle nested subgraph
return f"__graph.{self.id}({os.path.basename(self.file)}/{self.xname})"
else:
return f"__graph.{self.id}({os.path.basename(self.file)})"
#
# Intermediate Representation generators
#
def to_ir(self) -> Optional[IRGraph]:
""" Returns Heavy intermediate representation.
"""
# the set of all input/output signal channels used by the graph
input_channel_set = self.get_input_channel_set(recursive=True)
output_channel_set = self.get_output_channel_set(recursive=True)
if self.buffer_pool is not None:
return IRGraph(
name=IRName(
escaped=re.sub(r"\W", "_", self.xname),
display=self.xname
),
objects=self.get_object_dict(),
init=IRInit(
order=self.get_ir_init_list()
),
tables=self.get_ir_table_dict(),
control=IRControl(
receivers=self.get_ir_receiver_dict(),
sendMessage=self.get_ir_control_list()
),
signal=IRSignal(
numInputBuffers=max(input_channel_set) if len(input_channel_set) > 0 else 0,
numOutputBuffers=max(output_channel_set) if len(output_channel_set) > 0 else 0,
numTemporaryBuffers=IRNumTempBuffer(
float=self.buffer_pool.num_buffers("~f>"),
integer=self.buffer_pool.num_buffers("~i>")
),
processOrder=self.get_ir_signal_list()
)
)
else:
# we should never get here
raise Exception
def get_object_dict(self) -> Dict[str, IRObjectdict]:
# d = {o.id: o.get_object_dict() for o in self.objs.values() if o.type not in
# ["inlet", "__inlet", "outlet", "__outlet"]}
d = {}
for o in self.objs.values():
# NOTE(mhroth): a bit of a hack to remove unnecessary inlet and outlet ir objects
if o.type not in ["inlet", "__inlet", "outlet", "__outlet"]:
d.update(o.get_object_dict())
return d
def get_ir_init_list(self) -> List[str]:
""" Init list is returned with all signal objects at the front,
in the order that they are processed. This is to reduce cache misses
on the signal object state as the process function is executed.
"""
init_list = [x for o in self.objs.values() for x in o.get_ir_init_list()]
signal_list = self.get_ir_signal_list()
s_init_list = [x.id for x in signal_list if x.id in init_list]
i_init_list = [o_id for o_id in init_list if o_id not in s_init_list]
ordered_init_list = s_init_list + i_init_list
# ordered_init_list = list(OrderedDict.fromkeys(s_init_list + i_init_list))
return ordered_init_list
def get_ir_on_message(self, inlet_index: int = 0) -> List[IROnMessage]:
# pass the method through the inlet object, but only follow control connections
x = []
for c in self.inlet_objs[inlet_index].outlet_connections[0]:
if c.is_control:
x.extend(c.to_object.get_ir_on_message(c.inlet_index))
return x
def get_ir_table_dict(self) -> Dict[str, IRTable]:
""" Returns a dictionary of all publicly visible tables at the root graph
and their ids.
"""
assert self.is_root_graph(), "This function should only be called from the root graph."
d = self.local_vars.get_registered_objects_for_type("__table")
# update(), because tables can be registered either as Heavy tables
# or HeavyIr __tables. We need to be able to handle them both.
d.update(self.local_vars.get_registered_objects_for_type("table"))
e = {}
for k, v in d.items():
# escape table key to be used as the value for code stubs
key = (f"_{k}") if re.match(r"\d", k) else k
if key not in e:
e[key] = IRTable(
id=v[0].id,
display=k,
hash=f"0x{HeavyLangObject.get_hash(k):X}",
extern=v[0].args["extern"]
)
return e
def get_ir_control_list(self) -> List[IRSendMessage]:
return [x for o in self.objs.values() for x in o.get_ir_control_list()]
def get_ir_receiver_dict(self) -> Dict[str, IRReceiver]:
# NOTE(mhroth): this code assumes that v is always an array of length 1,
# as the grouping of control receivers should have grouped all same-named
# receivers into one logical receiver.
# NOTE(mhroth): a code-compatible name is only necessary for externed receivers
return {((f"_{k}") if re.match(r"\d", k) else k): IRReceiver(
display=k,
hash=f"0x{HeavyLangObject.get_hash(k):X}",
extern=v[0].args["extern"],
attributes=v[0].args["attributes"],
ids=[v[0].id]
) for k, v in self.local_vars.get_registered_objects_for_type("__receive").items()}
def get_ir_signal_list(self) -> List[IRSignalList]:
return [x for o in self.signal_order for x in o.get_ir_signal_list()]
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1735754044.260823
hvcc-0.13.3/hvcc/core/hv2ir/HeavyIrObject.py 0000644 0000000 0000000 00000024516 14735300474 015443 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023-2024 Wasted Audio
#
# 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 .
import json
import os
from typing import Dict, List, Optional, TYPE_CHECKING
from .Connection import Connection
from .HeavyException import HeavyException
from .HeavyLangObject import HeavyLangObject
from .BufferPool import BufferPool
from hvcc.types.IR import HeavyIRType, IRNode, IRObjectdict, IRSendMessage, IROnMessage, IRSignalList, IRBuffer
from hvcc.types.Lang import LangLetType
if TYPE_CHECKING:
from .HeavyGraph import HeavyGraph
class HeavyIrObject(HeavyLangObject):
""" Intermediate Representation (IR) objects are atomic and have
strictly defined interfaces and types. These are generally defined in
the file heavy.ir.json.
"""
# load the HeavyIR object definitions
with open(os.path.join(os.path.dirname(__file__), "../json/heavy.ir.json"), "r") as f:
__HEAVY_OBJS_IR_DICT = HeavyIRType(**json.load(f)).root
def __init__(
self,
obj_type: str,
args: Optional[Dict] = None,
graph: Optional['HeavyGraph'] = None,
num_inlets: int = -1,
num_outlets: int = -1,
annotations: Optional[Dict] = None
) -> None:
# allow the number of inlets and outlets to be overridden
num_inlets = (len(self.__HEAVY_OBJS_IR_DICT[obj_type].inlets)
if num_inlets < 0
else num_inlets)
num_outlets = (len(self.__HEAVY_OBJS_IR_DICT[obj_type].outlets)
if num_outlets < 0
else num_outlets)
super().__init__(obj_type, args, graph, num_inlets, num_outlets, annotations)
# resolve arguments and fill in missing defaults for HeavyIR objects
self.__resolve_default_ir_args()
# the list of signal buffers at the inlets and outlets
# these are filled in by HeavyGraph.assign_signal_buffers()
self.inlet_buffers = [("zero", 0)] * self.num_inlets
self.outlet_buffers = [("zero", 0)] * self.num_outlets
# True if this object has already been ordered in the signal chain
self.__is_ordered = False
def __resolve_default_ir_args(self) -> None:
""" Resolves missing default arguments. Also checks to make sure that all
required arguments are present.
"""
if self.type in self.__HEAVY_OBJS_IR_DICT.keys():
for arg in self.__obj_desc.args:
if arg.name not in self.args:
# if a defined argument is not in the argument dictionary
if not arg.required:
# if the argument is not required, use the default
self.args[arg.name] = arg.default
else:
self.add_error(f"Required argument \"{arg.name}\" not present for object {self}.")
else:
# enforce argument types.
# if the default argument is null, don't worry about about the arg
if arg.default is not None:
self.args[arg.name] = self.force_arg_type(
self.args[arg.name],
arg.value_type,
self.graph)
@classmethod
def is_ir(cls, obj_type: str) -> bool:
"""Returns true if the type is an IR object. False otherwise.
"""
return obj_type in cls.__HEAVY_OBJS_IR_DICT.keys()
@property
def does_process_signal(self) -> bool:
"""Returns True if this object processes a signal. False otherwise.
"""
return self.__obj_desc.ir.signal
@property
def __obj_desc(self) -> IRNode:
""" Returns the original HeavyIR object description.
"""
return self.__HEAVY_OBJS_IR_DICT[self.type]
def inlet_requires_signal(self, inlet_index: int = 0) -> bool:
""" Returns True if the indexed inlet requires a signal connection. False otherwise.
"""
return self.__obj_desc.inlets[inlet_index] in {"~i>", "~f>"}
def outlet_requires_signal(self, inlet_index: int = 0) -> bool:
""" Returns True if the indexed outlet requires a signal connection. False otherwise.
"""
return self.__obj_desc.outlets[inlet_index] in {"~i>", "~f>"}
def reduce(self) -> Optional[tuple]:
# A Heavy IR object is already reduced. Returns itself and no connection changes.
return ({self}, [])
def get_parent_order(self) -> List:
""" Returns a list of all objects in process order, with this object at the end.
"""
if self.__is_ordered:
return []
else:
self.__is_ordered = True
if self.is_root():
return [self]
else:
order_list = []
for c in [c for inlet in self.inlet_connections for c in inlet]:
order_list.extend(c.from_object.get_parent_order())
order_list.append(self)
return order_list
def assign_signal_buffers(self, buffer_pool: Optional[BufferPool]) -> None:
if buffer_pool is not None:
# assign the inlet buffers
for cc in self.inlet_connections:
cc = [c for c in cc if c.is_signal] # only need to deal with signal connections
if len(cc) == 0:
continue
if len(cc) == 1:
c = cc[0] # get the connection
# get the buffer at the outlet of the connected object
buf = c.from_object.outlet_buffers[c.outlet_index]
# assign the buffer to the inlet of this object
self.inlet_buffers[c.inlet_index] = buf
# decrease the retain count of the buffer
buffer_pool.release_buffer(buf)
else:
raise HeavyException(f"This object has {len(cc)} (> 1) signal inputs.")
# assign the output buffers
exclude_set: set = set()
for i in range(self.num_outlets):
# buffers are assigned even if the outlet has no connections.
# The buffer will still be filled. However, if the buffer has already
# been set (i.e. non-zero) (e.g. in the case of dac~),
# then we skip this set
connection_type = self._resolved_outlet_type(outlet_index=i)
if Connection.is_signal_type(connection_type) and self.outlet_buffers[i][0] == "zero" \
and connection_type is not None:
b = buffer_pool.get_buffer(
connection_type,
len(self.outlet_connections[i]),
exclude_set)
self.outlet_buffers[i] = b
# if the buffer has no dependencies, make sure that it isn't reused
# right away. All outlets should have independent buffers.
if len(self.outlet_connections[i]) == 0:
exclude_set.add(b)
def _resolved_outlet_type(self, outlet_index: int = 0) -> Optional[LangLetType]:
""" Returns the connection type at the given outlet.
This information is always well-defined for IR objects.
"""
return self.__obj_desc.outlets[outlet_index]
#
# Intermediate Representation generators
#
def get_object_dict(self) -> Dict[str, IRObjectdict]:
""" Returns a dictionary of all constituent low-level objects,
indexed by id, including their arguments and type.
"""
return {
self.id: IRObjectdict(
args=self.args,
type=self.type
)
}
def get_ir_init_list(self) -> List[str]:
""" Returns a list of all object id for obejcts that need initialisation.
"""
return [self.id] if self.__obj_desc.ir.init else []
def get_ir_on_message(self, inlet_index: int = 0) -> List[IROnMessage]:
""" Returns an array of dictionaries containing the information for the
corresponding on_message call.
"""
return [IROnMessage(
id=self.id,
inletIndex=inlet_index
)]
def get_ir_control_list(self) -> List[IRSendMessage]:
""" Returns the intermediate representation for object control functions.
Basically, does sendMessage() need to be written?
"""
if self.__obj_desc.ir.control:
on_message_list = []
for connections in self.outlet_connections:
on_messages_let = []
# only look at control connections
for c in [c for c in connections if c.is_control]:
on_messages_let.extend(c.to_object.get_ir_on_message(c.inlet_index))
on_message_list.append(on_messages_let)
return [IRSendMessage(
id=self.id,
onMessage=on_message_list
)]
else:
return []
def get_ir_signal_list(self) -> List[IRSignalList]:
""" Returns the intermediate representation for object process functions.
Only outputs buffer information for lets that require a signal.
"""
# we assume that this method will only be called on signal objects
assert self.__obj_desc.ir.signal
return [IRSignalList(
id=self.id,
inputBuffers=[
IRBuffer(type=b[0], index=b[1]) for i, b in enumerate(self.inlet_buffers)
if self.inlet_requires_signal(i)
],
outputBuffers=[
IRBuffer(type=b[0], index=b[1]) for i, b in enumerate(self.outlet_buffers)
if self.outlet_requires_signal(i)
]
)]
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1735754044.260823
hvcc-0.13.3/hvcc/core/hv2ir/HeavyLangObject.py 0000644 0000000 0000000 00000041266 14735300474 015753 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023-2024 Wasted Audio
#
# 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 .
import decimal
import json
import os
import random
import string
from struct import unpack, pack
from typing import Optional, Union, List, Dict, Any, TYPE_CHECKING
from .Connection import Connection
from .HeavyException import HeavyException
from hvcc.types.compiler import CompilerMsg, CompilerNotif
from hvcc.types.Lang import HeavyLangType, LangNode, LangLet, LangLetType, LangValueType
if TYPE_CHECKING:
from .HeavyGraph import HeavyGraph
from .HeavyIrObject import HeavyIrObject
class HeavyLangObject:
""" This is the base Heavy object class.
"""
__RANDOM = random.Random()
__ID_CHARS = string.ascii_letters + string.digits
# load the Heavy object definitions
with open(os.path.join(os.path.dirname(__file__), "../json/heavy.lang.json"), "r") as f:
_HEAVY_LANG_DICT = HeavyLangType(**json.load(f)).root
def __init__(
self,
obj_type: str,
args: Optional[Dict] = None,
graph: Optional['HeavyGraph'] = None,
num_inlets: int = -1,
num_outlets: int = -1,
annotations: Optional[Dict] = None
) -> None:
# set the object type
self.type = obj_type
# generate a unique id for this object
self.id = "".join(self.__RANDOM.choice(self.__ID_CHARS) for _ in range(8))
# assign the parent graph
self.graph = graph
# set local arguments
self.args: Dict = args or {}
# set local annotations
self.annotations: Dict = annotations or {}
# a list of locally generated warnings and errors (notifications)
self.warnings: List[CompilerMsg] = []
self.errors: List[CompilerMsg] = []
# resolve arguments and fill in missing defaults for HeavyLang objects
self.__resolve_default_lang_args()
# the list of connections at each inlet
num_inlets = num_inlets if num_inlets >= 0 else len(self._obj_desc.inlets)
self.inlet_connections: List = [[] for _ in range(num_inlets)]
# the list of connections at each outlet
num_outlets = num_outlets if num_outlets >= 0 else len(self._obj_desc.outlets)
self.outlet_connections: List = [[] for _ in range(num_outlets)]
@property
def scope(self) -> str:
""" Returns the scope of this object, private by default.
Scope may be public, protected, private.
"""
return self.annotations.get("scope", "private")
@property
def static(self) -> bool:
""" Returns true of this object is marked as static. False by default.
"""
return self.annotations.get("static", False)
@property
def const(self) -> bool:
""" Returns true of this object is marked as constant. False by default.
"""
return self.annotations.get("const", False)
@property
def name(self) -> Optional[str]:
""" Returns the name of this object. Returns None if there is no name.
"""
return self.args.get("name", None)
@property
def _obj_desc(self) -> LangNode:
""" Returns the HeavyLang object description.
"""
return self._HEAVY_LANG_DICT[self.type]
def inlet_connection_type(self, index: int) -> LangLet:
return self._obj_desc.inlets[index]
def outlet_connection_type(self, index: int) -> LangLet:
return self._obj_desc.outlets[index]
def name_for_arg(self, index: int = 0) -> str:
""" Returns the name of the argument at the given index.
"""
return self._obj_desc.args[index].name
def add_warning(self, warning: str) -> None:
""" Add a warning to this object.
"""
self.warnings.append(CompilerMsg(message=warning))
def add_error(self, error: str) -> None:
""" Add an error to this object and raise an exception.
"""
self.errors.append(CompilerMsg(message=error))
raise HeavyException(error)
def get_notices(self) -> CompilerNotif:
""" Returns a dictionary of all warnings and errors at this object.
"""
return CompilerNotif(
has_error=len(self.errors) > 0,
warnings=[CompilerMsg(message=f"{self}: {n.message}") for n in self.warnings],
errors=[CompilerMsg(message=f"{self}: {n.message}") for n in self.errors],
)
@classmethod
def force_arg_type(
cls,
value: LangValueType,
value_type: Optional[str] = None,
graph: Optional['HeavyGraph'] = None
) -> Any:
""" Attempts to convert a value to a given value type. Raises an Exception otherwise.
If the value_type is unknown and a graph is provided, a warning will be registered.
"""
if value_type == "float":
try:
return float(value)
except Exception:
raise Exception(f"Cannot convert argument \"{value}\" into float.")
elif value_type == "int":
return int(decimal.Decimal(value))
elif value_type == "string":
return str(value) if value is not None else None
elif value_type == "bool":
if isinstance(value, str):
return value.strip().lower() not in ["false", "f", "0"]
else:
return bool(value)
elif value_type == "floatarray":
if isinstance(value, list):
return [float(v) for v in value]
if isinstance(value, str):
return [float(v) for v in value.split()]
else:
raise HeavyException(f"Cannot convert value to type floatarray: {value}")
elif value_type == "intarray":
if isinstance(value, list):
return [int(v) for v in value]
if isinstance(value, str):
return [int(v) for v in value.split()]
else:
raise HeavyException(f"Cannot convert value to type intarray: {value}")
elif value_type == "stringarray":
if isinstance(value, list):
return [str(v) for v in value]
if isinstance(value, str):
return [str(v) for v in value.split()]
else:
raise HeavyException(f"Cannot convert value to type stringarray: {value}")
else:
# NOTE(mhroth): if value_type is not a known type or None, that is
# not necessarily an error. It may simply be that the value should
# not be resolved to anything other than what it already is.
# This happens most often with message objects.
# if graph is not None:
# graph.add_warning(f"Unknown value type \"{value_type}\" for value: {value}")
return value
def __resolve_default_lang_args(self) -> None:
""" Resolves missing default arguments. Also checks to make sure that all
required arguments are present. Does nothing if the object is IR.
"""
if self.type in self._HEAVY_LANG_DICT.keys():
for arg in self._obj_desc.args:
if arg.name not in self.args:
# if a defined argument is not in the argument dictionary
if not arg.required:
# if the argument is not required, use the default
self.args[arg.name] = arg.default
else:
self.add_error(f"Required argument \"{arg.name}\" not present for object {self}.")
else:
# enforce argument types
self.args[arg.name] = self.force_arg_type(
self.args[arg.name],
arg.value_type,
self.graph)
@property
def num_inlets(self) -> int:
return len(self.inlet_connections)
@property
def num_outlets(self) -> int:
return len(self.outlet_connections)
def add_connection(self, c: Connection) -> None:
""" Add a connection to this object.
"""
try:
if c.to_object is self:
self.inlet_connections[c.inlet_index].append(c)
elif c.from_object is self:
self.outlet_connections[c.outlet_index].append(c)
else:
raise HeavyException(f"Connection {c} does not connect to this object {self}.")
except Exception:
raise HeavyException(f"Connection {c} connects to out-of-range let.")
def remove_connection(self, c: Connection) -> None:
""" Remove a connection to this object.
"""
if c.to_object is self:
self.inlet_connections[c.inlet_index].remove(c)
elif c.from_object is self:
self.outlet_connections[c.outlet_index].remove(c)
else:
raise HeavyException(f"Connection {c} does not connect to this object {self}.")
def replace_connection(self, c: Connection, n_list: List) -> None:
""" Replaces connection c with connection list n_list, maintaining connection order
"""
if c.from_object is self:
cc = self.outlet_connections[c.outlet_index]
# NOTE(mhroth): this will throw an exception if c does not exist
# if a heavy object connects to itself, such as through a [t a]
# or directly, there may be an error here,
i = cc.index(c)
self.outlet_connections[c.outlet_index] = cc[0:i] + n_list + cc[i + 1:]
elif c.to_object is self:
# connection order doesn't matter at the inlet
self.inlet_connections[c.inlet_index].remove(c)
self.inlet_connections[c.inlet_index].extend(n_list)
else:
raise HeavyException(f"Connections must have a common endpoint: {c} / {n_list}")
def get_connection_move_list(self, o: 'HeavyIrObject', connection_type_filter: str = "-~>") -> List:
""" Create a list of commands to move all connections from this object
to the given object o.
"""
m = []
for c in [c for cc in self.inlet_connections for c in cc]:
m.append((c, [c.copy(to_object=o)]))
for c in [c for cc in self.outlet_connections for c in cc]:
m.append((c, [c.copy(from_object=o)]))
return m
def _get_connection_format(self, connections_list: List) -> str:
fmt = []
for cc in connections_list:
s = {c.type for c in cc}
if len(s) == 0:
fmt.append("_")
elif len(s) == 1:
if "-->" in s:
fmt.append("c")
elif "~f>" in s:
fmt.append("f")
elif "~i>" in s:
fmt.append("i")
else:
raise Exception(f"Unknown connection type in set {cc} in file {cc[0].from_object.graph.file}.")
elif s in [{"~f>", "-->"}, {"~i>", "-->"}]:
fmt.append("m")
else:
fmt.append("m")
return "".join(fmt)
def has_inlet_connection_format(self, fmts: Optional[Union[str, List]] = None) -> bool:
""" Returns true if the object has given format at its inlets.
"""
fmts = fmts if isinstance(fmts, list) else [fmts]
return self._get_connection_format(self.inlet_connections) in fmts
def has_outlet_connection_format(self, fmts: Optional[Union[str, List]] = None) -> bool:
""" Returns true if the object has given format at its outlets.
Take either litteral or list as input.
"""
fmts = fmts if isinstance(fmts, list) else [fmts]
return self._get_connection_format(self.outlet_connections) in fmts
def is_leaf(self) -> bool:
""" Returns True if this object is a leaf in the graph. False otherwise.
"""
return all(len(c) == 0 for c in self.outlet_connections)
def is_root(self) -> bool:
""" Returns True if this object is a root in the graph. False otherwise.
"""
return all(len(c) == 0 for c in self.inlet_connections)
def _resolved_outlet_type(self, outlet_index: int = 0) -> Optional[LangLetType]:
""" Returns the connection type expected at the given outlet.
The result may be influenced by the state of the input connections.
"""
# get the defined connection type
connection_type = self._obj_desc.outlets[outlet_index].connectionType
if connection_type == "-~>" and self.graph is not None:
# if the connection type is defined as mixed,
# use the default approach to resolve it
connection_type_set = {c.type for c in self.inlet_connections[0]}
if len(connection_type_set) == 0:
return "-->"
elif len(connection_type_set) == 1:
return list(connection_type_set)[0]
elif len(connection_type_set) == 2:
if {"-->", "~f>"} == connection_type_set:
return "~f>"
elif {"-->", "~i>"} == connection_type_set:
return "~i>"
# if the connection type cannot be resolved to a well-defined type
self.graph.add_error("Connection type cannot be resolved."
f"Unknown inlet connection type configuration: {connection_type_set}")
else:
return connection_type
return None
def _resolve_connection_types(self, obj_stack: Optional[set] = None) -> None:
""" Resolves the type of all connections before reduction to IR object types.
If connections incident on an object are incompatible, they are either
resolved, potentially by inserting conversion objects, or pruned.
If a viable resolution cannot be found, an exception may be raised.
"""
obj_stack = obj_stack or set()
if self in obj_stack:
return
else:
obj_stack.add(self)
# for all outgoing connections
if self.graph is not None:
for c in [c for cc in self.outlet_connections for c in cc]:
if c.is_mixed:
connection_type = self._resolved_outlet_type(outlet_index=c.outlet_index)
if connection_type == "-~>":
self.graph.add_error("Cannot resolve connection type from -~>.")
else:
self.graph.update_connection(c, [c.copy(type=connection_type)])
c.to_object._resolve_connection_types(obj_stack) # turtle all the way down
@classmethod
def get_hash(cls, x: str) -> int:
""" Compute the message element hash used by msg_getHash(). Returns a 32-bit integer.
"""
if isinstance(x, float) or isinstance(x, int):
# interpret the float bytes as an unsigned integer
return unpack("@I", pack("@f", float(x)))[0]
elif x == "bang":
return 0xFFFFFFFF
elif isinstance(x, str):
# this hash is based MurmurHash2
# http://en.wikipedia.org/wiki/MurmurHash
# https://sites.google.com/site/murmurhash/
x = str(x)
m = 0x5bd1e995
r = 24
h = len(x)
i = 0
while i < len(x) & ~0x3:
k = unpack("@I", bytes(x[i:i + 4], "utf-8"))[0]
k = (k * m) & 0xFFFFFFFF
k ^= k >> r
k = (k * m) & 0xFFFFFFFF
h = (h * m) & 0xFFFFFFFF
h ^= k
i += 4
n = len(x) & 0x3
x = x[i:i + n]
if n >= 3:
h ^= (ord(x[2]) << 16) & 0xFFFFFFFF
if n >= 2:
h ^= (ord(x[1]) << 8) & 0xFFFFFFFF
if n >= 1:
h ^= ord(x[0])
h = (h * m) & 0xFFFFFFFF
h ^= h >> 13
h = (h * m) & 0xFFFFFFFF
h ^= h >> 15
return h
else:
raise Exception("Message element hashes can only be computed for float and string types.")
def __repr__(self) -> str:
arg_str = " ".join([f"{k}:{o}" for (k, o) in self.args.items()])
return f"{self.type} {{{arg_str}}}" if len(arg_str) > 0 else self.type
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1735983617.1410897
hvcc-0.13.3/hvcc/core/hv2ir/HeavyParser.py 0000644 0000000 0000000 00000027021 14736201001 015153 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023-2024 Wasted Audio
#
# 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 .
import json
import random
import os
from typing import Any, Dict, List, Optional, Set, Tuple
from .HIrConvolution import HIrConvolution
from .HIrInlet import HIrInlet
from .HIrLorenz import HIrLorenz
from .HIrOutlet import HIrOutlet
from .HIrPack import HIrPack
from .HIrSwitchcase import HIrSwitchcase
from .HIrTabhead import HIrTabhead
from .HIrTabread import HIrTabread
from .HIrTabwrite import HIrTabwrite
from .HLangAdc import HLangAdc
from .HLangBinop import HLangBinop
from .HLangBiquad import HLangBiquad
from .HLangDac import HLangDac
from .HLangDelay import HLangDelay
# from .HLangIf import HLangIf # circular import. moved here
from .HeavyException import HeavyException
from .HeavyIrObject import HeavyIrObject
from .HeavyLangObject import HeavyLangObject
from .HLangLine import HLangLine
from .HLangMessage import HLangMessage
# from .HLangNoise import HLangNoise # circular import. moved here
from .HLangPhasor import HLangPhasor
from .HLangPrint import HLangPrint
from .HLangReceive import HLangReceive
from .HLangRandom import HLangRandom
from .HLangSend import HLangSend
from .HLangSequence import HLangSequence
from .HLangSlice import HLangSlice
from .HLangSystem import HLangSystem
from .HLangTable import HLangTable
from .HLangUnop import HLangUnop
from .HLangVar import HLangVar
from .HLangVario import HLangVario
from .HeavyGraph import HeavyGraph
from .Connection import Connection
class HeavyParser:
@classmethod
def graph_from_file(
cls,
hv_file: str,
graph: Optional[HeavyGraph] = None,
graph_args: Optional[Dict] = None,
path_stack: Optional[set] = None,
xname: Optional[str] = None
) -> HeavyGraph:
""" Read a graph object from a file.
@param graph The parent graph of this graph.
@param graph_args The arguments to this graph, in the form of a completely
resolved dictionary.
@param path_stack The path_stack is the current stack of resolved abstractions.
It prevents infinite recursion when reading many abstractions deep.
"""
# ensure that we have an absolute path to the hv_file
hv_file = os.path.abspath(os.path.expanduser(hv_file))
# copy the path stack such that no changes are made to the calling stack
path_stack = path_stack or set()
if hv_file in path_stack:
raise HeavyException(f"Abstraction recursion detected. Rereading {hv_file} on stack {path_stack}.")
else:
path_stack.add(hv_file)
# open and parse the heavy file
with open(hv_file, "r") as f:
json_heavy = json.load(f)
return cls.graph_from_object(hv_file, json_heavy, path_stack, graph, graph_args, xname)
@classmethod
def graph_from_object(
cls,
hv_file: str,
json_heavy: Dict,
path_stack: set,
graph: Optional[HeavyGraph] = None,
graph_args: Optional[Dict] = None,
xname: Optional[str] = None
) -> HeavyGraph:
""" Parse a graph object.
@param graph The parent graph.
@param graph_args The resolved arguments to this graph, in the form of a dictionary.
@param hv_file The Heavy file where this graph can be found.
"""
# resolve default graph arguments
graph_args = graph_args or {}
for a in json_heavy["args"]:
if a["name"] not in graph_args:
if a["required"]:
raise HeavyException(f"Required argument \"{a['name']}\" not present.")
else:
graph_args[a["name"]] = a["default"]
else:
# just to be safe, ensure that the argument type is correct
graph_args[a["name"]] = HeavyLangObject.force_arg_type(
graph_args[a["name"]],
a["value_type"],
graph=graph)
# create a new graph
subpatch_name = json_heavy.get("annotations", {}).get("name", xname)
g = HeavyGraph(graph, graph_args, file=hv_file, xname=subpatch_name)
# add the import paths to the global vars
g.local_vars.add_import_paths(json_heavy.get("imports", []))
# add the file's relative directory to global vars
g.local_vars.add_import_paths([os.path.dirname(hv_file)])
# instantiate all objects
try:
for obj_id, o in json_heavy["objects"].items():
if o["type"] == "comment":
continue # first and foremost, ignore comment objects
elif o["type"] == "graph":
# inline HeavyGraph objects (i.e. subgraphs)
# require a different set of initialisation arguments
x: Any = cls.graph_from_object(hv_file, o, path_stack, g, g.args, xname)
else:
# resolve the arguments dictionary based on the graph args
args = g.resolve_arguments(o["args"])
# before anything, search for an abstraction
# in case we want to override default functionality
# However, if we are in an abstraction that has the same
# name as the type that we are looking for, don't recurse!
abs_path = g.find_path_for_abstraction(o["type"])
if abs_path is not None and abs_path not in path_stack:
x = cls.graph_from_file(
hv_file=abs_path,
graph=g,
graph_args=args,
path_stack=path_stack)
# if we know how to handle this object type natively
# either as a custom type or as a generic IR object
elif HeavyParser.get_class_for_type(o["type"]) is not None:
obj_cls = HeavyParser.get_class_for_type(o["type"])
x = obj_cls(o["type"], args, g, o.get("annotations", {}))
# handle generic IR objects
elif HeavyIrObject.is_ir(o["type"]):
x = HeavyIrObject(o["type"], args, g, annotations=o.get("annotations", {}))
# an object definition can't be found
else:
g.add_error(f"Object type \"{o['type']}\" cannot be found.")
# note that add_error() raises an exception. So really, there is no continue.
continue
# add the new object to the graph's object dictionary
g.add_object(x, obj_id)
# parse all of the connections
for c in json_heavy["connections"]:
g.connect_objects(Connection(
g.objs[c["from"]["id"]],
c["from"]["outlet"],
g.objs[c["to"]["id"]],
c["to"]["inlet"],
c["type"]))
except HeavyException as e:
if g.is_root_graph():
# add the notification dictionary at the top level
e.notes = g.get_notices()
e.notes.has_error = True
e.notes.exception = e
raise e
if (g.graph is None) or (g.graph.file != g.file):
# remove this graph from the stack when finished.
# Subpatches should not remove themselves.
path_stack.remove(g.file)
return g
@classmethod
def get_class_for_type(cls, obj_type: str) -> Any:
""" Returns the class which can handle the given object type.
"""
if HLangUnop.handles_type(obj_type):
return HLangUnop
elif HLangBinop.handles_type(obj_type):
return HLangBinop
elif obj_type in LANG_CLASS_DICT:
return LANG_CLASS_DICT[obj_type]
else:
return None
class HLangIf(HeavyLangObject):
""" Translates HeavyLang object [if] to HeavyIR [if] or [if~].
"""
def __init__(
self,
obj_type: str,
args: Dict,
graph: HeavyGraph,
annotations: Optional[Dict] = None
) -> None:
assert obj_type == "if"
super().__init__("if", args, graph, num_inlets=2, num_outlets=2, annotations=annotations)
def reduce(self) -> Tuple[Set, List]:
if self.has_inlet_connection_format(["cc", "_c", "c_", "__"]):
x = HeavyIrObject("__if", self.args)
elif self.has_inlet_connection_format("ff"):
# TODO(mhroth): implement this
x = HeavyParser.graph_from_file("./hvlib/if~f.hv.json")
elif self.has_inlet_connection_format("ii"):
# TODO(mhroth): implement this
x = HeavyParser.graph_from_file("./hvlib/if~i.hv.json")
else:
fmt = self._get_connection_format(self.inlet_connections)
raise HeavyException(f"Unhandled connection configuration to object [if]: {fmt}")
return ({x}, self.get_connection_move_list(x))
class HLangNoise(HeavyLangObject):
""" Handles the HeavyLang "noise" object.
"""
def __init__(
self,
obj_type: str,
args: Dict,
graph: HeavyGraph,
annotations: Optional[Dict] = None
) -> None:
assert obj_type == "noise"
super().__init__("noise", args, graph, num_inlets=1, num_outlets=1, annotations=annotations)
def reduce(self) -> Tuple[Set, List]:
seed = int(random.uniform(1, 2147483647)) # assign a random 32-bit seed
noise_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "./hvlib/noise.hv.json")
x = HeavyParser.graph_from_file(noise_path, graph_args={"seed": seed})
x.reduce()
# TODO(mhroth): deal with control input
return ({x}, self.get_connection_move_list(x))
# A list of all of the HeavyLang objects and the classes
# that will translate them into HeavyIR objects.
LANG_CLASS_DICT = {
"__conv~f": HIrConvolution,
"biquad": HLangBiquad,
"if": HLangIf,
"inlet": HIrInlet,
"var": HLangVar,
"vario": HLangVario,
"outlet": HIrOutlet,
"__lorenz~f": HIrLorenz,
"print": HLangPrint,
"sequence": HLangSequence,
"adc": HLangAdc,
"dac": HLangDac,
"message": HLangMessage,
"noise": HLangNoise,
"system": HLangSystem,
"phasor": HLangPhasor,
"line": HLangLine,
"random": HLangRandom,
"delay": HLangDelay,
"table": HLangTable,
"slice": HLangSlice,
"__tabread~if": HIrTabread,
"__tabread~f": HIrTabread,
"__tabread_stoppable~f": HIrTabread,
"__tabreadu~f": HIrTabread,
"__tabread": HIrTabread,
"__tabhead~f": HIrTabhead,
"__tabhead": HIrTabhead,
"__tabwrite~f": HIrTabwrite,
"__tabwrite_stoppable~f": HIrTabwrite,
"__tabwrite": HIrTabwrite,
"receive": HLangReceive,
"send": HLangSend,
"__switchcase": HIrSwitchcase,
"switchcase": HIrSwitchcase,
"__pack": HIrPack
}
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1727976067.2635524
hvcc-0.13.3/hvcc/core/hv2ir/LocalVars.py 0000644 0000000 0000000 00000007562 14677551203 014640 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023 Wasted Audio
#
# 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 .
import os
from collections import defaultdict
from typing import Optional, Union, Dict, List
from .HeavyException import HeavyException
from .HeavyLangObject import HeavyLangObject
class LocalVars:
""" A set of scoped objects.
"""
def __init__(self, stdlib_dir: str = "./") -> None:
# a dictionary of name-registered objects
# the data structure is a list map
# key is the name under which the object is registered, value is a
# list of all objects who are registered with that name
self.__REGISTERED_OBJ_DICT: Dict = defaultdict(list)
# the list of globally declared paths
self.declared_paths = [stdlib_dir] # initialise with the standard library directory
def find_path_for_abstraction(self, name: str) -> Optional[str]:
# the file name based on the abstraction name
file_name = f"{name}.hv.json"
# iterate in order through the declared paths in order to find the file
for d in self.declared_paths:
file_path = os.path.join(d, file_name)
if os.path.exists(file_path):
return file_path # if a matching abstraction is found, return the path
return None # otherwise return None
def add_import_paths(self, path_list: List) -> None:
""" Add import paths. Paths are expanded and made as explicit as possible.
"""
self.declared_paths.extend([os.path.abspath(os.path.expanduser(p)) for p in path_list])
def register_object(self, obj: HeavyLangObject, name: str, static: bool = False, unique: bool = False) -> None:
""" Registers a named object.
"""
objs = self.get_objects_for_name(name, obj.type)
if (unique and len(objs) > 0) or (static and len(objs) > 1):
# if there is already a registered object of this type and the new
# object is not declared as static, throw an error
raise HeavyException(f"Object {obj} with name \"{name}\" already exists, "
"and the new object is static or unique.")
elif len(objs) == 1 and static:
pass # the static object has already been registered, move on
else:
self.__REGISTERED_OBJ_DICT[name].append(obj)
def unregister_object(self, obj: HeavyLangObject, name: str) -> None:
""" Unregisters a named object.
"""
self.__REGISTERED_OBJ_DICT[name].remove(obj)
def get_objects_for_name(self, name: str, obj_types: Union[str, List]) -> List:
""" Returns a list of objects registered under a name in this scope.
"""
obj_types = obj_types if isinstance(obj_types, list) else [obj_types]
return [o for o in self.__REGISTERED_OBJ_DICT[name] if o.type in obj_types]
def get_registered_objects_for_type(self, obj_type: str) -> Dict:
""" Returns a list-dictionary for all objects of the given type, indexed by name.
"""
d = defaultdict(list)
for k, v in self.__REGISTERED_OBJ_DICT.items():
x = [o for o in v if o.type == obj_type]
if len(x) > 0: # only return names that actually have associated objects
d[k].extend(x)
return d
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1685549294.7492065
hvcc-0.13.3/hvcc/core/hv2ir/__init__.py 0000644 0000000 0000000 00000000000 14435670357 014471 0 ustar 00 ././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1735754044.261823
hvcc-0.13.3/hvcc/core/hv2ir/hv2ir.py 0000644 0000000 0000000 00000012752 14735300474 013776 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023-2024 Wasted Audio
#
# 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 .
import argparse
import json
import os
import time
from typing import Optional
from .HeavyException import HeavyException
from .HeavyParser import HeavyParser
from hvcc.types.compiler import CompilerResp, CompilerNotif, CompilerMsg
class hv2ir:
@classmethod
def compile(
cls,
hv_file: str,
ir_file: str,
patch_name: Optional[str] = None,
verbose: bool = False
) -> CompilerResp:
""" Compiles a HeavyLang file into a HeavyIR file.
Returns a tuple of compile time in seconds, a notification dictionary,
and a heavy object counter.
"""
# keep track of the total compile time
tick = time.time()
hv_file = os.path.abspath(os.path.expanduser(hv_file))
ir_file = os.path.abspath(os.path.expanduser(ir_file))
try:
# parse heavy file
hv_graph = HeavyParser.graph_from_file(hv_file=hv_file, xname=patch_name)
except HeavyException as e:
return CompilerResp(
stage="hv2ir",
compile_time=time.time() - tick,
notifs=CompilerNotif(
has_error=True,
exception=e,
errors=[CompilerMsg(message=e.message)],
warnings=[]
),
in_file=os.path.basename(hv_file),
in_dir=os.path.dirname(hv_file),
out_file=os.path.basename(ir_file),
out_dir=os.path.dirname(ir_file)
)
try:
# get a counter of all heavy objects
hv_counter = hv_graph.get_object_counter(recursive=True)
# prepare the graph for exporting
hv_graph.prepare()
# ensure that the output directory exists
if not os.path.exists(os.path.dirname(ir_file)):
os.makedirs(os.path.dirname(ir_file))
# generate Heavy.IR
ir = hv_graph.to_ir()
except HeavyException as e:
return CompilerResp(
stage="hv2ir",
compile_time=time.time() - tick,
notifs=CompilerNotif(
has_error=True,
exception=e,
errors=[CompilerMsg(message=e.message)],
warnings=[]
),
in_file=os.path.basename(hv_file),
in_dir=os.path.dirname(hv_file),
out_file=os.path.basename(ir_file),
out_dir=os.path.dirname(ir_file),
obj_counter=hv_counter
)
if ir is not None:
# write the hv.ir file
with open(ir_file, "w") as f:
if verbose:
json.dump(
ir.model_dump(),
f,
sort_keys=True,
indent=2,
separators=(",", ": "))
else:
json.dump(ir.model_dump(), f)
if verbose and ir is not None:
if len(ir.signal.processOrder) > 0:
print("")
print("=== Signal Order ===")
for so in ir.signal.processOrder:
o = ir.objects[so.id]
if len(o.args) > 0:
print("{0} {{{1}}}".format(
o.type,
" ".join([f"{k}:{v}" for k, v in o.args.items()])))
else:
print(o.type)
return CompilerResp(
stage="hv2ir",
compile_time=time.time() - tick, # record the total compile time
notifs=hv_graph.get_notices(),
in_file=os.path.basename(hv_file),
in_dir=os.path.dirname(hv_file),
out_file=os.path.basename(ir_file),
out_dir=os.path.dirname(ir_file),
obj_counter=hv_counter,
ir=ir
)
def main() -> None:
parser = argparse.ArgumentParser(
description="A C-language compiler for the Heavy audio programming language.")
parser.add_argument(
"hv_path",
help="The patch to the top-level patch to compile.")
parser.add_argument(
"--hv_ir_path",
default="./heavy.hv.ir.json",
help="The output path of the hv.ir.json file.")
parser.add_argument(
"--name",
default="heavy",
help="")
parser.add_argument("-v", "--verbose", action="count")
args = parser.parse_args()
d = hv2ir.compile(
hv_file=args.hv_path,
ir_file=args.hv_ir_path,
patch_name=args.name,
verbose=args.verbose)
if args.verbose:
print(f"Total hv2ir time: {(d.compile_time * 1000):.2f}ms")
if __name__ == "__main__":
main()
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1735983617.1410897
hvcc-0.13.3/hvcc/core/json/heavy.ir.json 0000644 0000000 0000000 00000142351 14736201001 014713 0 ustar 00 {
"__^": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"name": "k",
"value_type": "float",
"description": "",
"default": 0,
"required": false
}]
},
"__^_k": {
"inlets": [
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"name": "k",
"value_type": "float",
"description": "",
"default": 0,
"required": false
}]
},
"__abs": {
"inlets": [
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": false
},
"outlets": [
"-->"
],
"args": [
]
},
"__neg~f": {
"inlets": [
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
],
"perf": {
"avx": 0.5,
"sse": 0.5
},
"args": [
]
},
"__abs~f": {
"inlets": [
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
],
"perf": {
"avx": 0.5,
"sse": 0.5
},
"args": [
]
},
"__abs~i": {
"inlets": [
"~i>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~i>"
]
},
"__acos": {
"inlets": [
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": false
},
"outlets": [
"-->"
],
"args": [
]
},
"__acosh": {
"inlets": [
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": false
},
"outlets": [
"-->"
],
"args": [
]
},
"__acosh~f": {
"inlets": [
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
]
},
"__acos~f": {
"inlets": [
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
]
},
"__add": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"name": "k",
"value_type": "float",
"description": "",
"default": 0,
"required": false
}]
},
"__add_k": {
"inlets": [
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"name": "k",
"value_type": "float",
"description": "",
"default": 0,
"required": false
}]
},
"__add~f": {
"ir": {
"control": false,
"signal": true,
"init": false
},
"inlets": [
"~f>",
"~f>"
],
"outlets": [
"~f>"
],
"perf": {
"avx": 3,
"sse": 3
},
"args": [
]
},
"__add~i": {
"ir": {
"control": false,
"signal": true,
"init": false
},
"inlets": [
"~i>",
"~i>"
],
"outlets": [
"~i>"
],
"perf": {
"avx": 2,
"sse": 0.5
},
"args": [{
"name": "k",
"value_type": "float",
"description": "",
"default": 0,
"required": false
}]
},
"__and": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"default": 0,
"value_type": "int",
"name": "k",
"description": "constant",
"required": false
}]
},
"__and_k": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"default": 0,
"value_type": "int",
"name": "k",
"description": "constant",
"required": false
}]
},
"__and~f": {
"inlets": [
"~f>",
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
],
"args": [
],
"perf": {
"avx": 0.5,
"sse": 0.5
}
},
"__andnot~f": {
"inlets": [
"~f>",
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
],
"args": [
],
"perf": {
"avx": 0.5,
"sse": 0.5
}
},
"__asin": {
"inlets": [
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": false
},
"outlets": [
"-->"
]
},
"__asinh": {
"inlets": [
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": false
},
"outlets": [
"-->"
]
},
"__atan": {
"inlets": [
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": false
},
"outlets": [
"-->"
],
"args": [
]
},
"__atan2": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"default": 1,
"value_type": "float",
"name": "y",
"description": "",
"required": false
}]
},
"__atan2_k": {
"inlets": [
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"default": 0,
"value_type": "float",
"name": "y",
"description": "",
"required": false
}]
},
"__atan2~f": {
"inlets": [
"~f>",
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
]
},
"__atanh": {
"inlets": [
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": false
},
"outlets": [
"-->"
],
"args": [
]
},
"__bimod": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [
]
},
"__bimod_k": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": false
},
"outlets": [
"-->"
],
"args": [
]
},
"__biquad_k~f": {
"inlets": [
"~f>",
"-->",
"-->",
"-->",
"-->",
"-->"
],
"args": [{
"name": "ff0",
"value_type": "float",
"description": "",
"default": 1,
"required": false
}, {
"name": "ff1",
"value_type": "float",
"description": "",
"default": 0,
"required": false
}, {
"name": "ff2",
"value_type": "float",
"description": "",
"default": 0,
"required": false
}, {
"name": "fb1",
"value_type": "float",
"description": "",
"default": 0,
"required": false
}, {
"name": "fb2",
"value_type": "float",
"description": "",
"default": 0,
"required": false
}],
"ir": {
"control": false,
"signal": true,
"init": true
},
"outlets": [
"~f>"
],
"perf": {
"avx": 30,
"sse": 15
}
},
"__biquad~f": {
"inlets": [
"~f>",
"~f>",
"~f>",
"~f>",
"~f>",
"~f>"
],
"args": [
],
"ir": {
"control": false,
"signal": true,
"init": true
},
"outlets": [
"~f>"
],
"perf": {
}
},
"__cast_b": {
"inlets": [
"-->"
],
"args": [
],
"ir": {
"control": true,
"signal": false,
"init": false
},
"outlets": [
"-->"
]
},
"__cast_f": {
"inlets": [
"-->"
],
"args": [
],
"ir": {
"control": true,
"signal": false,
"init": false
},
"outlets": [
"-->"
]
},
"__cast_s": {
"inlets": [
"-->"
],
"args": [
],
"ir": {
"control": true,
"signal": false,
"init": false
},
"outlets": [
"-->"
]
},
"__cast~fi": {
"args": [
],
"inlets": [
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~i>"
],
"perf": {
"avx": 1,
"sse": 1
}
},
"__cast~if": {
"args": [
],
"inlets": [
"~i>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
],
"perf": {
"avx": 1,
"sse": 1
}
},
"__ceil": {
"inlets": [
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": false
},
"outlets": [
"-->"
],
"args": [
]
},
"__ceil~f": {
"inlets": [
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
],
"perf": {
"avx": 1,
"sse": 1
}
},
"__conv~f": {
"inlets": [
"~f>",
"-->",
"-->"
],
"ir": {
"control": false,
"signal": true,
"init": true
},
"outlets": [
"~f>"
],
"args": [{
"name": "table",
"value_type": "string",
"description": "",
"default": "",
"required": true
}, {
"name": "size",
"value_type": "float",
"description": "",
"default": 0,
"required": true
}],
"perf": {
"avx": 0,
"sse": 0
}
},
"__cos": {
"inlets": [
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": false
},
"outlets": [
"-->"
],
"args": [
]
},
"__cosh": {
"inlets": [
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": false
},
"outlets": [
"-->"
],
"args": [
]
},
"__cosh~f": {
"inlets": [
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
]
},
"__cos~f": {
"inlets": [
"~f>"
],
"args": [
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
]
},
"__cpole~f": {
"inlets": [
"~f>",
"~f>",
"~f>",
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": true
},
"outlets": [
"~f>",
"~f>"
],
"args": [
],
"perf": {
"avx": 143,
"sse": 53
}
},
"__del1~f": {
"inlets": [
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": true
},
"outlets": [
"~f>"
],
"args": [
],
"perf": {
"avx": 5,
"sse": 2
}
},
"__delay": {
"inlets": [
"-->",
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"default": 0,
"value_type": "float",
"name": "delay",
"description": "The message delay in milliseconds.",
"required": false
}]
},
"__div": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"name": "k",
"value_type": "float",
"description": "",
"default": 0,
"required": false
}]
},
"__div_k": {
"inlets": [
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"name": "k",
"value_type": "float",
"description": "",
"default": 0,
"required": false
}]
},
"__div~f": {
"inlets": [
"~f>",
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
],
"args": [{
"name": "k",
"value_type": "float",
"description": "",
"default": 0,
"required": false
}],
"perf": {
"avx": 25,
"sse": 25
}
},
"__div~i": {
"inlets": [
"~i>",
"~i>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~i>"
],
"args": [{
"name": "k",
"value_type": "float",
"description": "",
"default": 0,
"required": false
}]
},
"__env~f": {
"inlets": [
"~f>"
],
"ir": {
"control": true,
"signal": true,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"name": "windowSize",
"value_type": "float",
"description": "",
"default": 1024,
"required": false
}, {
"name": "period",
"value_type": "float",
"description": "",
"default": 512,
"required": false
}],
"perf": {
"avx": 9,
"sse": 9
}
},
"__eq": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"default": 0,
"value_type": "float",
"name": "k",
"description": "constant",
"required": false
}]
},
"__eq_k": {
"inlets": [
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"default": 0,
"value_type": "float",
"name": "k",
"description": "constant",
"required": false
}]
},
"__eq~f": {
"inlets": [
"~f>",
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
],
"args": [
],
"perf": {
"avx": 3,
"sse": 3
}
},
"__exp": {
"inlets": [
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": false
},
"outlets": [
"-->"
],
"args": [
]
},
"__floor": {
"inlets": [
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": false
},
"outlets": [
"-->"
],
"args": [
]
},
"__floor~f": {
"args": [
],
"inlets": [
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
],
"perf": {
"avx": 5,
"sse": 5
}
},
"__fma~f": {
"inlets": [
"~f>",
"~f>",
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
],
"perf": {
"avx": 5,
"sse": 5
},
"args": [
]
},
"__fms~f": {
"inlets": [
"~f>",
"~f>",
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
],
"perf": {
"avx": 5,
"sse": 5
},
"args": [
]
},
"__gt": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"name": "k",
"value_type": "float",
"description": "",
"default": 0,
"required": false
}]
},
"__gt_k": {
"inlets": [
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"name": "k",
"value_type": "float",
"description": "",
"default": 0,
"required": false
}]
},
"__gte": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"name": "k",
"value_type": "float",
"description": "",
"default": 0,
"required": false
}]
},
"__gte_k": {
"inlets": [
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"name": "k",
"value_type": "float",
"description": "",
"default": 0,
"required": false
}]
},
"__gte~f": {
"inlets": [
"~f>",
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
],
"args": [
],
"perf": {
"avx": 1,
"sse": 1
}
},
"__gt~f": {
"inlets": [
"~f>",
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
],
"args": [
],
"perf": {
"avx": 1,
"sse": 1
}
},
"__if": {
"args": [{
"name": "k",
"value_type": "float",
"description": "constant",
"default": 0,
"required": false
}],
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->",
"-->"
]
},
"__inlet": {
"inlets": [
],
"ir": {
"control": false,
"signal": false,
"init": false
},
"outlets": [
"-->"
]
},
"__intdiv": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"name": "k",
"value_type": "float",
"description": "",
"default": 1,
"required": false
}]
},
"__intdiv_k": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"name": "k",
"value_type": "float",
"description": "",
"default": 1,
"required": false
}]
},
"__line~f": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": false,
"signal": true,
"init": true
},
"outlets": [
"~f>"
],
"args": [
],
"perf": {
"avx": 5.5,
"sse": 3.5
}
},
"__log": {
"inlets": [
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": false
},
"outlets": [
"-->"
],
"args": [
]
},
"__log10": {
"inlets": [
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": false
},
"outlets": [
"-->"
],
"args": [
]
},
"__log2": {
"inlets": [
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": false
},
"outlets": [
"-->"
],
"args": [
]
},
"__log2~f": {
"inlets": [
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
],
"args": [
]
},
"__logand": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"default": 0,
"value_type": "float",
"name": "k",
"description": "constant",
"required": false
}]
},
"__logand_k": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"default": 0,
"value_type": "float",
"name": "k",
"description": "constant",
"required": false
}]
},
"__logor": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"default": 0,
"value_type": "float",
"name": "k",
"description": "constant",
"required": false
}]
},
"__logor_k": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"default": 0,
"value_type": "float",
"name": "k",
"description": "constant",
"required": false
}]
},
"__lorenz~f": {
"inlets": [
"~f>",
"~f>",
"~f>",
"~f>",
"-->"
],
"ir": {
"control": false,
"signal": true,
"init": true
},
"outlets": [
"~f>",
"~f>",
"~f>"
],
"args": [{
"name": "x",
"value_type": "float",
"description": "Initial x output value",
"default": 0,
"required": false
}, {
"name": "y",
"value_type": "float",
"description": "Initial y output value",
"default": 0,
"required": false
}, {
"name": "z",
"value_type": "float",
"description": "Initial z output value",
"default": 0,
"required": false
}]
},
"__lt": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"name": "k",
"value_type": "float",
"description": "",
"default": 0,
"required": false
}]
},
"__lt_k": {
"inlets": [
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"name": "k",
"value_type": "float",
"description": "",
"default": 0,
"required": false
}]
},
"__lte": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"name": "k",
"value_type": "float",
"description": "",
"default": 0,
"required": false
}]
},
"__lte_k": {
"inlets": [
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"name": "k",
"value_type": "float",
"description": "",
"default": 0,
"required": false
}]
},
"__lt~f": {
"inlets": [
"~f>",
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
],
"args": [
],
"perf": {
"avx": 1,
"sse": 1
}
},
"__lte~f": {
"inlets": [
"~f>",
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
],
"args": [
],
"perf": {
"avx": 1,
"sse": 1
}
},
"__max": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"name": "k",
"value_type": "float",
"description": "",
"default": 0,
"required": false
}]
},
"__max_k": {
"inlets": [
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"name": "k",
"value_type": "float",
"description": "",
"default": 0,
"required": false
}]
},
"__max~f": {
"inlets": [
"~f>",
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
],
"args": [
],
"perf": {
"avx": 3,
"sse": 3
}
},
"__max~i": {
"inlets": [
"~i>",
"~i>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~i>"
],
"args": [{
"name": "k",
"value_type": "int",
"description": "",
"default": 0,
"required": false
}]
},
"__message": {
"inlets": [
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": false
},
"outlets": [
"-->"
]
},
"__min": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"name": "k",
"value_type": "float",
"description": "",
"default": 0,
"required": false
}]
},
"__min_k": {
"inlets": [
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"name": "k",
"value_type": "float",
"description": "",
"default": 0,
"required": false
}]
},
"__min~f": {
"inlets": [
"~f>",
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
],
"perf": {
"avx": 3,
"sse": 3
},
"args": [
]
},
"__min~i": {
"inlets": [
"~i>",
"~i>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~i>"
],
"perf": {
"sse": 0.5
},
"args": [{
"name": "k",
"value_type": "int",
"description": "",
"default": 0,
"required": false
}]
},
"__mul": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"name": "k",
"value_type": "float",
"description": "",
"default": 0,
"required": false
}]
},
"__mul_k": {
"inlets": [
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"name": "k",
"value_type": "float",
"description": "",
"default": 0,
"required": false
}]
},
"__mul~f": {
"inlets": [
"~f>",
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
],
"perf": {
"avx": 5,
"sse": 5
},
"args": [
]
},
"__mul~i": {
"inlets": [
"~i>",
"~i>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~i>"
],
"perf": {
"avx": 3,
"sse": 1
},
"args": [{
"name": "k",
"value_type": "int",
"description": "",
"default": 0,
"required": false
}]
},
"__neq": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"default": 0,
"value_type": "float",
"name": "k",
"description": "constant",
"required": false
}]
},
"__neq_k": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": false
},
"outlets": [
"-->"
],
"args": [{
"default": 0,
"value_type": "float",
"name": "k",
"description": "constant",
"required": false
}]
},
"__neq~f": {
"inlets": [
"~f>",
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
],
"args": [
],
"perf": {
"avx": 3,
"sse": 3
}
},
"__or": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"default": 0,
"value_type": "float",
"name": "k",
"description": "constant",
"required": false
}]
},
"__or_k": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"default": 0,
"value_type": "int",
"name": "k",
"description": "constant",
"required": false
}]
},
"__or~f": {
"inlets": [
"~f>",
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
],
"args": [
],
"perf": {
"avx": 1,
"sse": 1
}
},
"__outlet": {
"inlets": [
"-->"
],
"ir": {
"control": false,
"signal": false,
"init": false
},
"outlets": [
]
},
"__pack": {
"inlets": [
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"name": "values",
"value_type": "floatarray",
"description": "The preset values to this pack object.",
"default": [0.0, 0.0],
"required": false
}]
},
"__phasor_k~f": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": false,
"signal": true,
"init": true
},
"outlets": [
"~f>"
],
"args": [{
"default": 440,
"value_type": "float",
"name": "frequency",
"description": "",
"required": false
}, {
"default": 0,
"value_type": "float",
"name": "phase",
"description": "",
"required": false
}],
"perf": {
"avx": 13,
"sse": 10
}
},
"__phasor~f": {
"inlets": [
"~f>",
"-->"
],
"ir": {
"control": false,
"signal": true,
"init": true
},
"outlets": [
"~f>"
],
"args": [
],
"perf": {
"avx": 38,
"sse": 26
}
},
"__pow": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
]
},
"__pow_k": {
"inlets": [
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
]
},
"__pow~f": {
"inlets": [
"~f>",
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
]
},
"__print": {
"inlets": [
"-->"
],
"ir": {
"control": false,
"signal": false,
"init": false
},
"outlets": [
]
},
"__random": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"name": "range",
"value_type": "float",
"description": "",
"default": 2,
"required": false
}, {
"name": "seed",
"value_type": "float",
"description": "",
"default": 1,
"required": false
}]
},
"__receive": {
"inlets": [
],
"ir": {
"control": true,
"signal": false,
"init": false
},
"outlets": [
"-->"
]
},
"__rpole~f": {
"inlets": [
"~f>",
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": true
},
"outlets": [
"~f>"
],
"args": [
],
"perf": {
"avx": 143,
"sse": 53
}
},
"__rsqrt~f": {
"inlets": [
"~f>"
],
"args": [
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
]
},
"__samphold~f": {
"inlets": [
"~f>",
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": true
},
"outlets": [
"~f>"
],
"args": [
],
"perf": {
"avx": 0,
"sse": 3
}
},
"__sample~f": {
"inlets": [
"~f>",
"-->"
],
"ir": {
"control": true,
"signal": true,
"init": true
},
"outlets": [
"-->"
],
"args": [
],
"perf": {
"avx": 1,
"sse": 1
}
},
"__send": {
"inlets": [
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": false
},
"outlets": [
],
"args": [{
"name": "name",
"value_type": "string",
"description": "",
"default": null,
"required": true
}, {
"name": "hash",
"value_type": "string",
"description": "The 32-bit hash of the \"name\" argument in \"0x00000000\" format.",
"default": null,
"required": true
}]
},
"__shiftleft": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"name": "k",
"value_type": "int",
"description": "",
"default": 0,
"required": false
}]
},
"__shiftleft_k": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"name": "k",
"value_type": "int",
"description": "",
"default": 0,
"required": false
}]
},
"__shiftright": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"name": "k",
"value_type": "int",
"description": "",
"default": 0,
"required": false
}]
},
"__shiftright_k": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"name": "k",
"value_type": "int",
"description": "",
"default": 0,
"required": false
}]
},
"__sin": {
"inlets": [
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": false
},
"outlets": [
"-->"
],
"args": [
]
},
"__sinh": {
"inlets": [
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": false
},
"outlets": [
"-->"
]
},
"__sin~f": {
"inlets": [
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
]
},
"__slice": {
"description": "",
"ir": {
"control": true,
"signal": false,
"init": true
},
"inlets": [
"-->",
"-->",
"-->"
],
"outlets": [
"-->",
"-->"
],
"args": [{
"name": "index",
"value_type": "float",
"description": "The start index of the slice.",
"default": 0,
"required": false
}, {
"name": "length",
"value_type": "float",
"description": "The number of elements to include in the slice.",
"default": 1,
"required": false
}],
"alias": [
],
"tags": [
]
},
"__sqrt": {
"inlets": [
"-->"
],
"args": [
],
"ir": {
"control": true,
"signal": false,
"init": false
},
"outlets": [
"-->"
]
},
"__sqrt~f": {
"inlets": [
"~f>"
],
"args": [
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
]
},
"__sub": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"name": "k",
"value_type": "float",
"description": "",
"default": 0,
"required": false
}]
},
"__sub_k": {
"inlets": [
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"name": "k",
"value_type": "float",
"description": "",
"default": 0,
"required": false
}]
},
"__sub~f": {
"ir": {
"control": false,
"signal": true,
"init": false
},
"inlets": [
"~f>",
"~f>"
],
"outlets": [
"~f>"
],
"perf": {
"avx": 3,
"sse": 3
}
},
"__sub~i": {
"ir": {
"control": false,
"signal": true,
"init": false
},
"inlets": [
"~i>",
"~i>"
],
"outlets": [
"~i>"
],
"perf": {
"avx": 3,
"sse": 1
},
"args": [{
"name": "k",
"value_type": "float",
"description": "",
"default": 0,
"required": false
}]
},
"__switchcase": {
"ir": {
"control": true,
"signal": false,
"init": false
},
"inlets": [
"-->"
],
"outlets": [
],
"args": [{
"default": [
],
"value_type": "mixedarray",
"name": "cases",
"description": "",
"required": false
}]
},
"__system": {
"inlets": [
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": false
},
"outlets": [
"-->"
]
},
"__tabhead": {
"inlets": [
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"default": null,
"value_type": "string",
"name": "table",
"description": "The name of the table to reference.",
"required": true
}]
},
"__tabhead~f": {
"inlets": [
"-->"
],
"ir": {
"control": false,
"signal": true,
"init": true
},
"outlets": [
"~f>"
],
"args": [{
"default": null,
"value_type": "string",
"name": "table",
"description": "The name of the table to reference.",
"required": true
}],
"perf": {
"avx": 0.5,
"sse": 0.5
}
},
"__table": {
"inlets": [
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"name": "name",
"value_type": "string",
"description": "",
"default": null,
"required": true
}, {
"name": "size",
"value_type": "int",
"description": "The number of samples in this table.",
"default": 0,
"required": false
}, {
"name": "values",
"value_type": "floatarray",
"description": "The preset values in this table. The length of this list overrides the \"size\" argument.",
"default": [
],
"required": false
}]
},
"__tabread": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"name": "table",
"value_type": "string",
"description": "",
"default": null,
"required": true
}]
},
"__tabreadu~f": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": true,
"init": true
},
"outlets": [
"~f>",
"-->"
],
"args": [{
"default": null,
"value_type": "string",
"name": "table",
"description": "The name of the table to reference.",
"required": true
}],
"perf": {
"avx": 2,
"sse": 2
}
},
"__tabread~f": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": true,
"init": true
},
"outlets": [
"~f>",
"-->"
],
"args": [{
"default": null,
"value_type": "string",
"name": "table",
"description": "The name of the table to reference.",
"required": true
}],
"perf": {
"avx": 1,
"sse": 1
}
},
"__tabread_stoppable~f": {
"inlets": [
"-->",
"-->",
"-->"
],
"ir": {
"control": true,
"signal": true,
"init": true
},
"outlets": [
"~f>",
"-->",
"-->"
],
"args": [{
"default": null,
"value_type": "string",
"name": "table",
"description": "The name of the table to reference.",
"required": true
}],
"perf": {
"avx": 1,
"sse": 1
}
},
"__tabread~if": {
"inlets": [
"~i>",
"-->"
],
"ir": {
"control": false,
"signal": true,
"init": true
},
"outlets": [
"~f>"
],
"args": [{
"default": null,
"value_type": "string",
"name": "table",
"description": "The name of the table to reference.",
"required": true
}],
"perf": {
"avx": 4,
"sse": 2
}
},
"__tabwrite": {
"inlets": [
"-->",
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"name": "table",
"value_type": "string",
"description": "",
"default": null,
"required": true
}]
},
"__tabwrite_stoppable~f": {
"inlets": [
"~f>",
"-->",
"-->"
],
"ir": {
"control": false,
"signal": true,
"init": true
},
"outlets": [
],
"args": [{
"default": null,
"value_type": "string",
"name": "table",
"description": "The name of the table to reference.",
"required": true
}],
"perf": {
"avx": 2,
"sse": 2
}
},
"__tabwrite~f": {
"inlets": [
"~f>",
"-->",
"-->"
],
"ir": {
"control": false,
"signal": true,
"init": true
},
"outlets": [
],
"args": [{
"default": null,
"value_type": "string",
"name": "table",
"description": "The name of the table to reference.",
"required": true
}],
"perf": {
"avx": 1,
"sse": 1
}
},
"__tan": {
"inlets": [
"-->"
],
"args": [
],
"ir": {
"control": true,
"signal": false,
"init": false
},
"outlets": [
"-->"
]
},
"__tanh": {
"inlets": [
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": false
},
"outlets": [
"-->"
],
"args": [
]
},
"__unimod": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [
]
},
"__unimod_k": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": false
},
"outlets": [
"-->"
],
"args": [
]
},
"__var": {
"inlets": [
"-->",
"-->"
],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"name": "k",
"value_type": "auto",
"description": "",
"default": 0,
"required": false
}]
},
"__var_k~f": {
"ir": {
"control": false,
"signal": true,
"init": false
},
"inlets": [
],
"args": [{
"default": 0,
"value_type": "float",
"name": "k",
"description": "The value of this variable.",
"required": false
}, {
"default": 0,
"value_type": "float",
"name": "step",
"description": "The value step increment.",
"required": false
}, {
"default": false,
"value_type": "boolean",
"name": "reverse",
"description": "True if the value and step increments should be reversed.",
"required": false
}],
"outlets": [
"~f>"
],
"keywords": [
],
"perf": {
"avx": 0.5,
"sse": 0.5
}
},
"__var_k~i": {
"ir": {
"control": false,
"signal": true,
"init": false
},
"inlets": [
],
"args": [{
"default": 0,
"value_type": "int",
"name": "k",
"description": "The value of this variable.",
"required": false
}, {
"default": 0,
"value_type": "int",
"name": "step",
"description": "The value step increment.",
"required": false
}, {
"default": false,
"value_type": "boolean",
"name": "reverse",
"description": "True if the value and step increments should be reversed.",
"required": false
}],
"outlets": [
"~i>"
],
"keywords": [
],
"perf": {
"avx": 0.5,
"sse": 0.5
}
},
"__varread~f": {
"ir": {
"control": false,
"signal": true,
"init": false
},
"inlets": [
],
"args": [{
"default": null,
"value_type": "string",
"name": "var_id",
"description": "The id the of the var object to read from.",
"required": true
}],
"outlets": [
"~f>"
],
"perf": {
"avx": 1,
"sse": 1
}
},
"__varread~i": {
"ir": {
"control": false,
"signal": true,
"init": false
},
"inlets": [
],
"args": [{
"default": null,
"value_type": "string",
"name": "var_id",
"description": "The id the of the var object to read from.",
"required": true
}],
"outlets": [
"~i>"
],
"perf": {
"avx": 1,
"sse": 1
}
},
"__varwrite~f": {
"ir": {
"control": false,
"signal": true,
"init": false
},
"inlets": [
"~f>"
],
"args": [{
"default": null,
"value_type": "string",
"name": "var_id",
"description": "The id the of the var object to write to.",
"required": true
}],
"outlets": [
],
"perf": {
"avx": 1,
"sse": 1
}
},
"__varwrite~i": {
"ir": {
"control": false,
"signal": true,
"init": false
},
"inlets": [
"~i>"
],
"args": [{
"default": null,
"value_type": "string",
"name": "var_id",
"description": "The id the of the var object to write to.",
"required": true
}],
"outlets": [
],
"perf": {
"avx": 1,
"sse": 1
}
},
"__var~f": {
"ir": {
"control": false,
"signal": false,
"init": true
},
"inlets": [
"-->"
],
"args": [{
"default": 0,
"value_type": "float",
"name": "k",
"description": "The value of this variable.",
"required": false
}, {
"default": 0,
"value_type": "float",
"name": "step",
"description": "The value step increment.",
"required": false
}, {
"default": false,
"value_type": "boolean",
"name": "reverse",
"description": "True if the value and step increments should be reversed.",
"required": false
}],
"outlets": [
"~f>"
],
"keywords": [
"static"
],
"perf": {
"avx": 0,
"sse": 0,
"neon": 0
}
},
"__var~i": {
"ir": {
"control": false,
"signal": false,
"init": true
},
"inlets": [
"-->"
],
"args": [{
"default": 0,
"value_type": "int",
"name": "k",
"description": "The value of this variable.",
"required": false
}, {
"default": 0,
"value_type": "int",
"name": "step",
"description": "The value step increment.",
"required": false
}, {
"default": false,
"value_type": "boolean",
"name": "reverse",
"description": "True if the value and step increments should be reversed.",
"required": false
}],
"outlets": [
"~i>"
],
"perf": {
"avx": 0,
"sse": 0,
"neon": 0
}
},
"asinh~": {
"inlets": [
"signal"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"signal"
],
"args": [
]
},
"asin~": {
"inlets": [
"signal"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"signal"
]
},
"atanh~": {
"inlets": [
"signal"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"signal"
]
},
"atan~": {
"inlets": [
"signal"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"signal"
]
},
"__exp~f": {
"inlets": [
"~f>"
],
"args": [],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
],
"perf": {
"avx": 0,
"sse": 0,
"neon": 0
}
},
"sinh~": {
"inlets": [
"signal"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"signal"
],
"args": [
]
},
"tanh~": {
"inlets": [
"signal"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"signal"
]
},
"tan~": {
"inlets": [
"signal"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"signal"
]
}
}
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1735754044.261823
hvcc-0.13.3/hvcc/core/json/heavy.lang.json 0000644 0000000 0000000 00000123611 14735300474 015235 0 ustar 00 {
"!=": {
"description": "Output = (bool) (Input != k)",
"inlets": [
{
"name": "Input",
"connectionType": "-~>",
"description": "Set input"
},
{
"name": "k",
"connectionType": "-~>",
"description": "Set k"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
{
"name": "k",
"value_type": "float",
"description": "constant",
"default": 0,
"required": false
}
],
"alias": [
"ne"
],
"tags": [
"math",
"logical"
]
},
"%": {
"description": "Output = Input % k",
"inlets": [
{
"name": "Input",
"connectionType": "-~>",
"description": "Set input"
},
{
"name": "k",
"connectionType": "-~>",
"description": "Set k"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
{
"name": "k",
"value_type": "float",
"description": "constant",
"default": 1,
"required": false
}
],
"alias": [
"mod"
],
"tags": [
"math",
"logical"
]
},
"&": {
"description": "Binary AND operation.",
"inlets": [
{
"name": "Input",
"connectionType": "-~>",
"description": "Set input"
},
{
"name": "k",
"connectionType": "-~>",
"description": "Set k"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
{
"name": "k",
"value_type": "int",
"description": "constant",
"default": 0,
"required": false
}
],
"alias": [
"binaryand"
],
"tags": [
"math",
"bitwise"
]
},
"&&": {
"description": "Logical AND operation.",
"inlets": [
{
"name": "Input",
"connectionType": "-~>",
"description": "Set input"
},
{
"name": "k",
"connectionType": "-~>",
"description": "Set k"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
{
"name": "k",
"value_type": "float",
"description": "constant",
"default": 0,
"required": false
}
],
"alias": [
"logicaland"
],
"tags": [
"math",
"logical"
]
},
"*": {
"description": "Output = Input * k",
"inlets": [
{
"name": "Input",
"connectionType": "-~>",
"description": "Set input"
},
{
"name": "k",
"connectionType": "-~>",
"description": "Set k"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
{
"name": "k",
"value_type": "float",
"description": "constant",
"default": 0,
"required": false
}
],
"alias": [
"divide"
],
"tags": [
"math",
"arithmetic"
]
},
"+": {
"description": "Output = Input + k",
"inlets": [
{
"name": "Input",
"connectionType": "-~>",
"description": "Set input"
},
{
"name": "k",
"connectionType": "-~>",
"description": "Set k"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
{
"name": "k",
"value_type": "float",
"description": "constant",
"default": 0,
"required": false
}
],
"alias": [
"sub"
],
"tags": [
"math",
"arithmetic"
]
},
"-": {
"description": "Output = Input - k",
"inlets": [
{
"name": "Input",
"connectionType": "-~>",
"description": "Set input"
},
{
"name": "k",
"connectionType": "-~>",
"description": "Set k"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
{
"name": "k",
"value_type": "float",
"description": "constant",
"default": 0,
"required": false
}
],
"alias": [
"mult"
],
"tags": [
"math",
"arithmetic"
]
},
"/": {
"description": "Output = Input / k",
"inlets": [
{
"name": "Input",
"connectionType": "-~>",
"description": "Set input"
},
{
"name": "k",
"connectionType": "-~>",
"description": "Set k"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
{
"name": "k",
"value_type": "float",
"description": "constant",
"default": 1,
"required": false
}
],
"alias": [
"add"
],
"tags": [
"math",
"arithmetic"
]
},
"<": {
"description": "Output = (bool) (Input < k)",
"inlets": [
{
"name": "Input",
"connectionType": "-~>",
"description": "Set input"
},
{
"name": "k",
"connectionType": "-~>",
"description": "Set k"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
{
"name": "k",
"value_type": "float",
"description": "constant",
"default": 0,
"required": false
}
],
"alias": [
"lt"
],
"tags": [
"math",
"logical"
]
},
"<<": {
"description": "Output = (Input << k)",
"inlets": [
{
"name": "Input",
"connectionType": "-~>",
"description": "Set input"
},
{
"name": "k",
"connectionType": "-~>",
"description": "Set k"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
{
"name": "k",
"value_type": "int",
"description": "constant",
"default": 0,
"required": false
}
],
"alias": [
"leftshift"
],
"tags": [
"math",
"bitwise"
]
},
"<=": {
"description": "Output = (bool) (Input <= k)",
"inlets": [
{
"name": "Input",
"connectionType": "-~>",
"description": "Set input"
},
{
"name": "k",
"connectionType": "-~>",
"description": "Set k"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
{
"name": "k",
"value_type": "float",
"description": "constant",
"default": 0,
"required": false
}
],
"alias": [
"lte"
],
"tags": [
"math",
"logical"
]
},
"==": {
"description": "Output = (bool) (Input == k)",
"inlets": [
{
"name": "Input",
"connectionType": "-~>",
"description": "Set input"
},
{
"name": "k",
"connectionType": "-~>",
"description": "Set k"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
{
"name": "k",
"value_type": "float",
"description": "constant",
"default": 0,
"required": false
}
],
"alias": [
"eq"
],
"tags": [
"math",
"logical"
]
},
">": {
"description": "Output = (bool) (Input > k)",
"inlets": [
{
"name": "Input",
"connectionType": "-~>",
"description": "Set input"
},
{
"name": "k",
"connectionType": "-~>",
"description": "Set k"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
{
"name": "k",
"value_type": "float",
"description": "constant",
"default": 0,
"required": false
}
],
"alias": [
"gt"
],
"tags": [
"math",
"logical"
]
},
">=": {
"description": "Output = (bool) (Input >= k)",
"inlets": [
{
"name": "Input",
"connectionType": "-~>",
"description": "Set input"
},
{
"name": "k",
"connectionType": "-~>",
"description": "Set k"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
{
"name": "k",
"value_type": "float",
"description": "constant",
"default": 0,
"required": false
}
],
"alias": [
"gte"
],
"tags": [
"math",
"logical"
]
},
">>": {
"description": "Output = (Input >> k)",
"inlets": [
{
"name": "Input",
"connectionType": "-~>",
"description": "Set input"
},
{
"name": "k",
"connectionType": "-~>",
"description": "Set k"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
{
"name": "k",
"value_type": "int",
"description": "constant",
"default": 0,
"required": false
}
],
"alias": [
"rightshift"
],
"tags": [
"math",
"bitwise"
]
},
"^": {
"description": "Output = (Input ^ k)",
"inlets": [
{
"name": "Input",
"connectionType": "-~>",
"description": "Set input"
},
{
"name": "k",
"connectionType": "-~>",
"description": "Set k"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
{
"name": "k",
"value_type": "float",
"description": "constant",
"default": 0,
"required": false
}
],
"alias": [
"xor"
],
"tags": [
"math",
"bitwise"
]
},
"abs": {
"description": "Output = abs(Input)",
"inlets": [
{
"name": "inlet",
"connectionType": "-~>",
"description": "Input"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
],
"alias": [
],
"tags": [
"math"
]
},
"acos": {
"description": "Output = acos(Input)",
"inlets": [
{
"name": "inlet",
"connectionType": "-~>",
"description": "Input"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
],
"alias": [
],
"tags": [
"math",
"trigonometric"
]
},
"acosh": {
"description": "Output = acosh(Input)",
"inlets": [
{
"name": "inlet",
"connectionType": "-~>",
"description": "Input"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
],
"alias": [
],
"tags": [
"math",
"trigonometric"
]
},
"adc": {
"description": "",
"inlets": [
],
"outlets": [
{
"name": "",
"connectionType": "~f>",
"description": ""
}
],
"args": [
{
"name": "channels",
"value_type": "intarray",
"description": "Channels from input.",
"default": [
1,
2
],
"required": false
}
],
"alias": [
],
"tags": [
]
},
"asin": {
"description": "Output = asin(Input)",
"inlets": [
{
"name": "inlet",
"connectionType": "-~>",
"description": "Input"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
],
"alias": [
],
"tags": [
"math",
"trigonometric"
]
},
"asinh": {
"description": "Output = asinh(Input)",
"inlets": [
{
"name": "inlet",
"connectionType": "-~>",
"description": "Input"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
],
"alias": [
],
"tags": [
"math",
"trigonometric"
]
},
"atan": {
"description": "Output = atan(Input)",
"inlets": [
{
"name": "inlet",
"connectionType": "-~>",
"description": "Input"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
],
"alias": [
],
"tags": [
"math",
"trigonometric"
]
},
"atan2": {
"description": "Output = atan2(x, y)",
"inlets": [
{
"name": "x",
"connectionType": "-~>",
"description": "Set x"
},
{
"name": "y",
"connectionType": "-~>",
"description": "Set y"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
{
"name": "y",
"value_type": "float",
"description": "constant",
"default": 0,
"required": false
}
],
"alias": [
],
"tags": [
"math",
"trigonometric"
]
},
"atanh": {
"description": "Output = atanh(Input)",
"inlets": [
{
"name": "inlet",
"connectionType": "-~>",
"description": "Input"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
],
"alias": [
],
"tags": [
"math",
"trigonometric"
]
},
"biquad": {
"description": "out = in[0]*x0 + in[-1]*x1 + in[-2]*x2 - out[-1]*y1 - out[-2]*y2",
"inlets": [
{
"name": "input",
"connectionType": "~f>",
"description": "Filter input."
},
{
"name": "x0",
"connectionType": "-~>",
"description": ""
},
{
"name": "x1",
"connectionType": "-~>",
"description": ""
},
{
"name": "x2",
"connectionType": "-~>",
"description": ""
},
{
"name": "y1",
"connectionType": "-~>",
"description": ""
},
{
"name": "y2",
"connectionType": "-~>",
"description": ""
}
],
"outlets": [
{
"name": "output",
"connectionType": "~f>",
"description": "Filter output."
}
],
"args": [
{
"name": "ff0",
"value_type": "float",
"description": "",
"default": 1,
"required": false
},
{
"name": "ff1",
"value_type": "float",
"description": "",
"default": 0,
"required": false
},
{
"name": "ff2",
"value_type": "float",
"description": "",
"default": 0,
"required": false
},
{
"name": "fb1",
"value_type": "float",
"description": "",
"default": 0,
"required": false
},
{
"name": "fb2",
"value_type": "float",
"description": "",
"default": 0,
"required": false
}
],
"alias": [
],
"tags": [
"filters"
]
},
"ceil": {
"description": "",
"inlets": [
{
"name": "k",
"connectionType": "-~>",
"description": ""
}
],
"outlets": [
{
"name": "",
"connectionType": "-~>",
"description": "ceil(k)"
}
],
"args": [
],
"alias": [
],
"tags": [
]
},
"comment": {
"description": "A text comment.",
"inlets": [
],
"outlets": [
],
"args": [
{
"name": "text",
"value_type": "string",
"description": "The comment string.",
"default": null,
"required": true
},
{
"name": "mime",
"value_type": "string",
"description": "The MIME type of this comment.",
"default": "text/plain",
"required": false
}
],
"alias": [
],
"tags": [
]
},
"cos": {
"description": "Output = cos(Input)",
"inlets": [
{
"name": "inlet",
"connectionType": "-~>",
"description": "Input"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
],
"alias": [
],
"tags": [
"math",
"trigonometric"
]
},
"dac": {
"description": "",
"inlets": [
{
"name": "",
"connectionType": "~f>",
"description": ""
}
],
"outlets": [
],
"args": [
{
"name": "channels",
"value_type": "intarray",
"description": "Channels to output.",
"default": [
1,
2
],
"required": false
}
],
"alias": [
],
"tags": [
]
},
"delay": {
"description": "",
"inlets": [
{
"name": "message",
"connectionType": "-->",
"description": "The message to delay."
},
{
"name": "delay",
"connectionType": "-->",
"description": "The delay is milliseconds."
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-->",
"description": "The delayed message."
}
],
"args": [
{
"name": "delay",
"value_type": "float",
"description": "The message delay in milliseconds.",
"default": 0,
"required": false
}
],
"alias": [
],
"tags": [
]
},
"env~": {
"description": "",
"inlets": [
{
"name": "",
"connectionType": "~f>",
"description": ""
}
],
"outlets": [
{
"name": "",
"connectionType": "-->",
"description": ""
}
],
"args": [
{
"name": "windowSize",
"value_type": "float",
"description": "",
"default": 1024,
"required": false
},
{
"name": "period",
"value_type": "float",
"description": "",
"default": 512,
"required": false
}
],
"alias": [
"envelope"
],
"tags": [
]
},
"exp": {
"description": "Output = exp(Input)",
"inlets": [
{
"name": "inlet",
"connectionType": "-~>",
"description": "Input"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
],
"alias": [
],
"tags": [
"math"
]
},
"floor": {
"description": "",
"inlets": [
{
"name": "k",
"connectionType": "-~>",
"description": ""
}
],
"outlets": [
{
"name": "",
"connectionType": "-~>",
"description": "floor(k)"
}
],
"args": [
],
"alias": [
],
"tags": [
]
},
"graph": {
"description": "",
"inlets": [
],
"outlets": [
],
"args": [
],
"alias": [
],
"tags": [
]
},
"if": {
"description": "Input to the first inlet is routed to the first outlet if k == 0, else it's routed to the second outlet.",
"inlets": [
{
"name": "Input",
"connectionType": "-~>",
"description": "Set input"
},
{
"name": "k",
"connectionType": "-~>",
"description": "Set k"
}
],
"outlets": [
{
"name": "k is False",
"connectionType": "-~>",
"description": "Output."
},
{
"name": "k is True",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
{
"name": "k",
"value_type": "float",
"description": "constant",
"default": 0,
"required": false
}
],
"alias": [
],
"tags": [
"sequencing"
]
},
"inlet": {
"description": "",
"inlets": [
],
"outlets": [
{
"name": "",
"connectionType": "-~>",
"description": ""
}
],
"args": [
{
"name": "name",
"value_type": "string",
"description": "inlet name",
"default": "",
"required": false
},
{
"name": "index",
"value_type": "int",
"description": "Determines the inlet position within an abstraction/sub-graph",
"default": 0,
"required": true
},
{
"name": "type",
"value_type": "string",
"description": "Specifies the inlet connection type",
"default": null,
"required": true
}
],
"alias": [
],
"tags": [
]
},
"line": {
"description": "",
"inlets": [
{
"name": "",
"connectionType": "-->",
"description": ""
},
{
"name": "",
"connectionType": "-->",
"description": ""
}
],
"outlets": [
{
"name": "",
"connectionType": "-~>",
"description": ""
}
],
"args": [
],
"alias": [
],
"tags": [
]
},
"loadbang": {
"description": "Triggers an event once the patch is initialised",
"inlets": [
],
"outlets": [
{
"name": "outlet",
"connectionType": "-->",
"description": "Bang output."
}
],
"args": [
{
"name": "priority",
"value_type": "int",
"description": "Relative priority for when this loadbang should fire. Higher executes sooner.",
"default": 0,
"required": false
}
],
"alias": [
],
"tags": [
"sequencing"
]
},
"log": {
"description": "Output = round(Input)",
"inlets": [
{
"name": "inlet",
"connectionType": "-~>",
"description": "Input"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
],
"alias": [
],
"tags": [
"math"
]
},
"log10": {
"description": "Output = log10(Input)",
"inlets": [
{
"name": "inlet",
"connectionType": "-~>",
"description": "Input"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
],
"alias": [
],
"tags": [
"math"
]
},
"log2": {
"description": "Output = log2(Input)",
"inlets": [
{
"name": "inlet",
"connectionType": "-~>",
"description": "Input"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
],
"alias": [
],
"tags": [
"math"
]
},
"max": {
"description": "Finds the maximum of the two inputs.",
"inlets": [
{
"name": "",
"connectionType": "-~>",
"description": ""
},
{
"name": "",
"connectionType": "-~>",
"description": ""
}
],
"outlets": [
{
"name": "",
"connectionType": "-~>",
"description": ""
}
],
"args": [
{
"name": "k",
"value_type": "float",
"description": "constant",
"default": 0,
"required": false
}
],
"alias": [
],
"tags": [
"math"
]
},
"message": {
"description": "",
"inlets": [
{
"name": "",
"connectionType": "-->",
"description": ""
}
],
"outlets": [
{
"name": "",
"connectionType": "-->",
"description": ""
}
],
"args": [
{
"name": "local",
"value_type": null,
"description": "A list of message lists to send.",
"default": null,
"required": true
},
{
"name": "remote",
"value_type": null,
"description": "A list of message lists to send.",
"default": [
],
"required": false
}
],
"alias": [
],
"tags": [
]
},
"min": {
"description": "Finds the minimum of the two inputs.",
"inlets": [
{
"name": "",
"connectionType": "-~>",
"description": ""
},
{
"name": "",
"connectionType": "-~>",
"description": ""
}
],
"outlets": [
{
"name": "",
"connectionType": "-~>",
"description": ""
}
],
"args": [
{
"name": "k",
"value_type": "float",
"description": "constant",
"default": 0,
"required": false
}
],
"alias": [
],
"tags": [
"math"
]
},
"mod": {
"description": "Output = Input % k, (always positive)",
"inlets": [
{
"name": "Input",
"connectionType": "-~>",
"description": "Set input"
},
{
"name": "k",
"connectionType": "-~>",
"description": "Set k"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
{
"name": "k",
"value_type": "float",
"description": "constant",
"default": 1,
"required": false
}
],
"alias": [
],
"tags": [
"math",
"arithmetic"
]
},
"noise": {
"description": "White noise signal generator.",
"inlets": [
{
"name": "seed",
"connectionType": "-->",
"description": "Currently unused."
}
],
"outlets": [
{
"name": "Output",
"connectionType": "~f>",
"description": "White noise."
}
],
"args": [
{
"name": "noise",
"value_type": "string",
"description": "The type of noise to produce. Currently only `uniform` is supported.",
"default": "uniform",
"required": false
},
{
"name": "seed",
"value_type": "float",
"description": "The seed for the random number generator (RNG).",
"default": 0,
"required": false
}
],
"alias": [
],
"tags": [
"~f>",
"generators"
]
},
"outlet": {
"description": "",
"inlets": [
{
"name": "",
"connectionType": "-~>",
"description": ""
}
],
"outlets": [
],
"args": [
{
"name": "name",
"value_type": "string",
"description": "outlet name",
"default": "",
"required": false
},
{
"name": "index",
"value_type": "int",
"description": "Determines the outlet position within an abstraction/sub-graph",
"default": 0,
"required": true
},
{
"name": "type",
"value_type": "string",
"description": "specifies the outlet connection type",
"default": null,
"required": true
}
],
"alias": [
],
"tags": [
]
},
"phasor": {
"description": "",
"inlets": [
{
"name": "frequency",
"connectionType": "-~>",
"description": ""
},
{
"name": "phase",
"connectionType": "-->",
"description": ""
}
],
"outlets": [
{
"name": "~f>",
"connectionType": "~f>",
"description": ""
}
],
"args": [
{
"name": "frequency",
"value_type": "float",
"description": "",
"default": 0,
"required": false
},
{
"name": "phase",
"value_type": "float",
"description": "",
"default": 0,
"required": false
}
],
"alias": [
],
"tags": [
]
},
"pow": {
"description": "Output = pow(Input, k)",
"inlets": [
{
"name": "inlet",
"connectionType": "-~>",
"description": "Input"
},
{
"name": "constant",
"connectionType": "-~>",
"description": "set k"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
{
"name": "k",
"value_type": "float",
"description": "constant",
"default": 0,
"required": false
}
],
"alias": [
],
"tags": [
"math"
]
},
"print": {
"description": "",
"inlets": [
{
"name": "",
"connectionType": "-->",
"description": ""
}
],
"outlets": [
],
"args": [
{
"name": "label",
"value_type": "string",
"description": "",
"default": "print",
"required": false
}
],
"alias": [
],
"tags": [
"debugging"
]
},
"random": {
"description": "Random number generator",
"inlets": [
{
"name": "trigger",
"connectionType": "-->",
"description": "Outputs random number when triggered."
},
{
"name": "range",
"connectionType": "-->",
"description": "Generated number will be in the range [0.0, 1.0)."
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-->",
"description": "Output."
}
],
"args": [
{
"name": "seed",
"value_type": "int",
"description": "seed",
"default": -1,
"required": false
}
],
"alias": [
],
"tags": [
"math"
]
},
"receive": {
"description": "",
"inlets": [
],
"outlets": [
{
"name": "",
"connectionType": "-->",
"description": ""
}
],
"args": [
{
"name": "name",
"value_type": "string",
"description": "The name of this receiver.",
"default": null,
"required": true
},
{
"name": "priority",
"value_type": "int",
"description": "Higher priority receivers will be executed first.",
"default": 0,
"required": false
},
{
"name": "extern",
"value_type": "string",
"description": "If the receiver should be exposed to external (user) interfaces, 'param' or 'event'.",
"default": null,
"required": false
},
{
"name": "attributes",
"value_type": "dict",
"description": "Variable argument information depending on the extern type.",
"default": {},
"required": false
}
],
"alias": [
"r"
],
"tags": [
]
},
"send": {
"description": "",
"inlets": [
{
"name": "",
"connectionType": "-->",
"description": ""
}
],
"outlets": [
],
"args": [
{
"name": "name",
"value_type": "string",
"description": "",
"default": null,
"required": true
}
],
"alias": [
"s"
],
"tags": [
]
},
"sequence": {
"description": "",
"inlets": [
{
"name": "",
"connectionType": "-->",
"description": ""
}
],
"outlets": [
{
"name": "value",
"connectionType": "-->",
"description": ""
}
],
"args": [
{
"name": "casts",
"value_type": "stringarray",
"description": "Sequence and cast types.",
"default": null,
"required": true
}
],
"alias": [
"seq"
],
"tags": [
"sequencing"
]
},
"sin": {
"description": "Output = sin(Input)",
"inlets": [
{
"name": "inlet",
"connectionType": "-~>",
"description": "Input"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
],
"alias": [
],
"tags": [
"math",
"trigonometric"
]
},
"sinh": {
"description": "Output = sinh(Input)",
"inlets": [
{
"name": "inlet",
"connectionType": "-~>",
"description": "Input"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
],
"alias": [
],
"tags": [
"math",
"trigonometric"
]
},
"slice": {
"description": "",
"inlets": [
{
"name": "",
"connectionType": "-->",
"description": ""
},
{
"name": "",
"connectionType": "-->",
"description": ""
},
{
"name": "",
"connectionType": "-->",
"description": ""
}
],
"outlets": [
{
"name": "",
"connectionType": "-->",
"description": "The sliced output."
},
{
"name": "",
"connectionType": "-->",
"description": "If no slice is possible, a bang is emitted."
}
],
"args": [
{
"name": "index",
"value_type": "int",
"description": "The start index of the slice.",
"default": 0,
"required": false
},
{
"name": "length",
"value_type": "int",
"description": "The number of elements to include in the slice.",
"default": 1,
"required": false
}
],
"alias": [
],
"tags": [
]
},
"sqrt": {
"description": "Output = sqrt(Input)",
"inlets": [
{
"name": "inlet",
"connectionType": "-~>",
"description": "Input"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
],
"alias": [
],
"tags": [
"math"
]
},
"system": {
"description": "Returns system properties such as samplerate or block size.",
"inlets": [
{
"name": "parameter",
"connectionType": "-->",
"description": "The requested system parameter."
}
],
"outlets": [
{
"name": "value",
"connectionType": "-->",
"description": "The system parameter value."
}
],
"args": [
],
"alias": [
],
"tags": [
]
},
"table": {
"description": "",
"inlets": [
{
"name": "",
"connectionType": "-->",
"description": ""
}
],
"outlets": [
{
"name": "",
"connectionType": "-->",
"description": ""
}
],
"args": [
{
"name": "name",
"value_type": "string",
"description": "",
"default": null,
"required": true
},
{
"name": "size",
"value_type": "int",
"description": "The number of samples in this table.",
"default": 256,
"required": false
},
{
"name": "values",
"value_type": "floatarray",
"description": "The preset values in this table. The length of this list overrides the \"size\" argument.",
"default": [],
"required": false
},
{
"name": "extern",
"value_type": "bool",
"description": "Determines if the table should be publicly exposed in a framework.",
"default": false,
"required": false
}
],
"alias": [
],
"tags": [
]
},
"tabread": {
"description": "",
"inlets": [
{
"name": "",
"connectionType": "-~>",
"description": ""
}
],
"outlets": [
{
"name": "",
"connectionType": "-~>",
"description": "output = table[input]"
},
{
"name": "",
"connectionType": "-~>",
"description": "output = table[input+1]"
}
],
"args": [
{
"name": "table",
"value_type": "string",
"description": "The name of the table to read from.",
"default": null,
"required": true
},
{
"name": "delay",
"value_type": "float",
"description": "",
"default": 0,
"required": false
}
],
"alias": [
],
"tags": [
]
},
"tabwrite": {
"description": "",
"inlets": [
{
"name": "",
"connectionType": "-->",
"description": ""
}
],
"outlets": [
{
"name": "",
"connectionType": "~f>",
"description": ""
}
],
"args": [
{
"name": "name",
"value_type": "string",
"description": "The name of the table to read from.",
"default": null,
"required": true
},
{
"name": "loop",
"value_type": "bool",
"description": "If the object should continue writing to the table when the end is reached.",
"default": true,
"required": false
}
],
"alias": [
],
"tags": [
]
},
"tan": {
"description": "Output = tan(Input)",
"inlets": [
{
"name": "inlet",
"connectionType": "-~>",
"description": "Input"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
],
"alias": [
],
"tags": [
"math",
"trigonometric"
]
},
"tanh": {
"description": "Output = tanh(Input)",
"inlets": [
{
"name": "inlet",
"connectionType": "-~>",
"description": "Input"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
],
"alias": [
],
"tags": [
"math",
"trigonometric"
]
},
"var": {
"description": "",
"inlets": [
{
"name": "",
"connectionType": "-~>",
"description": ""
}
],
"outlets": [
{
"name": "",
"connectionType": "-~>",
"description": ""
}
],
"args": [
{
"default": 0,
"value_type": "float",
"name": "k",
"description": "The value of this variable.",
"required": false
},
{
"default": 0,
"value_type": "float",
"name": "step",
"description": "The value step increment.",
"required": false
},
{
"default": false,
"value_type": "bool",
"name": "reverse",
"description": "True if the value and step increments should be reversed.",
"required": false
},
{
"default": null,
"value_type": "string",
"name": "name",
"description": "The variable may be named, which is especially useful when used in conjunction with the __varset~f object.",
"required": false
}
],
"alias": [
],
"tags": [
]
},
"vario": {
"description": "",
"inlets": [
{
"name": "",
"connectionType": "-~>",
"description": ""
}
],
"outlets": [
{
"name": "",
"connectionType": "-~>",
"description": ""
}
],
"args": [
{
"default": null,
"value_type": "string",
"name": "name",
"description": "The named variable which will be manipulated.",
"required": true
}
],
"alias": [
],
"tags": [
]
},
"|": {
"description": "Binary OR operation.",
"inlets": [
{
"name": "Input",
"connectionType": "-~>",
"description": "Set input"
},
{
"name": "k",
"connectionType": "-~>",
"description": "Set k"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
{
"name": "k",
"value_type": "int",
"description": "constant",
"default": 0,
"required": false
}
],
"alias": [
"binaryor"
],
"tags": [
"math",
"bitwise"
]
},
"||": {
"description": "Logical OR operation.",
"inlets": [
{
"name": "Input",
"connectionType": "-~>",
"description": "Set input"
},
{
"name": "k",
"connectionType": "-~>",
"description": "Set k"
}
],
"outlets": [
{
"name": "outlet",
"connectionType": "-~>",
"description": "Output."
}
],
"args": [
{
"name": "k",
"value_type": "float",
"description": "constant",
"default": 0,
"required": false
}
],
"alias": [
"logicalor"
],
"tags": [
"math",
"logical"
]
}
}
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1685549294.7502067
hvcc-0.13.3/hvcc/generators/__init__.py 0000644 0000000 0000000 00000000000 14435670357 014660 0 ustar 00 ././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1685549294.7502067
hvcc-0.13.3/hvcc/generators/c2daisy/__init__.py 0000644 0000000 0000000 00000000000 14435670357 016216 0 ustar 00 ././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1735754044.261823
hvcc-0.13.3/hvcc/generators/c2daisy/c2daisy.py 0000644 0000000 0000000 00000014104 14735300474 016020 0 ustar 00 import jinja2
import os
import shutil
import time
import json2daisy # type: ignore
from typing import Any, Dict, Optional
from ..copyright import copyright_manager
from . import parameters
from hvcc.interpreters.pd2hv.NotificationEnum import NotificationEnum
from hvcc.types.compiler import Generator, CompilerResp, CompilerNotif, CompilerMsg, ExternInfo
from hvcc.types.meta import Meta, Daisy
hv_midi_messages = {
"__hv_noteout",
"__hv_ctlout",
"__hv_polytouchout",
"__hv_pgmout",
"__hv_touchout",
"__hv_bendout",
"__hv_midiout",
"__hv_midioutport"
}
class c2daisy(Generator):
""" Generates a Daisy wrapper for a given patch.
"""
@classmethod
def compile(
cls,
c_src_dir: str,
out_dir: str,
externs: ExternInfo,
patch_name: Optional[str] = None,
patch_meta: Meta = Meta(),
num_input_channels: int = 0,
num_output_channels: int = 0,
copyright: Optional[str] = None,
verbose: Optional[bool] = False
) -> CompilerResp:
tick = time.time()
out_dir = os.path.join(out_dir, "daisy")
daisy_meta: Daisy = patch_meta.daisy
board = daisy_meta.board
copyright_c = copyright_manager.get_copyright_for_c(copyright)
try:
# ensure that the output directory does not exist
out_dir = os.path.abspath(out_dir)
if os.path.exists(out_dir):
shutil.rmtree(out_dir)
# copy over static files
shutil.copytree(os.path.join(os.path.dirname(__file__), "static"), out_dir)
# copy over generated C source files
source_dir = os.path.join(out_dir, "source")
shutil.copytree(c_src_dir, source_dir)
if daisy_meta.board_file is not None:
header, board_info = json2daisy.generate_header_from_file(daisy_meta.board_file)
else:
header, board_info = json2daisy.generate_header_from_name(board)
# remove heavy out params from externs
externs.parameters.outParam = [
t for t in externs.parameters.outParam if not any(x == y for x in hv_midi_messages for y in t)]
component_glue = parameters.parse_parameters(
externs.parameters, board_info['components'], board_info['aliases'], 'hardware')
component_glue['class_name'] = board_info['name']
component_glue['patch_name'] = patch_name
component_glue['header'] = f"HeavyDaisy_{patch_name}.hpp"
component_glue['max_channels'] = board_info['channels']
component_glue['num_output_channels'] = num_output_channels
component_glue['has_midi'] = board_info['has_midi']
component_glue['displayprocess'] = board_info['displayprocess']
component_glue['debug_printing'] = daisy_meta.debug_printing
component_glue['usb_midi'] = daisy_meta.usb_midi
component_glue['pool_sizes_kb'] = externs.memoryPoolSizesKb
# samplerate
samplerate = daisy_meta.samplerate
if samplerate >= 96000:
component_glue['samplerate'] = 96000
elif samplerate >= 48000:
component_glue['samplerate'] = 48000
elif samplerate >= 32000:
component_glue['samplerate'] = 32000
elif samplerate >= 16000:
component_glue['samplerate'] = 16000
else:
component_glue['samplerate'] = 8000
# blocksize
blocksize = daisy_meta.blocksize
if blocksize is not None:
component_glue['blocksize'] = max(min(256, blocksize), 1)
else:
component_glue['blocksize'] = None
component_glue['copyright'] = copyright_c
daisy_h_path = os.path.join(source_dir, f"HeavyDaisy_{patch_name}.hpp")
with open(daisy_h_path, "w") as f:
f.write(header)
loader = jinja2.FileSystemLoader(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates'))
env = jinja2.Environment(loader=loader, trim_blocks=True, lstrip_blocks=True)
daisy_cpp_path = os.path.join(source_dir, f"HeavyDaisy_{patch_name}.cpp")
rendered_cpp = env.get_template('HeavyDaisy.cpp').render(component_glue)
with open(daisy_cpp_path, 'w') as f:
f.write(rendered_cpp)
makefile_replacements: Dict[str, Any] = {'name': patch_name}
makefile_replacements['linker_script'] = daisy_meta.linker_script
# libdaisy path
path = daisy_meta.libdaisy_path
if isinstance(path, int):
makefile_replacements['libdaisy_path'] = f'{"../" * path}libdaisy'
elif isinstance(path, str):
makefile_replacements['libdaisy_path'] = path
makefile_replacements['bootloader'] = daisy_meta.bootloader
makefile_replacements['debug_printing'] = daisy_meta.debug_printing
rendered_makefile = env.get_template('Makefile').render(makefile_replacements)
with open(os.path.join(source_dir, "Makefile"), "w") as f:
f.write(rendered_makefile)
# ======================================================================================
return CompilerResp(
stage="c2daisy",
in_dir=c_src_dir,
out_dir=out_dir,
out_file=os.path.basename(daisy_h_path),
compile_time=time.time() - tick
)
except Exception as e:
return CompilerResp(
stage="c2daisy",
notifs=CompilerNotif(
has_error=True,
exception=e,
warnings=[],
errors=[CompilerMsg(
enum=NotificationEnum.ERROR_EXCEPTION,
message=str(e)
)]
),
in_dir=c_src_dir,
out_dir=out_dir,
compile_time=time.time() - tick
)
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1735754044.2628229
hvcc-0.13.3/hvcc/generators/c2daisy/parameters.py 0000644 0000000 0000000 00000022331 14735300474 016626 0 ustar 00
from copy import deepcopy
from typing import Any, Dict, Optional
from hvcc.types.compiler import ExternParams
def filter_match(
set: Dict,
key: str,
match: str,
key_exclude: Optional[str] = None,
match_exclude: Optional[str] = None
) -> filter:
if (key_exclude is not None and match_exclude is not None):
return filter(lambda x: x.get(key, '') == match and x.get(key_exclude, '') != match_exclude, set)
else:
return filter(lambda x: x.get(key, '') == match, set)
def verify_param_exists(name: str, original_name: str, components: Dict, input: bool = True) -> Any:
for comp in components:
# Dealing with the cvouts the way we have it set up is really annoying
if comp['component'] == 'CVOuts':
if name == comp['name']:
if input:
raise TypeError(
f'Parameter "{original_name}" cannot be used as an {"input" if input else "output"}')
return
else:
variants = [mapping['name'].format_map(
{'name': comp['name']}) for mapping in comp['mapping']]
if name in variants:
if input and comp['direction'] == 'output' or not input and comp['direction'] == 'input':
raise TypeError(
f'Parameter "{original_name}" cannot be used as an {"input" if input else "output"}')
return
raise NameError(f'Unknown parameter "{original_name}"')
def verify_param_direction(name: str, components: Dict) -> bool:
for comp in components:
if comp['component'] == 'CVOuts':
if name == comp['name']:
return True
else:
variants = [mapping['name'].format_map(
{'name': comp['name']}) for mapping in comp['mapping']]
if name in variants:
return True
return False
def get_root_component(variant: str, original_name: str, components: Dict) -> str:
for comp in components:
if comp['component'] == 'CVOuts':
if variant == comp['name']:
return variant
else:
variants = [mapping['name'].format_map(
{'name': comp['name']}) for mapping in comp['mapping']]
if variant in variants:
return comp['name']
raise NameError(f'Unknown parameter "{original_name}"')
def get_component_mapping(
component_variant: str,
original_name: str,
component: Dict,
components: Dict
) -> Dict:
for variant in component['mapping']:
if component['component'] == 'CVOuts':
stripped = variant['name'].format_map({'name': ''})
if stripped in component['name']:
return variant
elif variant['name'].format_map({'name': component['name']}) == component_variant:
return variant
raise NameError(f'Unknown parameter "{original_name}"')
def verify_param_used(
component: Dict,
params_in: Dict,
params_out: Dict,
params_in_original_name: Dict,
params_out_original_name: Dict,
components: Dict
) -> bool:
# Exclude parents, since they don't have 1-1 i/o mapping
if component.get('is_parent', False):
return True
for param in {**params_in, **params_out}:
root = get_root_component(
param, ({**params_in_original_name, **params_out_original_name})[param], components)
if root == component['name']:
return True
return False
def de_alias(
name: str,
aliases: Dict,
components: Dict
) -> str:
low = name.lower()
# simple case
if low in aliases:
return aliases[low]
# aliased variant
potential_aliases = list(filter(lambda x: x in low, aliases))
for alias in potential_aliases:
try:
target_component = list(filter_match(
components, 'name', aliases[alias]))[0]
# The CVOuts setup really bothers me
if target_component['component'] != 'CVOuts':
for mapping in target_component['mapping']:
if mapping['name'].format_map({'name': alias}) == low:
return mapping['name'].format_map({'name': aliases[alias]})
except IndexError:
# No matching alias from filter
pass
# otherwise, it's a direct parameter or unkown one
return low
def parse_parameters(
parameters: ExternParams,
components: Dict,
aliases: Dict,
object_name: str
) -> Dict:
"""
Parses the `parameters` passed from hvcc and generates getters and setters
according to the info in `components`. The `aliases` help disambiguate parameters
and the `object_name` sets the identifier for the generated Daisy hardware class,
in this case `hardware`.
"""
# Verify that the params are valid and remove unused components
replacements: Dict = {}
params_in: Dict = {}
params_in_original_names: Dict = {}
for key, recv in parameters.inParam:
de_aliased = de_alias(key, aliases, components)
params_in[de_aliased] = recv
params_in_original_names[de_aliased] = key
params_out = {}
params_out_original_names = {}
for key, msg in parameters.outParam:
de_aliased = de_alias(key, aliases, components)
params_out[de_aliased] = msg
params_out_original_names[de_aliased] = key
[verify_param_exists(key, params_in_original_names[key],
components, input=True) for key in params_in]
[verify_param_exists(key, params_out_original_names[key],
components, input=False) for key in params_out]
for i in range(len(components) - 1, -1, -1):
if not verify_param_used(
components[i], params_in, params_out,
params_in_original_names, params_out_original_names,
components):
components.pop(i)
out_idx = 0
replacements['parameters'] = []
replacements['output_parameters'] = []
replacements['loop_write_in'] = []
replacements['callback_write_out'] = []
replacements['loop_write_out'] = []
replacements['hook_write_out'] = []
replacements['callback_write_in'] = []
for param_name, param in params_in.items():
root = get_root_component(
param_name, params_in_original_names[param_name], components)
component = list(filter_match(components, 'name', root))[0]
param_struct = {
"hash_enum": params_in_original_names[param_name], 'name': root, 'type': component['component'].upper()}
replacements['parameters'].append(param_struct)
mapping = get_component_mapping(
param_name, params_in_original_names[param_name], component, components)
write_location = 'callback_write_in' if mapping.get(
'where', 'callback') == 'callback' else 'loop_write_in'
component_info = deepcopy(component)
component_info['name'] = root
component_info['class_name'] = object_name
# A bit of a hack to get cv_1, etc to be written as CV_1
component_info['name_upper'] = root.upper()
component_info['value'] = f'output_data[{out_idx}]'
component_info['default_prefix'] = component.get(
"default_prefix", '') if component.get('default', False) else ''
process = mapping["get"].format_map(component_info)
replacements[write_location].append(
{"process": process, "bool": mapping.get('bool', False),
"hash_enum": params_in_original_names[param_name]})
for param_name, param in params_out.items():
root = get_root_component(
param_name, params_out_original_names[param_name], components)
component = list(filter_match(components, 'name', root))[0]
mapping = get_component_mapping(
param_name, params_out_original_names[param_name], component, components)
default_prefix = component.get(
"default_prefix", '') if component.get('default', False) else ''
write_locations = {'callback': 'callback_write_out',
'loop': 'loop_write_out',
'hook': 'hook_write_out'}
write_location = write_locations.get(mapping.get('where', 'callback'), 'callback_write_out')
param_struct = {
'hash_enum': params_out_original_names[param_name],
'index': out_idx, 'name': param_name,
'hook': write_location == 'hook_write_out'}
replacements['output_parameters'].append(param_struct)
component_info = deepcopy(component)
component_info['hash_enum'] = params_out_original_names[param_name]
component_info['name'] = root
component_info['class_name'] = object_name
component_info['value'] = f'output_data[{out_idx}]' if \
write_location != 'hook_write_out' else 'sig'
component_info['default_prefix'] = default_prefix
write = mapping["set"].format_map(component_info)
replacements[write_location].append(
{"name": param_name,
"process": write,
"bool": mapping.get('bool', False),
"value": component_info['value']})
out_idx += 1
replacements['output_comps'] = len(replacements['output_parameters'])
return replacements
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1685549294.7502067
hvcc-0.13.3/hvcc/generators/c2daisy/static/README.md 0000644 0000000 0000000 00000017253 14435670357 016675 0 ustar 00 # Daisy
To build this code, navigate into the source folder and run `make`.
Flashing can be done over USB with `make program-dfu`. Make sure your Daisy is in DFU mode ([check this out if you're not sure how to do that](https://github.com/electro-smith/DaisyWiki/wiki/1.-Setting-Up-Your-Development-Environment#4-Run-the-Blink-Example)). If you have an ST-Link or other JTAG programmer, you can use `make program`.
If you've made hardware based on the Daisy Seed or Patch Submodule, you can supply custom json for the board description.
# Interacting with the Daisy I/O
Each board has a selection of physical I/O that can interact with your PD patch. Most components have an _alias_, which allows you to refer to the same input/output by different names if you have a preference. All names and aliases are _case insensitive_, so you can style them however you like (e.g. `GateIn`).
Some components have _variants_, which allow you to interact with them in multiple ways. For example, you can receive a bang from a `Gate In` instead of a float if you add `_trig` to the end of the gate's name (or any of its aliases). So, if a `Gate In`'s name is `gatein1`, you would use `gatein1_trig`.
Here's what each component expects for its default behavior and variants:
| Type (_variant) | Behavior |
| --- | --- |
| **Inputs** | --- |
| Voltage Input | Returns a floating point representation of the voltage at its input. The typical range is 0-5 V, which is represented as 0-1. |
| Bipolar Voltage Input | Similar to a regular voltage input, but can represent negative voltages. |
| Switch | Returns a bang on the signal's rising edge (i.e. when the switch is actuated). |
| Switch (_press) | Returns a float representing the current state (1 = pressed, 0 = not pressed) |
| Switch (_fall) | Returns a bang on the signal's falling edge (i.e. when the switch is released). |
| Switch (_seconds) | Returns a float representing the number of seconds the switch has been held down. |
| SPDT Switch | Returns a float representing the current state, either 0 or 1. |
| Encoder | Returns a 1 if turned one direction, -1 if turned in the other, and 0 otherwise. |
| Encoder (\_rise) | Returns a bang when the encoder is pressed. The special alias _EncSwitch_ is always bound to this. |
| Encoder (_press) | Same as switch _press. |
| Encoder (_fall) | Same as switch _fall. |
| Encoder (_seconds) | Same as switch _seconds. |
| Gate In | Returns a float representing the current gate voltage, where a _high_ voltage is 1 and a _low_ voltage is 0. |
| Gate In (_trig) | Returns a bang on the rising edge of the gate signal. |
| **Outputs** | --- |
| CV Out | Expects a floating point value from 0-1, usually converted to 0-5V. |
| Gate Out | Expects a floating point value from 0-1. 0 sets the output low, and 1 sets it high. |
| LED | Expects a floating point value from 0-1. The brightness is PWM modulated to match the input. |
| RGB LED | Expects a floating point value from 0-1. The default behavior sets all three colors to the same brightness. |
| RGB LED (_white) | Same as default. |
| RGB LED (_red) | Expects a floating point value from 0-1. Sets the brightness of the red LED only. |
| RGB LED (_green) | Expects a floating point value from 0-1. Sets the brightness of the green LED only. |
| RGB LED (_blue) | Expects a floating point value from 0-1. Sets the brightness of the blue LED only. |
# Daisy Board I/O
## patch
| Name | Aliases | Type | Variants |
| --- | --- | --- | --- |
| knob1 | knob, ctrl, ctrl1 | Voltage Input | --- |
| knob2 | ctrl2 | Voltage Input | --- |
| knob3 | ctrl3 | Voltage Input | --- |
| knob4 | ctrl4 | Voltage Input | --- |
| encoder | --- | Encoder | encoder_press, encoder_rise, encoder_fall, encoder_seconds |
| gateout | --- | Gate Out | --- |
| cvout1 | cvout | CV Out | --- |
| cvout2 | --- | CV Out | --- |
| gatein1 | gate, gate1 | Gate In | gatein1_trig |
| gatein2 | gate2 | Gate In | gatein2_trig |
## patch_init
| Name | Aliases | Type | Variants |
| --- | --- | --- | --- |
| cv_1 | knob, knob1, ctrl, ctrl1 | Voltage Input | --- |
| cv_2 | knob2, ctrl2 | Voltage Input | --- |
| cv_3 | knob3, ctrl3 | Voltage Input | --- |
| cv_4 | knob4, ctrl4 | Voltage Input | --- |
| cv_5 | knob5, ctrl5 | Voltage Input | --- |
| cv_6 | knob6, ctrl6 | Voltage Input | --- |
| cv_7 | knob7, ctrl7 | Voltage Input | --- |
| cv_8 | knob8, ctrl8 | Voltage Input | --- |
| adc_9 | --- | Voltage Input | --- |
| adc_10 | --- | Voltage Input | --- |
| adc_11 | --- | Voltage Input | --- |
| adc_12 | --- | Voltage Input | --- |
| gate_out_1 | gateout, gateout1 | Gate Out | --- |
| gate_out_2 | gateout2 | Gate Out | --- |
| cvout1 | cvout, cv_out_1 | CV Out | --- |
| cvout2 | cv_out_2 | CV Out | --- |
| gate_in_1 | gate, gate1 | Gate In | gate_in_1_trig |
| gate_in_2 | gate2 | Gate In | gate_in_2_trig |
| sw1 | switch, switch1, button | Switch | sw1_press, sw1_fall, sw1_seconds |
| sw2 | switch2, toggle | Switch | sw2_press, sw2_fall, sw2_seconds |
## petal
| Name | Aliases | Type | Variants |
| --- | --- | --- | --- |
| sw1 | switch, switch1 | Switch | sw1_press, sw1_fall, sw1_seconds |
| sw2 | switch2 | Switch | sw2_press, sw2_fall, sw2_seconds |
| sw3 | switch3 | Switch | sw3_press, sw3_fall, sw3_seconds |
| sw4 | switch4 | Switch | sw4_press, sw4_fall, sw4_seconds |
| sw5 | switch5 | Switch | sw5_press, sw5_fall, sw5_seconds |
| sw6 | switch6 | Switch | sw6_press, sw6_fall, sw6_seconds |
| sw7 | switch7 | Switch | sw7_press, sw7_fall, sw7_seconds |
| encoder | --- | Encoder | encoder_press, encoder_rise, encoder_fall, encoder_seconds |
| knob1 | knob, ctrl, ctrl1 | Voltage Input | --- |
| knob2 | ctrl2 | Voltage Input | --- |
| knob3 | ctrl3 | Voltage Input | --- |
| knob4 | ctrl4 | Voltage Input | --- |
| knob5 | ctrl5 | Voltage Input | --- |
| knob6 | ctrl6 | Voltage Input | --- |
| expression | --- | Voltage Input | --- |
| led_ring_1 ... led_ring_8 | --- | RGB LED | led_ring_1_red, led_ring_1_green, led_ring_1_blue, led_ring_1_white |
| led_fs_1 | --- | LED | --- |
| led_fs_2 | --- | LED | --- |
| led_fs_3 | --- | LED | --- |
| led_fs_4 | --- | LED | --- |
## pod
| Name | Aliases | Type | Variants |
| --- | --- | --- | --- |
| sw1 | switch, button, switch1, button1 | Switch | sw1_press, sw1_fall, sw1_seconds |
| sw2 | switch2, button2 | Switch | sw2_press, sw2_fall, sw2_seconds |
| knob1 | knob, ctrl, ctrl1 | Voltage Input | --- |
| knob2 | ctrl2 | Voltage Input | --- |
| encoder | --- | Encoder | encoder_press, encoder_rise, encoder_fall, encoder_seconds |
| led1 | led | RGB LED | led1_red, led1_green, led1_blue, led1_white |
| led2 | --- | RGB LED | led2_red, led2_green, led2_blue, led2_white |
| led3 | --- | LED | --- |
| cvout1 | cvout | CV Out | --- |
| gatein | gate, gate1 | Gate In | gatein_trig |
| sw3 | switch3 | SPDT Switch | --- |
## field
| Name | Aliases | Type | Variants |
| --- | --- | --- | --- |
| sw1 | switch, button, switch1, button1 | Switch | sw1_press, sw1_fall, sw1_seconds |
| sw2 | switch2, button2 | Switch | sw2_press, sw2_fall, sw2_seconds |
| cv1 | --- | Bipolar Voltage Input | --- |
| cv2 | --- | Bipolar Voltage Input | --- |
| cv3 | --- | Bipolar Voltage Input | --- |
| cv4 | --- | Bipolar Voltage Input | --- |
| knob1 | knob, ctrl, ctrl1 | Voltage Input | --- |
| knob2 | ctrl2 | Voltage Input | --- |
| knob3 ... knob8 | --- | Voltage Input | --- |
| cvout1 | cvout | CV Out | --- |
| cvout2 | --- | CV Out | --- |
| gatein | --- | Gate In | gatein_trig |
| gateout | --- | Gate Out | --- |
| pada1 ... pada8 | --- | Switch | pada1_press, pada1_fall |
| padb1 ... padb8 | --- | Switch | padb1_press, padb1_fall |
| led_key_a1 ... led_key_a8 | --- | LED | --- |
| led_key_b1 ... led_key_b8 | --- | LED | --- |
| led_knob_1 ... led_knob_8 | --- | LED | --- |
| led_sw_1 | --- | LED | --- |
| led_sw_2 | --- | LED | --- |
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1739952989.4286463
hvcc-0.13.3/hvcc/generators/c2daisy/templates/HeavyDaisy.cpp 0000644 0000000 0000000 00000036555 14755311535 020700 0 ustar 00 {{copyright}}
#include "Heavy_{{patch_name}}.h"
#include "Heavy_{{patch_name}}.hpp"
#include "HeavyDaisy_{{patch_name}}.hpp"
#define SAMPLE_RATE {{samplerate}}.f
{% if (has_midi is sameas true) or (usb_midi is sameas true) %}
#define HV_HASH_NOTEIN 0x67E37CA3
#define HV_HASH_CTLIN 0x41BE0f9C
#define HV_HASH_POLYTOUCHIN 0xBC530F59
#define HV_HASH_PGMIN 0x2E1EA03D
#define HV_HASH_TOUCHIN 0x553925BD
#define HV_HASH_BENDIN 0x3083F0F7
#define HV_HASH_MIDIIN 0x149631bE
#define HV_HASH_MIDIREALTIMEIN 0x6FFF0BCF
#define HV_HASH_NOTEOUT 0xD1D4AC2
#define HV_HASH_CTLOUT 0xE5e2A040
#define HV_HASH_POLYTOUCHOUT 0xD5ACA9D1
#define HV_HASH_PGMOUT 0x8753E39E
#define HV_HASH_TOUCHOUT 0x476D4387
#define HV_HASH_BENDOUT 0xE8458013
#define HV_HASH_MIDIOUT 0x6511DE55
#define HV_HASH_MIDIOUTPORT 0x165707E4
#define MIDI_RT_CLOCK 0xF8
#define MIDI_RT_START 0xFA
#define MIDI_RT_CONTINUE 0xFB
#define MIDI_RT_STOP 0xFC
#define MIDI_RT_ACTIVESENSE 0xFE
#define MIDI_RT_RESET 0xFF
#define MIDI_OUT_FIFO_SIZE 128
{% endif %}
using namespace daisy;
json2daisy::Daisy{{ class_name|capitalize }} hardware;
Heavy_{{patch_name}}* hv;
void audiocallback(daisy::AudioHandle::InputBuffer in, daisy::AudioHandle::OutputBuffer out, size_t size);
static void sendHook(HeavyContextInterface *c, const char *receiverName, uint32_t receiverHash, const HvMessage * m);
{% if debug_printing is sameas true %}
static void printHook(HeavyContextInterface *c, const char *printLabel, const char *msgString, const HvMessage *m);
/** FIFO to hold messages as we're ready to print them */
FIFO, 64> event_log;
{% elif usb_midi is sameas true %}
daisy::MidiUsbHandler midiusb;
{% endif %}
{% if (has_midi is sameas true) or (usb_midi is sameas true) %}
FIFO midi_tx_fifo;
{% endif %}
// int midiOutCount;
// uint8_t* midiOutData;
void CallbackWriteIn(Heavy_{{patch_name}}* hv);
void LoopWriteIn(Heavy_{{patch_name}}* hv);
void CallbackWriteOut();
void LoopWriteOut();
void PostProcess();
void Display();
{% if output_parameters|length > 0 %}
constexpr int DaisyNumOutputParameters = {{output_parameters|length}};
/** This array holds the output values received from PD hooks. These values are
* then written at the appropriate time in the following callback or loop.
*/
float output_data[DaisyNumOutputParameters];
struct DaisyHvParamOut
{
uint32_t hash;
uint32_t index;
void (*hook_write_out)(float);
void Process(float sig)
{
output_data[index] = sig;
if (hook_write_out)
(*hook_write_out)(sig);
}
};
{% for param in hook_write_out %}
void {{param.name}}_hook(float sig) {
{{param.process}}
};
{% endfor %}
DaisyHvParamOut DaisyOutputParameters[DaisyNumOutputParameters] = {
{% for param in output_parameters %}
{% if param.hook %}
{ (uint32_t) HV_{{patch_name|upper}}_PARAM_OUT_{{param.hash_enum|upper}}, {{param.index}}, &{{param.name}}_hook }, // {{param.name}}
{% else %}
{ (uint32_t) HV_{{patch_name|upper}}_PARAM_OUT_{{param.hash_enum|upper}}, {{param.index}}, nullptr }, // {{param.name}}
{% endif %}
{% endfor %}
};
{% endif %}
{% if (has_midi is sameas true) or (usb_midi is sameas true) %}
// Typical Switch case for Message Type.
void HandleMidiMessage(MidiEvent m)
{
ScopedIrqBlocker block; //< Disables interrupts while in scope
for (int i = 0; i <= 2; ++i) {
hv->sendMessageToReceiverV(HV_HASH_MIDIIN, 0, "ff",
(float) m.data[i],
(float) m.channel);
}
switch(m.type)
{
case SystemRealTime: {
float srtType;
switch(m.srt_type)
{
case TimingClock:
srtType = MIDI_RT_CLOCK;
break;
case Start:
srtType = MIDI_RT_START;
break;
case Continue:
srtType = MIDI_RT_CONTINUE;
break;
case Stop:
srtType = MIDI_RT_STOP;
break;
case ActiveSensing:
srtType = MIDI_RT_ACTIVESENSE;
break;
case Reset:
srtType = MIDI_RT_RESET;
break;
}
hv->sendMessageToReceiverV(HV_HASH_MIDIREALTIMEIN, 0, "ff",
(float) srtType);
break;
}
case NoteOff: {
NoteOnEvent p = m.AsNoteOn();
hv->sendMessageToReceiverV(HV_HASH_NOTEIN, 0, "fff",
(float) p.note, // pitch
(float) 0, // velocity
(float) p.channel);
break;
}
case NoteOn: {
NoteOnEvent p = m.AsNoteOn();
hv->sendMessageToReceiverV(HV_HASH_NOTEIN, 0, "fff",
(float) p.note, // pitch
(float) p.velocity, // velocity
(float) p.channel);
break;
}
case PolyphonicKeyPressure: { // polyphonic aftertouch
PolyphonicKeyPressureEvent p = m.AsPolyphonicKeyPressure();
hv->sendMessageToReceiverV(HV_HASH_POLYTOUCHIN, 0, "fff",
(float) p.pressure, // pressure
(float) p.note, // note
(float) p.channel);
break;
}
case ControlChange: {
ControlChangeEvent p = m.AsControlChange();
hv->sendMessageToReceiverV(HV_HASH_CTLIN, 0, "fff",
(float) p.value, // value
(float) p.control_number, // cc number
(float) p.channel);
break;
}
case ProgramChange: {
ProgramChangeEvent p = m.AsProgramChange();
hv->sendMessageToReceiverV(HV_HASH_PGMIN, 0, "ff",
(float) p.program,
(float) p.channel);
break;
}
case ChannelPressure: {
ChannelPressureEvent p = m.AsChannelPressure();
hv->sendMessageToReceiverV(HV_HASH_TOUCHIN, 0, "ff",
(float) p.pressure,
(float) p.channel);
break;
}
case PitchBend: {
PitchBendEvent p = m.AsPitchBend();
// combine 7bit lsb and msb into 32bit int
hv_uint32_t value = (((hv_uint32_t) m.data[1]) << 7) | ((hv_uint32_t) m.data[0]);
hv->sendMessageToReceiverV(HV_HASH_BENDIN, 0, "ff",
(float) value,
(float) p.channel);
break;
}
default: break;
}
}
{% endif %}
int main(void)
{
hardware.Init(true);
hv = new Heavy_{{patch_name}}(SAMPLE_RATE, {{pool_sizes_kb.internal}}, {{pool_sizes_kb.inputQueue}}, {{pool_sizes_kb.outputQueue}});
{% if samplerate %}
hardware.SetAudioSampleRate({{samplerate}});
{% endif %}
{% if blocksize %}
hardware.SetAudioBlockSize({{blocksize}});
{% endif %}
{% if has_midi is sameas true %}
MidiUartHandler::Config midi_config;
hardware.midi.Init(midi_config);
hardware.midi.StartReceive();
{% endif %}
{% if (debug_printing is not sameas true) and (usb_midi is sameas true) %}
MidiUsbHandler::Config midiusb_config;
midiusb.Init(midiusb_config);
midiusb.StartReceive();
{% endif %}
hardware.StartAudio(audiocallback);
{% if debug_printing is sameas true %}
hardware.som.StartLog();
hv->setPrintHook(printHook);
uint32_t now = System::GetNow();
uint32_t log_time = System::GetNow();
{% endif %}
hv->setSendHook(sendHook);
for(;;)
{
{% if debug_printing %}
now = System::GetNow();
{% endif %}
hardware.LoopProcess();
{% if has_midi %}
hardware.midi.Listen();
while(hardware.midi.HasEvents())
{
HandleMidiMessage(hardware.midi.PopEvent());
}
{% endif %}
{% if (debug_printing is not sameas true) and (usb_midi is sameas true) %}
midiusb.Listen();
while(midiusb.HasEvents())
{
HandleMidiMessage(midiusb.PopEvent());
}
{% endif %}
Display();
{% if loop_write_in|length > 0 %}
LoopWriteIn(hv);
{% endif %}
{% if output_parameters|length > 0 %}
LoopWriteOut();
{% endif %}
{% if (has_midi is sameas true) or (usb_midi is sameas true) %}
uint8_t midiData[MIDI_OUT_FIFO_SIZE];
size_t numElements = 0;
while(!midi_tx_fifo.IsEmpty() && numElements < MIDI_OUT_FIFO_SIZE)
{
midiData[numElements++] = midi_tx_fifo.PopFront();
}
if(numElements > 0)
{
{% if has_midi is sameas true %}
hardware.midi.SendMessage(midiData, numElements);
{% endif %}
{% if (debug_printing is not sameas true) and (usb_midi is sameas true) %}
midiusb.SendMessage(midiData, numElements);
{% endif %}
}
{% endif %}
{% if debug_printing is sameas true %}
/** Now separately, every 5ms we'll print the top message in our queue if there is one */
if(now - log_time > 5)
{
log_time = now;
if(!event_log.IsEmpty())
{
auto msg = event_log.PopFront();
hardware.som.PrintLine(msg);
}
}
{% endif %}
}
}
/** The audio processing function. At the standard 48KHz sample rate and a block
* size of 48, this will fire every millisecond.
*/
void audiocallback(daisy::AudioHandle::InputBuffer in, daisy::AudioHandle::OutputBuffer out, size_t size)
{
{% if num_output_channels == 0 %}
// A zero fill to keep I/O quiet for a patch lacking ADC~/DAC~
for (size_t chn = 0; chn < {{max_channels}}; chn++)
{
for (size_t i = 0; i < size; i++)
out[chn][i] = 0;
}
{% endif %}
{% if parameters|length > 0 %}
hardware.ProcessAllControls();
CallbackWriteIn(hv);
{% endif %}
hv->process((float**)in, (float**)out, size);
{% if output_parameters|length > 0 %}
CallbackWriteOut();
{% endif %}
hardware.PostProcess();
}
{% if (has_midi is sameas true) or (usb_midi is sameas true) %}
void HandleMidiOut(uint8_t *midiData, const uint8_t numElements)
{
for (int i = 0; i < numElements; i++) {
midi_tx_fifo.PushBack(midiData[i]);
}
}
void HandleMidiSend(uint32_t sendHash, const HvMessage *m)
{
switch(sendHash){
case HV_HASH_NOTEOUT: // __hv_noteout
{
uint8_t note = hv_msg_getFloat(m, 0);
uint8_t velocity = hv_msg_getFloat(m, 1);
uint8_t ch = hv_msg_getFloat(m, 2);
ch %= 16; // drop any pd "ports"
const uint8_t numElements = 3;
uint8_t midiData[numElements];
if (velocity > 0){
midiData[0] = 0x90 | ch; // noteon
} else {
midiData[0] = 0x80 | ch; // noteoff
}
midiData[1] = note;
midiData[2] = velocity;
HandleMidiOut(midiData, numElements);
break;
}
case HV_HASH_POLYTOUCHOUT:
{
uint8_t value = hv_msg_getFloat(m, 0);
uint8_t note = hv_msg_getFloat(m, 1);
uint8_t ch = hv_msg_getFloat(m, 2);
ch %= 16; // drop any pd "ports"
const uint8_t numElements = 3;
uint8_t midiData[numElements];
midiData[0] = 0xA0 | ch; // send Poly Aftertouch
midiData[1] = note;
midiData[2] = value;
HandleMidiOut(midiData, numElements);
break;
}
case HV_HASH_CTLOUT:
{
uint8_t value = hv_msg_getFloat(m, 0);
uint8_t cc = hv_msg_getFloat(m, 1);
uint8_t ch = hv_msg_getFloat(m, 2);
ch %= 16;
const uint8_t numElements = 3;
uint8_t midiData[numElements];
midiData[0] = 0xB0 | ch; // send CC
midiData[1] = cc;
midiData[2] = value;
HandleMidiOut(midiData, numElements);
break;
}
case HV_HASH_PGMOUT:
{
uint8_t pgm = hv_msg_getFloat(m, 0);
uint8_t ch = hv_msg_getFloat(m, 1);
ch %= 16;
const uint8_t numElements = 2;
uint8_t midiData[numElements];
midiData[0] = 0xC0 | ch; // send Program Change
midiData[1] = pgm;
HandleMidiOut(midiData, numElements);
break;
}
case HV_HASH_TOUCHOUT:
{
uint8_t value = hv_msg_getFloat(m, 0);
uint8_t ch = hv_msg_getFloat(m, 1);
ch %= 16;
const uint8_t numElements = 2;
uint8_t midiData[numElements];
midiData[0] = 0xD0 | ch; // send Touch
midiData[1] = value;
HandleMidiOut(midiData, numElements);
break;
}
case HV_HASH_BENDOUT:
{
uint16_t value = hv_msg_getFloat(m, 0);
uint8_t lsb = value & 0x7F;
uint8_t msb = (value >> 7) & 0x7F;
uint8_t ch = hv_msg_getFloat(m, 1);
ch %= 16;
const uint8_t numElements = 3;
uint8_t midiData[numElements];
midiData[0] = 0xE0 | ch; // send Bend
midiData[1] = lsb;
midiData[2] = msb;
HandleMidiOut(midiData, numElements);
break;
}
// not functional yet
// case HV_HASH_MIDIOUT: // __hv_midiout
// {
// if (midiOutCount == 0 ) {
// uint8_t midiOutData[3];
// }
// midiOutData[midiOutCount] = hv_msg_getFloat(m, 0);
// if (midiOutCount < 2) {
// midiOutCount++;
// break;
// }
// HandleMidiOut(midiOutData, 3);
// midiOutCount = 0;
// break;
// }
default:
break;
}
}
{% endif %}
/** Receives messages from PD and writes them to the appropriate
* index in the `output_data` array, to be written later.
*/
static void sendHook(HeavyContextInterface *c, const char *receiverName, uint32_t receiverHash, const HvMessage * m)
{
{% if output_parameters|length > 0 %}
for (int i = 0; i < DaisyNumOutputParameters; i++)
{
if (DaisyOutputParameters[i].hash == receiverHash)
{
DaisyOutputParameters[i].Process(msg_getFloat(m, 0));
}
}
{% endif %}
{% if (has_midi is sameas true) or (usb_midi is sameas true) %}
HandleMidiSend(receiverHash, m);
{% endif %}
}
{% if debug_printing is sameas true %}
/** Receives messages from the PD [print] object and writes them to the serial console.
*
*/
static void printHook(HeavyContextInterface *c, const char *printLabel, const char *msgString, const HvMessage *m)
{
char buf[64];
char *dst = buf;
int len = strnlen(printLabel, 48);
dst = stpncpy(dst, printLabel, len);
dst = stpcpy(dst, " ");
dst = stpncpy(dst, msgString, 63-len);
/** Regardless of message, let's add the message data to our queue to output */
event_log.PushBack(buf);
}
{% endif %}
/** Sends signals from the Daisy hardware to the PD patch via the receive objects during the main loop
*
*/
void LoopWriteIn(Heavy_{{patch_name}}* hv)
{
ScopedIrqBlocker block; //< Disables interrupts while in scope
{% for param in loop_write_in %}
{% if param.bool %}
if ({{param.process}})
hv->sendBangToReceiver((uint32_t) HV_{{patch_name|upper}}_PARAM_IN_{{param.hash_enum|upper}});
{% else %}
hv->sendFloatToReceiver((uint32_t) HV_{{patch_name|upper}}_PARAM_IN_{{param.hash_enum|upper}}, {{param.process}});
{% endif %}
{% endfor %}
}
/** Sends signals from the Daisy hardware to the PD patch via the receive objects during the audio callback
*
*/
void CallbackWriteIn(Heavy_{{patch_name}}* hv)
{
{% for param in callback_write_in %}
{% if param.bool %}
if ({{param.process}})
hv->sendBangToReceiver((uint32_t) HV_{{patch_name|upper}}_PARAM_IN_{{param.hash_enum|upper}});
{% else %}
hv->sendFloatToReceiver((uint32_t) HV_{{patch_name|upper}}_PARAM_IN_{{param.hash_enum|upper}}, {{param.process}});
{% endif %}
{% endfor %}
}
/** Writes the values sent to PD's receive objects to the Daisy hardware during the main loop
*
*/
void LoopWriteOut() {
ScopedIrqBlocker block; //< Disables interrupts while in scope
{% for param in loop_write_out %}
{% if param.bool %}
if ({{param.value}})
{{param.process}}
{% else %}
{{param.process}}
{% endif %}
{% endfor %}
}
/** Writes the values sent to PD's receive objects to the Daisy hardware during the audio callback
*
*/
void CallbackWriteOut() {
{% for param in callback_write_out %}
{% if param.bool %}
if ({{param.value}})
{{param.process}}
{% else %}
{{param.process}}
{% endif %}
{% endfor %}
}
/** Handles the display code if the hardware defines a display
*
*/
void Display() {
{{displayprocess}}
}
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1735754044.263823
hvcc-0.13.3/hvcc/generators/c2daisy/templates/Makefile 0000644 0000000 0000000 00000000777 14735300474 017561 0 ustar 00 # Project Name
TARGET = HeavyDaisy_{{name}}
# Library Locations
LIBDAISY_DIR = {{libdaisy_path}}
{% if linker_script != '' %}
LDSCRIPT = {{linker_script}}
{% endif %}
{% if bootloader != None %}
APP_TYPE = {{bootloader}}
{% endif %}
{% if debug_printing is sameas true %}
LDFLAGS += -u _printf_float
{% endif %}
# Project Source
C_SOURCES = $(wildcard *.c)
CPP_SOURCES = $(wildcard *.cpp)
# Core location, and generic makefile.
SYSTEM_FILES_DIR = $(LIBDAISY_DIR)/core
include $(SYSTEM_FILES_DIR)/Makefile
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1685549294.7512066
hvcc-0.13.3/hvcc/generators/c2dpf/__init__.py 0000644 0000000 0000000 00000000000 14435670357 015656 0 ustar 00 ././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1735754044.263823
hvcc-0.13.3/hvcc/generators/c2dpf/c2dpf.py 0000644 0000000 0000000 00000014464 14735300474 015131 0 ustar 00 # Copyright (C) 2021-2024 Wasted Audio
#
# 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 .
import os
import shutil
import time
import jinja2
from typing import Optional
from ..copyright import copyright_manager
from ..filters import filter_uniqueid
from hvcc.interpreters.pd2hv.NotificationEnum import NotificationEnum
from hvcc.types.compiler import Generator, CompilerResp, CompilerMsg, CompilerNotif, ExternInfo
from hvcc.types.meta import Meta, DPF
class c2dpf(Generator):
""" Generates a DPF wrapper for a given patch.
"""
@classmethod
def compile(
cls,
c_src_dir: str,
out_dir: str,
externs: ExternInfo,
patch_name: Optional[str] = None,
patch_meta: Meta = Meta(),
num_input_channels: int = 0,
num_output_channels: int = 0,
copyright: Optional[str] = None,
verbose: Optional[bool] = False
) -> CompilerResp:
tick = time.time()
out_dir = os.path.join(out_dir, "plugin")
receiver_list = externs.parameters.inParam
sender_list = externs.parameters.outParam
dpf_meta: DPF = patch_meta.dpf
dpf_path = dpf_meta.dpf_path
copyright_c = copyright_manager.get_copyright_for_c(copyright)
try:
# ensure that the output directory does not exist
out_dir = os.path.abspath(out_dir)
if os.path.exists(out_dir):
shutil.rmtree(out_dir)
# copy over static files
shutil.copytree(os.path.join(os.path.dirname(__file__), "static"), out_dir)
shutil.copy(os.path.join(os.path.dirname(__file__), "static/README.md"), f'{out_dir}/../')
# copy over generated C source files
source_dir = os.path.join(out_dir, "source")
shutil.copytree(c_src_dir, source_dir)
# initialize the jinja template environment
env = jinja2.Environment()
env.filters["uniqueid"] = filter_uniqueid
env.loader = jinja2.FileSystemLoader(
os.path.join(os.path.dirname(os.path.abspath(__file__)), "templates"))
# generate DPF wrapper from template
dpf_h_path = os.path.join(source_dir, f"HeavyDPF_{patch_name}.hpp")
with open(dpf_h_path, "w") as f:
f.write(env.get_template("HeavyDPF.hpp").render(
name=patch_name,
meta=dpf_meta,
class_name=f"HeavyDPF_{patch_name}",
num_input_channels=num_input_channels,
num_output_channels=num_output_channels,
receivers=receiver_list,
senders=sender_list,
copyright=copyright_c))
dpf_cpp_path = os.path.join(source_dir, f"HeavyDPF_{patch_name}.cpp")
with open(dpf_cpp_path, "w") as f:
f.write(env.get_template("HeavyDPF.cpp").render(
name=patch_name,
meta=dpf_meta,
class_name=f"HeavyDPF_{patch_name}",
num_input_channels=num_input_channels,
num_output_channels=num_output_channels,
receivers=receiver_list,
senders=sender_list,
pool_sizes_kb=externs.memoryPoolSizesKb,
copyright=copyright_c))
if dpf_meta.enable_ui:
dpf_ui_path = os.path.join(source_dir, f"HeavyDPF_{patch_name}_UI.cpp")
with open(dpf_ui_path, "w") as f:
f.write(env.get_template("HeavyDPF_UI.cpp").render(
name=patch_name,
meta=dpf_meta,
class_name=f"HeavyDPF_{patch_name}",
receivers=receiver_list,
senders=sender_list,
copyright=copyright_c))
dpf_h_path = os.path.join(source_dir, "DistrhoPluginInfo.h")
with open(dpf_h_path, "w") as f:
f.write(env.get_template("DistrhoPluginInfo.h").render(
name=patch_name,
meta=dpf_meta,
class_name=f"HeavyDPF_{patch_name}",
num_input_channels=num_input_channels,
num_output_channels=num_output_channels,
pool_sizes_kb=externs.memoryPoolSizesKb,
copyright=copyright_c))
# plugin makefile
with open(os.path.join(source_dir, "Makefile"), "w") as f:
f.write(env.get_template("Makefile_plugin").render(
name=patch_name,
meta=dpf_meta,
nosimd=patch_meta.nosimd,
dpf_path=dpf_path))
# project makefile
with open(os.path.join(source_dir, "../../Makefile"), "w") as f:
f.write(env.get_template("Makefile_project").render(
name=patch_name,
meta=dpf_meta,
dpf_path=dpf_path))
return CompilerResp(
stage="c2dpf",
in_dir=c_src_dir,
out_dir=out_dir,
out_file=os.path.basename(dpf_h_path),
compile_time=time.time() - tick
)
except Exception as e:
return CompilerResp(
stage="c2dpf",
notifs=CompilerNotif(
has_error=True,
exception=e,
warnings=[],
errors=[CompilerMsg(
enum=NotificationEnum.ERROR_EXCEPTION,
message=str(e)
)]
),
in_dir=c_src_dir,
out_dir=out_dir,
compile_time=time.time() - tick
)
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1727976067.2655523
hvcc-0.13.3/hvcc/generators/c2dpf/static/README.md 0000644 0000000 0000000 00000002057 14677551203 016325 0 ustar 00 # Distrho Plugin Format
This output is for the Distrho Plugin Format ([DPF](https://github.com/DISTRHO/DPF)), and can be used to build LV2, VST2 and jack standalone versions of your Heavy code.
# Build Instructions
Make sure you have a (recent) DPF in the root of your output directory
```bash
$ cd
$ git clone https://github.com/DISTRHO/DPF.git dpf
```
Then compile the plugins from the source folder:
```bash
$ make
```
This will result in an `bin/` folder with all binary assets.
* LV2 - move `bin/.lv2/` folder to your local `~/.lv2/` dir
* VST2 - move `bin/-vst.so`, can be placed directly into your `~/.vst/` dir
## Metadata
You will likely want to add more configuration options to your project. Create a json file as described in the [documentation](https://wasted-audio.github.io/hvcc/docs/03.gen.dpf.html#metadata).
## Jack
The Jack binary can be executed in place and used to test functionality `./bin/`. Currently there is no UI, so this is not recommended. You will have to be running jack in order to use this.
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1735754044.263823
hvcc-0.13.3/hvcc/generators/c2dpf/templates/DistrhoPluginInfo.h 0000644 0000000 0000000 00000004542 14735300474 021333 0 ustar 00 {{copyright}}
#pragma once
#define DISTRHO_PLUGIN_NAME "{{name.replace('_', ' ')}}"
{%- if meta.plugin_uri != None %}
#define DISTRHO_PLUGIN_URI "{{meta.plugin_uri}}"
{% else %}
#define DISTRHO_PLUGIN_URI "urn:hvcc:{{name}}"
{%- endif %}
{%- if meta.plugin_clap_id != None %}
#define DISTRHO_PLUGIN_CLAP_ID "{{meta.plugin_clap_id}}"
{% else %}
#define DISTRHO_PLUGIN_CLAP_ID "urn.hvcc.{{name}}"
{%- endif %}
#define DISTRHO_PLUGIN_NUM_INPUTS {{num_input_channels}}
#define DISTRHO_PLUGIN_NUM_OUTPUTS {{num_output_channels}}
#define DISTRHO_PLUGIN_IS_SYNTH {{1 if num_output_channels > 0 and meta.midi_input > 0 else 0}}
#define DISTRHO_PLUGIN_HAS_UI {{1 if meta.enable_ui is sameas true else 0}}
#define DISTRHO_PLUGIN_IS_RT_SAFE 1
#define DISTRHO_PLUGIN_WANT_PROGRAMS 0
#define DISTRHO_PLUGIN_WANT_STATE 0
#define DISTRHO_PLUGIN_WANT_TIMEPOS 1
#define DISTRHO_PLUGIN_WANT_FULL_STATE 0
#define DISTRHO_PLUGIN_WANT_MIDI_INPUT {{1 if meta.midi_input is sameas true else 0}}
#define DISTRHO_PLUGIN_WANT_MIDI_OUTPUT {{1 if meta.midi_output is sameas true else 0}}
{%- if meta.lv2_info != None %}
#define DISTRHO_PLUGIN_LV2_CATEGORY "{{meta.lv2_info}}"
{%- endif %}
{%- if meta.vst3_info != None %}
#define DISTRHO_PLUGIN_VST3_CATEGORIES "{{meta.vst3_info}}"
{%- endif %}
{%- if meta.clap_info|length > 0 %}
#define DISTRHO_PLUGIN_CLAP_FEATURES "{{ meta.clap_info|join('", "') }}"
{%- endif %}
// for level monitoring
#define DISTRHO_PLUGIN_WANT_DIRECT_ACCESS 0
{% if meta.enable_ui is sameas true %}
// if you are using a UI you'll probably want to modify these settings to your needs
#define DISTRHO_UI_USE_CUSTOM 1
#define DISTRHO_UI_CUSTOM_INCLUDE_PATH "DearImGui.hpp"
#define DISTRHO_UI_CUSTOM_WIDGET_TYPE DGL_NAMESPACE::ImGuiTopLevelWidget
{%- if meta.ui_size != None %}
#define DISTRHO_UI_DEFAULT_WIDTH {{meta.ui_size.width}}
#define DISTRHO_UI_DEFAULT_HEIGHT {{meta.ui_size.height}}
{% else %}
#define DISTRHO_UI_DEFAULT_WIDTH 400
#define DISTRHO_UI_DEFAULT_HEIGHT 400
{%- endif %}
{%- endif %}
{%- if meta.enable_modgui is sameas true %}
#undef DISTRHO_PLUGIN_USES_MODGUI
#define DISTRHO_PLUGIN_USES_MODGUI 1
{%- endif %}
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1735754044.264823
hvcc-0.13.3/hvcc/generators/c2dpf/templates/HeavyDPF.cpp 0000644 0000000 0000000 00000015041 14735300474 017661 0 ustar 00 {{copyright}}
#include "Heavy_{{name}}.h"
#include "{{class_name}}.hpp"
#include
{% if meta.denormals is sameas false %}
#include "extra/ScopedDenormalDisable.hpp"
{% endif %}
#define HV_DPF_NUM_PARAMETER {{receivers|length + senders|length}}
#define HV_HASH_NOTEIN 0x67E37CA3
#define HV_HASH_CTLIN 0x41BE0f9C
#define HV_HASH_POLYTOUCHIN 0xBC530F59
#define HV_HASH_PGMIN 0x2E1EA03D
#define HV_HASH_TOUCHIN 0x553925BD
#define HV_HASH_BENDIN 0x3083F0F7
#define HV_HASH_MIDIIN 0x149631bE
#define HV_HASH_MIDIREALTIMEIN 0x6FFF0BCF
#define HV_HASH_NOTEOUT 0xD1D4AC2
#define HV_HASH_CTLOUT 0xE5e2A040
#define HV_HASH_POLYTOUCHOUT 0xD5ACA9D1
#define HV_HASH_PGMOUT 0x8753E39E
#define HV_HASH_TOUCHOUT 0x476D4387
#define HV_HASH_BENDOUT 0xE8458013
#define HV_HASH_MIDIOUT 0x6511DE55
#define HV_HASH_MIDIOUTPORT 0x165707E4
#define MIDI_RT_CLOCK 0xF8
#define MIDI_RT_START 0xFA
#define MIDI_RT_CONTINUE 0xFB
#define MIDI_RT_STOP 0xFC
#define MIDI_RT_ACTIVESENSE 0xFE
#define MIDI_RT_RESET 0xFF
#define HV_HASH_DPF_BPM 0xDF8C2721
// midi realtime messages
std::set mrtSet {
MIDI_RT_CLOCK,
MIDI_RT_START,
MIDI_RT_CONTINUE,
MIDI_RT_STOP,
MIDI_RT_RESET
};
START_NAMESPACE_DISTRHO
// -------------------------------------------------------------------
// Heavy Send and Print hooks
static void hvSendHookFunc(HeavyContextInterface *c, const char *sendName, uint32_t sendHash, const HvMessage *m)
{
{{class_name}}* plugin = ({{class_name}}*)c->getUserData();
if (plugin != nullptr)
{
plugin->setOutputParameter(sendHash, m);
{%- if meta.midi_output is sameas true %}
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
plugin->handleMidiSend(sendHash, m);
#endif
{% endif %}
}
}
static void hvPrintHookFunc(HeavyContextInterface *c, const char *printLabel, const char *msgString, const HvMessage *m)
{
char buf[64];
char* dst = buf;
int len = strnlen(printLabel, 48);
dst = strncpy(dst, printLabel, len);
dst = strcpy(dst, " ");
dst = strncpy(dst, msgString, 63-len);
printf("> %s \n", buf);
}
// -------------------------------------------------------------------
// Main DPF plugin class
{{class_name}}::{{class_name}}()
: Plugin(HV_DPF_NUM_PARAMETER, 0, 0)
{
{% for k, v in receivers + senders -%}
_parameters[{{loop.index-1}}] = {{v.attributes.default}}f;
{% endfor %}
_context = hv_{{name}}_new_with_options(getSampleRate(), {{pool_sizes_kb.internal}}, {{pool_sizes_kb.inputQueue}}, {{pool_sizes_kb.outputQueue}});
_context->setUserData(this);
_context->setSendHook(&hvSendHookFunc);
_context->setPrintHook(&hvPrintHookFunc);
{% if receivers|length > 0 %}
// ensure that the new context has the current parameters
for (int i = 0; i < HV_DPF_NUM_PARAMETER; ++i) {
setParameterValue(i, _parameters[i]);
}
{%- endif %}
}
{{class_name}}::~{{class_name}}() {
hv_{{name}}_free(_context);
}
{%- if meta.port_groups != None %}
{% include 'portGroups.cpp' %}
{%- endif %}
void {{class_name}}::initParameter(uint32_t index, Parameter& parameter)
{
{%- if (receivers|length > 0) or (senders|length > 0) -%}
// initialise parameters with defaults
switch (index)
{
{% for k, v in receivers + senders %}
{% include 'initParameter.cpp' %}
{% endfor -%}
}
{% endif %}
}
// -------------------------------------------------------------------
// Internal data
float {{class_name}}::getParameterValue(uint32_t index) const
{
{%- if (receivers|length > 0) or (senders|length > 0) %}
return _parameters[index];
{% else %}
return 0.0f;
{%- endif %}
}
void {{class_name}}::setParameterValue(uint32_t index, float value)
{
{%- if receivers|length > 0 %}
switch (index) {
{% for k, v in receivers -%}
case {{loop.index-1}}: {
_context->sendFloatToReceiver(
Heavy_{{name}}::Parameter::In::{{k|upper}},
value);
break;
}
{% endfor %}
default: return;
}
_parameters[index] = value;
{% else %}
// nothing to do
{%- endif %}
}
void {{class_name}}::setOutputParameter(uint32_t sendHash, const HvMessage *m)
{
{%- if senders|length > 0 %}
switch (sendHash) {
{% for k, v in senders -%}
case {{v.hash}}: // {{v.display}}
_parameters[param{{v.display}}] = hv_msg_getFloat(m, 0);
break;
{% endfor %}
}
{%- endif %}
}
// -------------------------------------------------------------------
// Process
// void {{class_name}}::activate()
// {
// }
// void {{class_name}}::deactivate()
// {
// }
{%- if meta.midi_input is sameas true %}
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
{% include 'midiInput.cpp' %}
#endif
{% endif %}
{%- if meta.midi_output is sameas true %}
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
{% include 'midiOutput.cpp' %}
#endif
{% endif %}
{% include 'hostTransportEvents.cpp' %}
// -------------------------------------------------------------------
// DPF Plugin run() loop
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
void {{class_name}}::run(const float** inputs, float** outputs, uint32_t frames, const MidiEvent* midiEvents, uint32_t midiEventCount)
{
handleMidiInput(frames, midiEvents, midiEventCount);
#else
void {{class_name}}::run(const float** inputs, float** outputs, uint32_t frames)
{
#endif
hostTransportEvents(frames);
{% if meta.denormals is sameas false %}
const ScopedDenormalDisable sdd;
{% endif %}
const TimePosition& timePos(getTimePosition());
if (timePos.playing && timePos.bbt.valid)
_context->sendMessageToReceiverV(HV_HASH_DPF_BPM, 0, "f", timePos.bbt.beatsPerMinute);
_context->process((float**)inputs, outputs, frames);
}
// -------------------------------------------------------------------
// Callbacks
void {{class_name}}::sampleRateChanged(double newSampleRate)
{
hv_{{name}}_free(_context);
_context = hv_{{name}}_new_with_options(getSampleRate(), {{pool_sizes_kb.internal}}, {{pool_sizes_kb.inputQueue}}, {{pool_sizes_kb.outputQueue}});
_context->setUserData(this);
_context->setSendHook(&hvSendHookFunc);
_context->setPrintHook(&hvPrintHookFunc);
{% if receivers|length > 0 -%}
// ensure that the new context has the current parameters
for (int i = 0; i < HV_DPF_NUM_PARAMETER; ++i) {
setParameterValue(i, _parameters[i]);
}
{%- endif %}
}
// -----------------------------------------------------------------------
/* Plugin entry point, called by DPF to create a new plugin instance. */
Plugin* createPlugin()
{
return new {{class_name}}();
}
// -----------------------------------------------------------------------
END_NAMESPACE_DISTRHO
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1735754044.264823
hvcc-0.13.3/hvcc/generators/c2dpf/templates/HeavyDPF.hpp 0000644 0000000 0000000 00000010361 14735300474 017666 0 ustar 00 {{copyright}}
#ifndef _HEAVY_LV2_{{name|upper}}_
#define _HEAVY_LV2_{{name|upper}}_
#include "DistrhoPlugin.hpp"
#include "DistrhoPluginInfo.h"
#include "Heavy_{{name}}.hpp"
START_NAMESPACE_DISTRHO
static void hvSendHookFunc(HeavyContextInterface *c, const char *sendName, uint32_t sendHash, const HvMessage *m);
static void hvPrintHookFunc(HeavyContextInterface *c, const char *printLabel, const char *msgString, const HvMessage *m);
class {{class_name}} : public Plugin
{
public:
enum Parameters
{
{% for k, v in receivers -%}
param{{v.display}},
{% endfor %}
{% for k, v in senders -%}
param{{v.display}},
{% endfor %}
};
{% if meta.port_groups != None %}
enum PortGroups
{
{%- if meta.port_groups.input|length %}
{%- for group, value in meta.port_groups.input.items() %}
kPortGroup{{group}},
{%- endfor %}
{%- endif %}
{%- if meta.port_groups.output|length %}
{%- for group, value in meta.port_groups.output.items() %}
kPortGroup{{group}},
{%- endfor %}
{%- endif %}
kPortGroupCount
};
{%- endif %}
{{class_name}}();
~{{class_name}}() override;
void handleMidiInput(uint32_t frames, const MidiEvent* midiEvents, uint32_t midiEventCount);
void handleMidiSend(uint32_t sendHash, const HvMessage *m);
void hostTransportEvents(uint32_t frames);
void setOutputParameter(uint32_t sendHash, const HvMessage *m);
protected:
// -------------------------------------------------------------------
// Information
const char* getLabel() const noexcept override
{
return "{{name}}";
}
{%- if meta.description != None %}
const char* getDescription() const override
{
return "{{meta.description}}";
}
{%- endif %}
const char* getMaker() const noexcept override
{
{%- if meta.maker != None %}
return "{{meta.maker}}";
{% else %}
return "Wasted Audio";
{%- endif %}
}
{%- if meta.homepage != None %}
const char* getHomePage() const override
{
return "{{meta.homepage}}";
}
{%- endif %}
const char* getLicense() const noexcept override
{
{%- if meta.license != None %}
return "{{meta.license}}";
{% else %}
return "GPL v3+";
{%- endif %}
}
uint32_t getVersion() const noexcept override
{
{%- if meta.version != None %}
return d_version({{meta.version}});
{% else %}
return d_version(0, 0, 1);
{%- endif %}
}
int64_t getUniqueId() const noexcept override
{
return int64_t( {{class_name|uniqueid}} );
}
// -------------------------------------------------------------------
// Init
void initParameter(uint32_t index, Parameter& parameter) override;
{% if meta.port_groups != None %}
void initAudioPort(bool input, uint32_t index, AudioPort& port) override;
void initPortGroup(uint32_t groupId, PortGroup& portGroup) override;
{%- endif %}
// -------------------------------------------------------------------
// Internal data
float getParameterValue(uint32_t index) const override;
void setParameterValue(uint32_t index, float value) override;
// -------------------------------------------------------------------
// Process
// void activate() override;
// void deactivate() override;
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
void run(const float** inputs, float** outputs, uint32_t frames, const MidiEvent* midiEvents, uint32_t midiEventCount) override;
#else
void run(const float** inputs, float** outputs, uint32_t frames) override;
#endif
// -------------------------------------------------------------------
// Callbacks
void sampleRateChanged(double newSampleRate) override;
// -------------------------------------------------------------------
private:
{%- if (receivers|length > 0) or senders|length > 0 %}
// parameters
float _parameters[{{receivers|length + senders|length}}]; // in range of [0,1]
{%- endif %}
// transport values
bool wasPlaying = false;
double nextClockTick = 0.0;
double sampleAtCycleStart = 0.0;
// midi out buffer
int midiOutCount;
MidiEvent midiOutEvent;
// heavy context
HeavyContextInterface *_context;
// {{class_name}} f{{name}};
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR({{class_name}})
};
// -----------------------------------------------------------------------
END_NAMESPACE_DISTRHO
#endif // _HEAVY_LV2_{{name|upper}}_
{# newline #}
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1735754044.264823
hvcc-0.13.3/hvcc/generators/c2dpf/templates/HeavyDPF_UI.cpp 0000644 0000000 0000000 00000013137 14735300474 020262 0 ustar 00 {{copyright}}
#include "DistrhoUI.hpp"
#include "ResizeHandle.hpp"
START_NAMESPACE_DISTRHO
// --------------------------------------------------------------------------------------------------------------------
{%- if (receivers|length > 0) or (senders|length > 0) %}
enum HeavyParams {
{%- for k, v in receivers + senders -%}
{{v.display|upper}},
{%- endfor %}
};
{%- endif %}
class ImGuiPluginUI : public UI
{
{% for k, v in receivers + senders -%}
{%- if v.attributes.type == 'bool': %}
bool f{{v.display|lower}} = {{v.attributes.default}}f != 0.0f;
{%- elif v.attributes.type == 'int': %}
int f{{v.display|lower}} = {{v.attributes.default}};
{%- else %}
float f{{v.display|lower}} = {{v.attributes.default}}f;
{%- endif %}
{%- endfor %}
ResizeHandle fResizeHandle;
// ----------------------------------------------------------------------------------------------------------------
public:
/**
UI class constructor.
The UI should be initialized to a default state that matches the plugin side.
*/
ImGuiPluginUI()
: UI(DISTRHO_UI_DEFAULT_WIDTH, DISTRHO_UI_DEFAULT_HEIGHT),
fResizeHandle(this)
{
setGeometryConstraints(DISTRHO_UI_DEFAULT_WIDTH, DISTRHO_UI_DEFAULT_HEIGHT, true);
// hide handle if UI is resizable
if (isResizable())
fResizeHandle.hide();
}
protected:
// ----------------------------------------------------------------------------------------------------------------
// DSP/Plugin Callbacks
/**
A parameter has changed on the plugin side.@n
This is called by the host to inform the UI about parameter changes.
*/
void parameterChanged(uint32_t index, float value) override
{
{%- if (receivers|length > 0) or (senders|length > 0) %}
switch (index) {
{% for k, v in receivers + senders -%}
case {{v.display|upper}}:
{%- if v.attributes.type == 'bool': %}
f{{v.display|lower}} = value != 0.0f;
{%- else %}
f{{v.display|lower}} = value;
{%- endif %}
break;
{% endfor %}
default: return;
}
{% else %}
// nothing to do
{%- endif %}
repaint();
}
// ----------------------------------------------------------------------------------------------------------------
// Widget Callbacks
/**
ImGui specific onDisplay function.
*/
void onImGuiDisplay() override
{
const float width = getWidth();
const float height = getHeight();
const float margin = 20.0f * getScaleFactor();
ImGui::SetNextWindowPos(ImVec2(margin, margin));
ImGui::SetNextWindowSize(ImVec2(width - 2 * margin, height - 2 * margin));
if (ImGui::Begin("{{name.replace('_', ' ')}}", nullptr, ImGuiWindowFlags_NoResize + ImGuiWindowFlags_NoCollapse))
{
{%- for k, v in receivers + senders %}
{%- set v_display = v.display|lower %}
{%- if meta.enumerators != None and meta.enumerators[v.display] is defined -%}
{%- set enums = meta.enumerators[v.display] -%}
{%- set enumlen = enums|length %}
{%- set enum_list = v_display + "_list" %}
const char* {{enum_list}}[{{enumlen}}] = {
{%- for i in enums %}
"{{i}}",
{%- endfor %}
};
if (ImGui::BeginCombo("{{v.display.replace('_', ' ')}}", {{enum_list}}[f{{v_display}}]))
{
for (int n = 0; n < {{enumlen}}; n++)
{
bool is_selected = (f{{v_display}} == n);
if (ImGui::Selectable({{enum_list}}[n], is_selected))
{
f{{v_display}} = n;
editParameter({{v.display|upper}}, true);
setParameterValue({{v.display|upper}}, f{{v_display}});
}
if (is_selected)
ImGui::SetItemDefaultFocus();
}
ImGui::EndCombo();
}
{%- else %}
{%- if v.attributes.type == 'bool': %}
if (ImGui::Toggle("{{v.display.replace('_', ' ')}}", &f{{v_display}}))
{%- elif v.attributes.type == 'int' %}
if (ImGui::SliderInt("{{v.display.replace('_', ' ')}}", &f{{v_display}}, {{v.attributes.min}}f, {{v.attributes.max}}f))
{%- else %}
if (ImGui::SliderFloat("{{v.display.replace('_', ' ')}}", &f{{v_display}}, {{v.attributes.min}}f, {{v.attributes.max}}f))
{%- endif %}
{
{%- if not v.type == "send" %}
if (ImGui::IsItemActivated())
{
editParameter({{v.display|upper}}, true);
}
setParameterValue({{v.display|upper}}, f{{v_display}});
{%- endif %}
}
{%- endif %}
{% endfor %}
if (ImGui::IsItemDeactivated())
{
{% for k, v in receivers + senders -%}
editParameter({{v.display|upper}}, false);
{% endfor -%}
}
}
ImGui::End();
}
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImGuiPluginUI)
};
// --------------------------------------------------------------------------------------------------------------------
UI* createUI()
{
return new ImGuiPluginUI();
}
// --------------------------------------------------------------------------------------------------------------------
END_NAMESPACE_DISTRHO
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1735754044.264823
hvcc-0.13.3/hvcc/generators/c2dpf/templates/Makefile_plugin 0000644 0000000 0000000 00000002624 14735300474 020570 0 ustar 00 NAME = {{name}}
{%- if meta.enable_modgui is sameas true %}
MODGUI_CLASS_NAME = hv_{{name}}
{%- endif %}
{%- if meta.enable_ui is sameas true %}
FILES_DSP = $(filter-out HeavyDPF_{{name}}_UI.cpp, $(wildcard *.cpp))
{%- else %}
FILES_DSP = $(wildcard *.cpp)
{%- endif %}
FILES_DSP += $(wildcard *.c)
{%- if meta.enable_ui is sameas true %}
FILES_UI = HeavyDPF_{{name}}_UI.cpp
FILES_UI += ../../{{dpf_path}}dpf-widgets/opengl/DearImGui.cpp
{%- endif %}
DPF_TARGET_DIR = ../../bin
DPF_BUILD_DIR = ../../build
DPF_PATH = ../../{{dpf_path}}dpf
include ../../{{dpf_path}}dpf/Makefile.plugins.mk
{%- if meta.enable_ui is sameas true %}
BUILD_CXX_FLAGS += -I ../../{{dpf_path}}dpf-widgets/generic
BUILD_CXX_FLAGS += -I ../../{{dpf_path}}dpf-widgets/opengl
{%- endif %}
{%- if meta.makefile_dep|length > 0 %}
{%- for dependency in meta.makefile_dep %}
BUILD_CXX_FLAGS += -I ../../{{dpf_path}}{{dependency}}
{%- endfor %}
{%- endif %}
BUILD_C_FLAGS += -Wno-unused-parameter -std=c11 -fno-strict-aliasing -pthread
BUILD_CXX_FLAGS += -Wno-unused-parameter -fno-strict-aliasing -pthread
LINK_FLAGS += -pthread
{%- if nosimd is sameas true %}
BUILD_C_FLAGS += -DHV_SIMD_NONE
BUILD_CXX_FLAGS += -DHV_SIMD_NONE
{%- endif %}
{% if meta.plugin_formats|length > 0 %}
{%- for format in meta.plugin_formats %}
TARGETS += {{format}}
{%- endfor %}
{% else %}
TARGETS += jack
TARGETS += lv2_dsp
TARGETS += vst
{%- endif %}
all: $(TARGETS)
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1727976067.2655523
hvcc-0.13.3/hvcc/generators/c2dpf/templates/Makefile_project 0000644 0000000 0000000 00000001562 14677551203 020743 0 ustar 00 #!/usr/bin/make -f
# Makefile for DISTRHO Plugins #
# ---------------------------- #
# Created by falkTX
#
# Modified by Wasted Audio
#
include {{dpf_path}}dpf/Makefile.base.mk
all: plugin gen
# --------------------------------------------------------------
dgl:
# ifeq ($(HAVE_CAIRO_OR_OPENGL),true)
# $(MAKE) -C {{dpf_path}}dpf/dgl FILE_BROWSER_DISABLED=true
# endif
plugin: dgl
$(MAKE) all -C plugin/source
ifneq ($(CROSS_COMPILING),true)
gen: plugin lv2_ttl_generator
@$(CURDIR)/{{dpf_path}}dpf/utils/generate-ttl.sh
lv2_ttl_generator:
$(MAKE) -C {{dpf_path}}dpf/utils/lv2-ttl-generator
else
gen:
endif
# --------------------------------------------------------------
clean:
$(MAKE) clean -C {{dpf_path}}dpf/utils/lv2-ttl-generator
$(MAKE) clean -C plugin/source
rm -rf bin build
# --------------------------------------------------------------
.PHONY: plugin
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1735754044.264823
hvcc-0.13.3/hvcc/generators/c2dpf/templates/hostTransportEvents.cpp 0000644 0000000 0000000 00000003654 14735300474 022341 0 ustar 00 // -------------------------------------------------------------------
// Host Transport Events handler
void {{class_name}}::hostTransportEvents(uint32_t frames)
{
// Realtime events
const TimePosition& timePos(getTimePosition());
bool reset = false;
if (timePos.playing)
{
if (timePos.frame == 0)
{
_context->sendMessageToReceiverV(HV_HASH_MIDIREALTIMEIN, 0,
"ff", (float) MIDI_RT_RESET, 0.0);
reset = true;
}
if (! this->wasPlaying)
{
if (timePos.frame == 0)
{
_context->sendMessageToReceiverV(HV_HASH_MIDIREALTIMEIN, 0,
"ff", (float) MIDI_RT_START, 0.0);
}
if (! reset)
{
_context->sendMessageToReceiverV(HV_HASH_MIDIREALTIMEIN, 0,
"ff", (float) MIDI_RT_CONTINUE, 0.0);
}
}
}
else if (this->wasPlaying)
{
_context->sendMessageToReceiverV(HV_HASH_MIDIREALTIMEIN, 0,
"ff", (float) MIDI_RT_STOP, 0.0);
}
this->wasPlaying = timePos.playing;
// sending clock ticks
if (timePos.playing && timePos.bbt.valid)
{
float samplesPerBeat = 60 * getSampleRate() / timePos.bbt.beatsPerMinute;
float samplesPerTick = samplesPerBeat / 24.0;
/* get state */
double nextClockTick = this->nextClockTick;
double sampleAtCycleStart = this->sampleAtCycleStart;
double sampleAtCycleEnd = sampleAtCycleStart + frames;
if (nextClockTick >= 0 && sampleAtCycleStart >= 0 && sampleAtCycleEnd > sampleAtCycleStart) {
while (nextClockTick < sampleAtCycleEnd) {
double delayMs = 1000*(nextClockTick - sampleAtCycleStart)/getSampleRate();
if (delayMs >= 0.0) {
_context->sendMessageToReceiverV(HV_HASH_MIDIREALTIMEIN, delayMs,
"ff", (float) MIDI_RT_CLOCK, 0.0);
}
nextClockTick += samplesPerTick;
}
}
/* save variables for next cycle */
this->sampleAtCycleStart = sampleAtCycleEnd;
this->nextClockTick = nextClockTick;
}
}
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1735754044.265823
hvcc-0.13.3/hvcc/generators/c2dpf/templates/initParameter.cpp 0000644 0000000 0000000 00000004054 14735300474 021061 0 ustar 00 case param{{v.display}}:
parameter.name = "{{v.display.replace('_', ' ')}}";
parameter.symbol = "{{v.display|lower}}";
{%- if v.attributes.type == 'db': %}
parameter.unit = "dB";
{%- elif v.attributes.type in ['hz', 'log_hz']: %}
parameter.unit = "Hz";
{%- endif %}
{%- if v.type == "send" %}
parameter.hints = kParameterIsOutput
{%- else %}
parameter.hints = kParameterIsAutomatable
{%- endif %}
{%- if v.attributes.type == 'bool': %}
| kParameterIsBoolean
{%- elif v.attributes.type == 'trig': -%}
| kParameterIsTrigger
{%- elif v.attributes.type == 'int': -%}
| kParameterIsInteger
{%- elif v.attributes.type in ['log', 'log_hz']: -%}
| kParameterIsLogarithmic
{%- endif %};
parameter.ranges.min = {{v.attributes.min}}f;
parameter.ranges.max = {{v.attributes.max}}f;
parameter.ranges.def = {{v.attributes.default}}f;
{%- if v.attributes.type == 'db' and not (meta.enumerators != None and meta.enumerators[v.display] is defined): %}
{
ParameterEnumerationValue* const enumValues = new ParameterEnumerationValue[1];
enumValues[0].value = {{v.attributes.min}}f;
enumValues[0].label = "-inf";
parameter.enumValues.count = 1;
parameter.enumValues.values = enumValues;
}
{%- endif %}
{%- if meta.enumerators != None and meta.enumerators[v.display] is defined %}
{% set enums = meta.enumerators[v.display] %}
{% set enumlen = enums|length %}
if (ParameterEnumerationValue *values = new ParameterEnumerationValue[{{enumlen}}])
{
parameter.enumValues.restrictedMode = true;
{% for i in enums -%}
values[{{loop.index - 1}}].value = {{loop.index - 1}}.0f;
values[{{loop.index - 1}}].label = "{{i}}";
{% endfor -%}
parameter.enumValues.count = {{enumlen}};
parameter.enumValues.values = values;
}
{%- endif %}
break;
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1735754044.265823
hvcc-0.13.3/hvcc/generators/c2dpf/templates/midiInput.cpp 0000644 0000000 0000000 00000005507 14735300474 020223 0 ustar 00 // -------------------------------------------------------------------
// Midi Input handler
void {{class_name}}::handleMidiInput(uint32_t frames, const MidiEvent* midiEvents, uint32_t midiEventCount)
{
// Midi events
for (uint32_t i=0; i < midiEventCount; ++i)
{
int status = midiEvents[i].data[0];
int command = status & 0xF0;
int channel = status & 0x0F;
int data1 = midiEvents[i].data[1];
int data2 = midiEvents[i].data[2];
// raw [midiin] messages
int dataSize = *(&midiEvents[i].data + 1) - midiEvents[i].data;
for (int i = 0; i < dataSize; ++i) {
_context->sendMessageToReceiverV(HV_HASH_MIDIIN, 1000.0*midiEvents->frame/getSampleRate(), "ff",
(float) midiEvents[i].data[i],
(float) channel);
}
if(mrtSet.find(status) != mrtSet.end())
{
_context->sendMessageToReceiverV(HV_HASH_MIDIREALTIMEIN, 1000.0*midiEvents->frame/getSampleRate(),
"ff", (float) status);
}
// typical midi messages
switch (command) {
case 0x80: { // note off
_context->sendMessageToReceiverV(HV_HASH_NOTEIN, 1000.0*midiEvents->frame/getSampleRate(), "fff",
(float) data1, // pitch
(float) 0, // velocity
(float) channel);
break;
}
case 0x90: { // note on
_context->sendMessageToReceiverV(HV_HASH_NOTEIN, 1000.0*midiEvents->frame/getSampleRate(), "fff",
(float) data1, // pitch
(float) data2, // velocity
(float) channel);
break;
}
case 0xA0: { // polyphonic aftertouch
_context->sendMessageToReceiverV(HV_HASH_POLYTOUCHIN, 1000.0*midiEvents->frame/getSampleRate(), "fff",
(float) data2, // pressure
(float) data1, // note
(float) channel);
break;
}
case 0xB0: { // control change
_context->sendMessageToReceiverV(HV_HASH_CTLIN, 1000.0*midiEvents->frame/getSampleRate(), "fff",
(float) data2, // value
(float) data1, // cc number
(float) channel);
break;
}
case 0xC0: { // program change
_context->sendMessageToReceiverV(HV_HASH_PGMIN, 1000.0*midiEvents->frame/getSampleRate(), "ff",
(float) data1,
(float) channel);
break;
}
case 0xD0: { // aftertouch
_context->sendMessageToReceiverV(HV_HASH_TOUCHIN, 1000.0*midiEvents->frame/getSampleRate(), "ff",
(float) data1,
(float) channel);
break;
}
case 0xE0: { // pitch bend
// combine 7bit lsb and msb into 32bit int
hv_uint32_t value = (((hv_uint32_t) data2) << 7) | ((hv_uint32_t) data1);
_context->sendMessageToReceiverV(HV_HASH_BENDIN, 1000.0*midiEvents->frame/getSampleRate(), "ff",
(float) value,
(float) channel);
break;
}
default: break;
}
}
}
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1735753758.5911822
hvcc-0.13.3/hvcc/generators/c2dpf/templates/midiOutput.cpp 0000644 0000000 0000000 00000006104 14735300037 020411 0 ustar 00 // -------------------------------------------------------------------
// Midi Send handler
void {{class_name}}::handleMidiSend(uint32_t sendHash, const HvMessage *m)
{
MidiEvent midiSendEvent;
midiSendEvent.frame = 0;
midiSendEvent.dataExt = nullptr;
switch(sendHash){
case HV_HASH_NOTEOUT: // __hv_noteout
{
uint8_t note = hv_msg_getFloat(m, 0);
uint8_t velocity = hv_msg_getFloat(m, 1);
uint8_t ch = hv_msg_getFloat(m, 2);
ch %= 16; // drop any pd "ports"
midiSendEvent.size = 3;
if (velocity > 0){
midiSendEvent.data[0] = 0x90 | ch; // noteon
} else {
midiSendEvent.data[0] = 0x80 | ch; // noteoff
}
midiSendEvent.data[1] = note;
midiSendEvent.data[2] = velocity;
writeMidiEvent(midiSendEvent);
break;
}
case HV_HASH_CTLOUT:
{
uint8_t value = hv_msg_getFloat(m, 0);
uint8_t cc = hv_msg_getFloat(m, 1);
uint8_t ch = hv_msg_getFloat(m, 2);
ch %= 16;
midiSendEvent.size = 3;
midiSendEvent.data[0] = 0xB0 | ch; // send CC
midiSendEvent.data[1] = cc;
midiSendEvent.data[2] = value;
writeMidiEvent(midiSendEvent);
break;
}
case HV_HASH_POLYTOUCHOUT:
{
uint8_t value = hv_msg_getFloat(m, 0);
uint8_t note = hv_msg_getFloat(m, 1);
uint8_t ch = hv_msg_getFloat(m, 2);
midiSendEvent.size = 3;
midiSendEvent.data[0] = 0xA0 | ch; // send Poly Aftertouch
midiSendEvent.data[1] = note;
midiSendEvent.data[2] = value;
writeMidiEvent(midiSendEvent);
break;
}
case HV_HASH_PGMOUT:
{
uint8_t pgm = hv_msg_getFloat(m, 0);
uint8_t ch = hv_msg_getFloat(m, 1);
ch %= 16;
midiSendEvent.size = 2;
midiSendEvent.data[0] = 0xC0 | ch; // send Program Change
midiSendEvent.data[1] = pgm;
writeMidiEvent(midiSendEvent);
break;
}
case HV_HASH_TOUCHOUT:
{
uint8_t value = hv_msg_getFloat(m, 0);
uint8_t ch = hv_msg_getFloat(m, 1);
ch %= 16;
midiSendEvent.size = 2;
midiSendEvent.data[0] = 0xD0 | ch; // send Touch
midiSendEvent.data[1] = value;
writeMidiEvent(midiSendEvent);
break;
}
case HV_HASH_BENDOUT:
{
uint16_t value = hv_msg_getFloat(m, 0);
uint8_t lsb = value & 0x7F;
uint8_t msb = (value >> 7) & 0x7F;
uint8_t ch = hv_msg_getFloat(m, 1);
ch %= 16;
midiSendEvent.size = 3;
midiSendEvent.data[0] = 0xE0 | ch; // send Bend
midiSendEvent.data[1] = lsb;
midiSendEvent.data[2] = msb;
writeMidiEvent(midiSendEvent);
break;
}
case HV_HASH_MIDIOUT: // __hv_midiout
{
if (midiOutCount == 0) {
midiOutEvent.frame = 0;
midiOutEvent.dataExt = nullptr;
// we don't support sysex
midiOutEvent.size = 4;
}
midiOutEvent.data[midiOutCount] = hv_msg_getFloat(m, 0);
if (midiOutCount < 3) {
midiOutCount++;
break;
}
writeMidiEvent(midiOutEvent);
midiOutCount = 0;
break;
}
default:
break;
}
}
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1735754044.265823
hvcc-0.13.3/hvcc/generators/c2dpf/templates/portGroups.cpp 0000644 0000000 0000000 00000005071 14735300474 020441 0 ustar 00 void {{class_name}}::initAudioPort(bool input, uint32_t index, AudioPort& port)
{
port.hints = 0x0;
if (input)
{
switch (index)
{
{%- if meta.port_groups.input|length %}
{%- for group, gConfig in meta.port_groups.input.items() %}
{%- for port, value in gConfig.items() %}
case {{value[0] or value}}:
port.name = "Input {{port}} ({{group}})";
port.symbol = "in_{{port|lower}}_{{group|lower}}";
port.groupId = kPortGroup{{group}};
{%- if value[1] is sameas true %}
port.hints = kAudioPortIsCV | kCVPortHasPositiveUnipolarRange | kCVPortHasScaledRange | kCVPortIsOptional;
{%- endif %}
break;
{%- endfor %}
{%- endfor %}
{%- else %}
{%- if num_input_channels == 2 %}
case 0:
port.name = "Input Left";
port.symbol = "in_left";
break;
case 1:
port.name = "Input Right";
port.symbol = "in_right";
break;
port.groupId = kPortGroupStereo;
{%- endif %}
{%- endif %}
}
}
else
{
switch (index)
{
{%- if meta.port_groups.output|length %}
{%- for group, gConfig in meta.port_groups.output.items() %}
{%- for port, value in gConfig.items() %}
case {{value[0] or value}}:
port.name = "Output {{port}} ({{group}})";
port.symbol = "out_{{port|lower}}_{{group|lower}}";
port.groupId = kPortGroup{{group}};
{%- if value[1] is sameas true %}
port.hints = kAudioPortIsCV | kCVPortHasPositiveUnipolarRange | kCVPortHasScaledRange | kCVPortIsOptional;
{%- endif %}
break;
{%- endfor %}
{%- endfor %}
{% else %}
{%- if num_output_channels == 2 %}
case 0:
port.name = "Output Left";
port.symbol = "out_left";
break;
case 1:
port.name = "Output Right";
port.symbol = "out_right";
break;
}
port.groupId = kPortGroupStereo;
{%- endif %}
{%- endif %}
}
}
}
void {{class_name}}::initPortGroup(uint32_t groupId, PortGroup& portGroup)
{
switch (groupId)
{
{%- if meta.port_groups.input|length %}
{%- for group, value in meta.port_groups.input.items() %}
case kPortGroup{{group}}:
portGroup.name = "{{group}}";
portGroup.symbol = "{{group|lower}}";
break;
{%- endfor %}
{%- endif %}
{%- if meta.port_groups.output|length %}
{%- for group, value in meta.port_groups.output.items() %}
case kPortGroup{{group}}:
portGroup.name = "{{group}}";
portGroup.symbol = "{{group|lower}}";
break;
{%- endfor %}
{%- endif %}
}
}
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1685549294.7532067
hvcc-0.13.3/hvcc/generators/c2js/README.md 0000644 0000000 0000000 00000000255 14435670357 014703 0 ustar 00 # c2js Generator
Note: make sure emscripten sdk is sourced and `emcc` command is in your PATH:
http://kripken.github.io/emscripten-site/docs/getting_started/downloads.html ././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1685549294.7532067
hvcc-0.13.3/hvcc/generators/c2js/__init__.py 0000644 0000000 0000000 00000000000 14435670357 015521 0 ustar 00 ././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1739952989.4286463
hvcc-0.13.3/hvcc/generators/c2js/c2js.py 0000644 0000000 0000000 00000025140 14755311535 014632 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2021-2024 Wasted Audio
#
# 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 .
import os
import subprocess
import time
import jinja2
from shutil import which
from typing import Optional
from hvcc.core.hv2ir.HeavyException import HeavyException
from ..copyright import copyright_manager
from hvcc.interpreters.pd2hv.NotificationEnum import NotificationEnum
from hvcc.types.compiler import Generator, CompilerResp, CompilerNotif, CompilerMsg, ExternInfo
from hvcc.types.meta import Meta
class c2js(Generator):
"""Compiles a directory of C source files into javascript. Requires the
emscripten library to be installed - https://github.com/kripken/emscripten
"""
__HV_API = [
"_hv_{0}_new",
"_hv_{0}_new_with_options",
"_hv_delete",
"_hv_processInline",
"_hv_getNumInputChannels",
"_hv_getNumOutputChannels",
"_hv_samplesToMilliseconds",
"_hv_setPrintHook",
"_hv_setSendHook",
"_hv_sendFloatToReceiver",
"_hv_sendBangToReceiver",
"_hv_sendSymbolToReceiver",
"_hv_stringToHash",
"_hv_msg_getByteSize",
"_hv_msg_init",
"_hv_msg_hasFormat",
"_hv_msg_setFloat",
"_hv_msg_getFloat",
"_hv_msg_getTimestamp",
"_hv_table_getLength",
"_hv_table_setLength",
"_hv_table_getBuffer",
"_hv_sendMessageToReceiverV",
"_hv_sendMessageToReceiverFF",
"_hv_sendMessageToReceiverFFF",
"_malloc" # Rationale: https://github.com/emscripten-core/emscripten/issues/6882#issuecomment-406745898
]
@classmethod
def run_emscripten(
cls,
c_src_dir: str,
out_dir: str,
patch_name: str,
output_name: str,
post_js_path: str,
should_modularize: int,
environment: str,
pre_js_path: str = "",
binaryen_async: int = 1
) -> str:
"""Run the emcc command to compile C source files to a javascript library.
"""
# Detect Windows OS, but ignore if running in MingW
if os.name == 'nt' and os.environ.get('MSYSTEM') is None:
emcc_path = which("emcc.bat")
else:
emcc_path = which("emcc")
if emcc_path is None:
raise HeavyException("emcc is not in the PATH")
c_flags = [
f"-I {c_src_dir}",
"-DHV_SIMD_NONE",
"-ffast-math",
"-DNDEBUG",
"-Wall"
]
c_src_paths = [os.path.join(c_src_dir, c) for c in os.listdir(c_src_dir) if c.endswith((".c"))]
cpp_src_paths = [os.path.join(c_src_dir, cpp) for cpp in os.listdir(c_src_dir) if cpp.endswith((".cpp"))]
obj_paths = []
cmd = ""
# compile C files
for c in c_src_paths:
obj_path = f"{os.path.splitext(c)[0]}.o"
cmd = [emcc_path] + c_flags + ["-c", "-o", obj_path, c] # type: ignore
subprocess.check_output(cmd) # run emscripten
obj_paths += [obj_path]
# compile C++ files
for cpp in cpp_src_paths:
obj_path = f"{os.path.splitext(cpp)[0]}.o"
cmd = [emcc_path] + c_flags + ["-std=c++11"] + ["-c", "-o", obj_path, cpp] # type: ignore
subprocess.check_output(cmd) # run emscripten
obj_paths += [obj_path]
# exported heavy api methods
hv_api_defs = ", ".join([f"\"{x.format(patch_name)}\"" for x in cls.__HV_API])
# output path
wasm_js_path = os.path.join(out_dir, f"{output_name}.js")
linker_flags = [
"-O3",
"-s", "RESERVED_FUNCTION_POINTERS=2",
"-s", "DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=$addFunction",
"-s", f"EXPORTED_FUNCTIONS=[{hv_api_defs.format(patch_name)}]",
"-s", f"MODULARIZE={should_modularize}",
'-s', 'ASSERTIONS=1',
'-s', f'ENVIRONMENT={environment}',
'-s', 'SINGLE_FILE=1',
'-s', 'ALLOW_TABLE_GROWTH=1',
'-s', f'BINARYEN_ASYNC_COMPILATION={binaryen_async}', # Set this to 0 for the worklet so we don't
# wait for promises when instantiating
"--post-js", post_js_path
]
if len(pre_js_path):
linker_flags = linker_flags + [
"--pre-js", pre_js_path
]
# include C/C++ obj files in js library
cmd = [emcc_path] + obj_paths + linker_flags # type: ignore
subprocess.check_output( # WASM
cmd + [ # type: ignore
"-s", "WASM=1",
"-s", f"EXPORT_NAME='{output_name}_Module'",
"-o", wasm_js_path
])
# clean up
for o in obj_paths:
os.remove(o)
return wasm_js_path
@classmethod
def compile(
cls,
c_src_dir: str,
out_dir: str,
externs: ExternInfo,
patch_name: Optional[str] = None,
patch_meta: Meta = Meta(),
num_input_channels: int = 0,
num_output_channels: int = 0,
copyright: Optional[str] = None,
verbose: Optional[bool] = False
) -> CompilerResp:
tick = time.time()
parameter_list = externs.parameters.inParam
parameter_out_list = externs.parameters.outParam
event_list = externs.events.inEvent
event_out_list = externs.events.outEvent
midi_list = externs.midi.inMidi
midi_out_list = externs.midi.outMidi
out_dir = os.path.join(out_dir, "js")
patch_name = patch_name or "heavy"
copyright_js = copyright_manager.get_copyright_for_c(copyright)
copyright_html = copyright_manager.get_copyright_for_xml(copyright)
if not os.path.exists(out_dir):
os.makedirs(out_dir)
out_dir = os.path.abspath(out_dir)
try:
# initialise the jinja template environment
env = jinja2.Environment()
env.loader = jinja2.FileSystemLoader(os.path.join(
os.path.dirname(__file__),
"template"))
# generate heavy js wrapper from template
# Note: this file will be incorporated into the emscripten output
# and removed afterwards
post_js_path = os.path.join(out_dir, "hv_wrapper.js")
with open(post_js_path, "w") as f:
f.write(env.get_template("hv_wrapper.js").render(
name=patch_name,
copyright=copyright_js,
externs=externs,
pool_sizes_kb=externs.memoryPoolSizesKb))
js_path = cls.run_emscripten(c_src_dir=c_src_dir,
out_dir=out_dir,
patch_name=patch_name,
output_name=patch_name,
post_js_path=post_js_path,
should_modularize=1,
environment="web")
# delete temporary files
os.remove(post_js_path)
js_out_file = os.path.basename(js_path)
# generate index.html from template
with open(os.path.join(out_dir, "index.html"), "w") as f:
f.write(env.get_template("index.html").render(
name=patch_name,
includes=[f"./{js_out_file}"],
parameters=parameter_list,
parameters_out=parameter_out_list,
events=event_list,
events_out=event_out_list,
midi=midi_list,
midi_out=midi_out_list,
copyright=copyright_html))
# generate heavy js worklet from template
# Note: this file will be incorporated into the emscripten output
# and removed afterwards
post_js_path = os.path.join(out_dir, "hv_worklet.js")
with open(post_js_path, "w") as f:
f.write(env.get_template("hv_worklet.js").render(
name=patch_name,
copyright=copyright_js,
externs=externs,
pool_sizes_kb=externs.memoryPoolSizesKb))
pre_js_path = os.path.join(out_dir, "hv_worklet_start.js")
with open(pre_js_path, "w") as f:
f.write(env.get_template("hv_worklet_start.js").render(
name=patch_name,
copyright=copyright_js,
externs=externs,
pool_sizes_kb=externs.memoryPoolSizesKb))
js_path = cls.run_emscripten(c_src_dir=c_src_dir,
out_dir=out_dir,
patch_name=patch_name,
output_name=f"{patch_name}_AudioLibWorklet",
post_js_path=post_js_path,
should_modularize=0,
environment="shell,worker",
pre_js_path=pre_js_path,
binaryen_async=0)
# delete temporary files
os.remove(post_js_path)
os.remove(pre_js_path)
return CompilerResp(
stage="c2js",
in_dir=c_src_dir,
out_dir=out_dir,
out_file=js_out_file,
compile_time=time.time() - tick
)
except Exception as e:
return CompilerResp(
stage="c2js",
notifs=CompilerNotif(
has_error=True,
exception=e,
warnings=[],
errors=[CompilerMsg(
enum=NotificationEnum.ERROR_EXCEPTION,
message=str(e)
)]
),
in_dir=c_src_dir,
out_dir=out_dir,
compile_time=time.time() - tick
)
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1739952883.945202
hvcc-0.13.3/hvcc/generators/c2js/template/hv_worklet.js 0000644 0000000 0000000 00000026727 14755311364 017770 0 ustar 00 {{copyright}}
/*
* AudioLibWorklet - Processes the audio through the Heavy C API
*/
class {{name}}_AudioLibWorklet extends AudioWorkletProcessor {
constructor({ processorOptions }) {
super();
this.sampleRate = processorOptions.sampleRate || 44100.0;
// As of right now (June 2022), blockSize is always 128.
// In the future, it could become dynamic,
// and we'll have to read the lengths of incoming outputs and re-alloc the processBuffer if it changes.
this.blockSize = 128;
// instantiate heavy context
this.heavyContext = _hv_{{name}}_new_with_options(this.sampleRate, {{pool_sizes_kb.internal}}, {{pool_sizes_kb.inputQueue}}, {{pool_sizes_kb.outputQueue}});
this.setPrintHook();
this.setSendHook();
// allocate temporary buffers (pointer size is 4 bytes in javascript)
var lengthInSamples = this.blockSize * this.getNumOutputChannels();
this.processBuffer = new Float32Array(
Module.HEAPF32.buffer,
Module._malloc(lengthInSamples * Float32Array.BYTES_PER_ELEMENT),
lengthInSamples);
this.port.onmessage = (e) => {
console.log(e.data);
switch(e.data.type){
case 'setFloatParameter':
this.setFloatParameter(e.data.name, e.data.value);
break;
case 'sendEvent':
this.sendEvent(e.data.name);
break;
case 'sendMidi':
this.sendMidi(e.data.message);
break;
case 'fillTableWithFloatBuffer':
this.fillTableWithFloatBuffer(e.data.name, e.data.buffer);
break;
default:
console.error('No handler for message of type: ', e.data.type);
}
}
}
process(inputs, outputs, parameters) {
try{
_hv_processInline(this.heavyContext, null, this.processBuffer.byteOffset, this.blockSize);
// TODO: Figure out what "multiple outputs" means if not multiple channels
var output = outputs[0];
for (var i = 0; i < this.getNumOutputChannels(); ++i) {
var channel = output[i];
var offset = i * this.blockSize;
for (var j = 0; j < this.blockSize; ++j) {
channel[j] = this.processBuffer[offset+j];
}
}
} catch(e){
this.port.postMessage({ type:'error', error: e.toString() });
}
return true;
}
getNumInputChannels() {
return (this.heavyContext) ? _hv_getNumInputChannels(this.heavyContext) : -1;
}
getNumOutputChannels() {
return (this.heavyContext) ? _hv_getNumOutputChannels(this.heavyContext) : -1;
}
setPrintHook() {
if (!this.heavyContext) {
console.error("heavy: Can't set Print Hook, no Heavy Context instantiated");
return;
}
var self = this;
// typedef void (HvPrintHook_t) (HeavyContextInterface *context, const char *printName, const char *str, const HvMessage *msg);
var printHook = addFunction(function(context, printName, str, msg) {
// Converts Heavy print callback to a printable message
var timeInSecs =_hv_samplesToMilliseconds(context, _hv_msg_getTimestamp(msg)) / 1000.0;
var m = UTF8ToString(printName) + " [" + timeInSecs.toFixed(3) + "]: " + UTF8ToString(str);
self.port.postMessage({
type: 'printHook',
payload: m
});
},
"viiii"
);
_hv_setPrintHook(this.heavyContext, printHook);
}
setSendHook() {
if (!this.heavyContext) {
console.error("heavy: Can't set Send Hook, no Heavy Context instantiated");
return;
}
var self = this;
// typedef void (HvSendHook_t) (HeavyContextInterface *context, const char *sendName, hv_uint32_t sendHash, const HvMessage *msg);
var sendHook = addFunction(function(context, sendName, sendHash, msg) {
// Filter out MIDI messages
const midiMessage = sendMidiOut(UTF8ToString(sendName), msg);
if (midiMessage.length > 0) {
self.port.postMessage({
type: 'midiOut',
payload: midiMessage
});
} else {
// Converts sendhook callback to (sendName, float) message
self.port.postMessage({
type: 'sendHook',
payload: [UTF8ToString(sendName), _hv_msg_getFloat(msg, 0)]
});
}
},
"viiii"
);
_hv_setSendHook(this.heavyContext, sendHook);
}
sendEvent(name) {
if (this.heavyContext) {
_hv_sendBangToReceiver(this.heavyContext, eventInHashes[name]);
}
}
setFloatParameter(name, floatValue) {
if (this.heavyContext) {
_hv_sendFloatToReceiver(this.heavyContext, parameterInHashes[name], parseFloat(floatValue));
}
}
sendMidi(message) {
sendMidiIn(this.heavyContext, message);
}
sendStringToReceiver(name, message) {
// Note(joe): it's not a good idea to call this frequently it is possible for
// the stack memory to run out over time.
if (this.heavyContext) {
var r = allocate(intArrayFromString(name), 'i8', ALLOC_STACK);
var m = allocate(intArrayFromString(message), 'i8', ALLOC_STACK);
_hv_sendSymbolToReceiver(this.heavyContext, _hv_stringToHash(r), m);
}
}
fillTableWithFloatBuffer(name, buffer) {
var tableHash = tableHashes[name];
if (_hv_table_getBuffer(this.heavyContext, tableHash) !== 0) {
// resize current table to new buffer length
_hv_table_setLength(this.heavyContext, tableHash, buffer.length);
// access internal float buffer from table
let tableBuffer = new Float32Array(
Module.HEAPF32.buffer,
_hv_table_getBuffer(this.heavyContext, tableHash),
buffer.length);
// set the table buffer with the data from the 1st channel (mono)
tableBuffer.set(buffer);
} else {
console.error("heavy: Table '" + name + "' doesn't exist in the patch context.");
}
}
}
var parameterInHashes = {
{%- for k,v in externs.parameters.inParam %}
"{{v.display}}": {{v.hash}}, // {{v.display}}
{%- endfor %}
};
var parameterOutHashes = {
{%- for k,v in externs.parameters.outParam %}
"{{v.display}}": {{v.hash}}, // {{v.display}}
{%- endfor %}
};
var eventInHashes = {
{%- for k,v in externs.events.inEvent %}
"{{v.display}}": {{v.hash}}, // {{v.display}}
{%- endfor %}
};
var eventOutHashes = {
{%- for k,v in externs.events.outEvent %}
"{{v.display}}": {{v.hash}}, // {{v.display}}
{%- endfor %}
};
var tableHashes = {
{%- for k,v in externs.tables %}
"{{v.display}}": {{v.hash}}, // {{v.display}}
{%- endfor %}
};
registerProcessor("{{name}}_AudioLibWorklet", {{name}}_AudioLibWorklet);
// midi_utils
function sendMidiIn(hv_context, message) {
if (hv_context) {
var command = message[0] & 0xF0;
var channel = message[0] & 0x0F;
var data1 = message[1];
var data2 = message[2];
// all events to [midiin]
for (var i = 1; i <= 2; i++) {
_hv_sendMessageToReceiverFF(hv_context, HV_HASH_MIDIIN, 0,
message[i],
channel
);
}
// realtime events to [midirealtimein]
if (MIDI_REALTIME.includes(message[0])) {
_hv_sendMessageToReceiverFF(hv_context, HV_HASH_MIDIREALTIMEIN, 0,
message[0]
);
}
switch(command) {
case 0x80: // note off
_hv_sendMessageToReceiverFFF(hv_context, HV_HASH_NOTEIN, 0,
data1,
0,
channel);
break;
case 0x90: // note on
_hv_sendMessageToReceiverFFF(hv_context, HV_HASH_NOTEIN, 0,
data1,
data2,
channel);
break;
case 0xA0: // polyphonic aftertouch
_hv_sendMessageToReceiverFFF(hv_context, HV_HASH_POLYTOUCHIN, 0,
data2, // pressure
data1, // note
channel);
break;
case 0xB0: // control change
_hv_sendMessageToReceiverFFF(hv_context, HV_HASH_CTLIN, 0,
data2, // value
data1, // cc number
channel);
break;
case 0xC0: // program change
_hv_sendMessageToReceiverFF(hv_context, HV_HASH_PGMIN, 0,
data1,
channel);
break;
case 0xD0: // aftertouch
_hv_sendMessageToReceiverFF(hv_context, HV_HASH_TOUCHIN, 0,
data1,
channel);
break;
case 0xE0: // pitch bend
// combine 7bit lsb and msb into 32bit int
var value = (data2 << 7) | data1;
_hv_sendMessageToReceiverFF(hv_context, HV_HASH_BENDIN, 0,
value,
channel);
break;
default:
// console.error('No handler for midi message: ', message);
}
}
}
function sendMidiOut(sendName, msg) {
switch (sendName) {
case "__hv_noteout":
var note = _hv_msg_getFloat(msg, 0);
var velocity = _hv_msg_getFloat(msg, 1);
var channel = _hv_msg_getFloat(msg, 2) % 16; // no pd midi ports
return [
((velocity > 0) ? 144 : 128) | channel,
note,
velocity
]
case "__hv_ctlout":
var value = _hv_msg_getFloat(msg, 0);
var cc = _hv_msg_getFloat(msg, 1);
var channel = _hv_msg_getFloat(msg, 2) % 16; // no pd midi ports
return [
176 | channel,
cc,
value
]
case "__hv_pgmout":
var program = _hv_msg_getFloat(msg, 0);
var channel = _hv_msg_getFloat(msg, 1) % 16; // no pd midi ports
return [
192 | channel,
program
]
case "__hv_touchout":
var pressure = _hv_msg_getFloat(msg, 0);
var channel = _hv_msg_getFloat(msg, 1) % 16; // no pd midi ports
return [
208 | channel,
pressure,
]
case "__hv_polytouchout":
var value = _hv_msg_getFloat(msg, 0);
var note = _hv_msg_getFloat(msg, 1);
var channel = _hv_msg_getFloat(msg, 2) % 16; // no pd midi ports
return[
160 | channel,
note,
value
]
case "__hv_bendout":
var value = _hv_msg_getFloat(msg, 0);
let lsb = value & 0x7F;
let msb = (value >> 7) & 0x7F;
var channel = _hv_msg_getFloat(msg, 1) % 16; // no pd midi ports
return [
224 | channel,
lsb,
msb
]
case "__hv_midiout":
let firstByte = _hv_msg_getFloat(msg, 0);
return (firstByte === 192 || firstByte === 208) ?
[_hv_msg_getFloat(msg, 0), _hv_msg_getFloat(msg, 1)] :
[_hv_msg_getFloat(msg, 0), _hv_msg_getFloat(msg, 1), _hv_msg_getFloat(msg, 2)];
default:
console.warn(`Unhandled sendName: ${sendName}`);
return [];
}
}
/*
* MIDI Constants
*/
const HV_HASH_NOTEIN = 0x67E37CA3;
const HV_HASH_CTLIN = 0x41BE0f9C;
const HV_HASH_POLYTOUCHIN = 0xBC530F59;
const HV_HASH_PGMIN = 0x2E1EA03D;
const HV_HASH_TOUCHIN = 0x553925BD;
const HV_HASH_BENDIN = 0x3083F0F7;
const HV_HASH_MIDIIN = 0x149631bE;
const HV_HASH_MIDIREALTIMEIN = 0x6FFF0BCF;
const MIDI_REALTIME = [0xF8, 0xFA, 0xFB, 0xFC, 0xFE, 0xFF];
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1735754044.266823
hvcc-0.13.3/hvcc/generators/c2js/template/hv_worklet_start.js 0000644 0000000 0000000 00000000504 14735300474 021164 0 ustar 00
// Some hacks gathered from this thread: https://github.com/emscripten-core/emscripten/issues/6230
var self = {
location: {
href: "http://localhost:3000/" // URL where the module was loaded from
}
};
function importScripts(){
console.warn('importScripts should not be called in an AudioWorklet', arguments);
}
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1739952883.945202
hvcc-0.13.3/hvcc/generators/c2js/template/hv_wrapper.js 0000644 0000000 0000000 00000035324 14755311364 017752 0 ustar 00 {{copyright}}
var audioWorkletSupported = (typeof AudioWorklet === 'function');
/*
* AudioLibLoader - Convenience functions for setting up the web audio context
* and initialising the AudioLib context
*/
var AudioLibLoader = function() {
this.isPlaying = false;
this.webAudioContext = null;
this.webAudioProcessor = null;
this.webAudioWorklet = null;
this.audiolib = null;
}
/*
* @param (Object) options
* @param options.blockSize (Number) number of samples to process in each iteration
* @param options.printHook (Function) callback that gets triggered on each print message
* @param options.sendHook (Function) callback that gets triggered for messages sent via @hv_param/@hv_event
*/
AudioLibLoader.prototype.init = function(options) {
// use provided web audio context or create a new one
this.webAudioContext = options.webAudioContext ||
(new (window.AudioContext || window.webkitAudioContext || null));
if (this.webAudioContext) {
return (async() => {
var blockSize = options.blockSize || 2048;
if (audioWorkletSupported) {
await this.webAudioContext.audioWorklet.addModule("{{name}}_AudioLibWorklet.js");
this.webAudioWorklet = new AudioWorkletNode(this.webAudioContext, "{{name}}_AudioLibWorklet", {
outputChannelCount: [2],
processorOptions: {
sampleRate: this.webAudioContext.sampleRate,
blockSize,
}
});
this.webAudioWorklet.port.onmessage = (event) => {
if (event.data.type === 'printHook' && options.printHook) {
options.printHook(event.data.payload);
} else if (event.data.type === 'sendHook' && options.sendHook) {
options.sendHook(event.data.payload[0], event.data.payload[1]);
} else if (event.data.type === 'midiOut' && options.sendHook) {
options.sendHook("midiOutMessage", event.data.payload);
} else {
console.log('Unhandled message from {{name}}_AudioLibWorklet:', event.data);
}
};
this.webAudioWorklet.connect(this.webAudioContext.destination);
} else {
console.warn('heavy: AudioWorklet not supported, reverting to ScriptProcessorNode');
var instance = new {{name}}_AudioLib({
sampleRate: this.webAudioContext.sampleRate,
blockSize: blockSize,
printHook: options.printHook,
sendHook: options.sendHook
});
this.audiolib = instance;
this.webAudioProcessor = this.webAudioContext.createScriptProcessor(blockSize, instance.getNumInputChannels(), Math.max(instance.getNumOutputChannels(), 1));
this.webAudioProcessor.onaudioprocess = (function(e) {
instance.process(e)
})
}
})();
} else {
console.error("heavy: failed to load - WebAudio API not available in this browser")
}
}
AudioLibLoader.prototype.start = function() {
if (this.audiolib) {
this.webAudioProcessor.connect(this.webAudioContext.destination);
} else {
this.webAudioContext.resume();
}
this.isPlaying = true;
}
AudioLibLoader.prototype.stop = function() {
if (this.audiolib) {
this.webAudioProcessor.disconnect(this.webAudioContext.destination);
} else {
this.webAudioContext.suspend();
}
this.isPlaying = false;
}
AudioLibLoader.prototype.sendFloatParameterToWorklet = function(name, value) {
if (this.audiolib) {
this.audiolib.sendEvent(name, value);
} else {
this.webAudioWorklet.port.postMessage({
type:'setFloatParameter',
name,
value
});
}
}
AudioLibLoader.prototype.sendEvent = function(name, value) {
if (this.audiolib) {
this.audiolib.sendEvent(name, value);
} else {
this.webAudioWorklet.port.postMessage({
type:'sendEvent',
name,
value
});
}
}
AudioLibLoader.prototype.sendMidi = function(message) {
if (this.audiolib) {
this.audiolib.sendMidi(message);
} else {
this.webAudioWorklet.port.postMessage({
type:'sendMidi',
message:message
});
}
}
AudioLibLoader.prototype.fillTableWithFloatBuffer = function(name, buffer) {
if (this.audiolib) {
this.audiolib.fillTableWithFloatBuffer(name, buffer);
} else {
this.webAudioWorklet.port.postMessage({
type:'fillTableWithFloatBuffer',
name,
buffer
});
}
}
Module.AudioLibLoader = AudioLibLoader;
/*
* Heavy Javascript AudioLib - Wraps over the Heavy C API
*/
/*
* @param (Object) options
* @param options.sampleRate (Number) audio sample rate
* @param options.blockSize (Number) number of samples to process in each iteration
* @param options.printHook (Function) callback that gets triggered on each print message
* @param options.sendHook (Function) callback that gets triggered for messages sent via @hv_param/@hv_event
*/
var {{name}}_AudioLib = function(options) {
this.sampleRate = options.sampleRate || 44100.0;
this.blockSize = options.blockSize || 2048;
// instantiate heavy context
this.heavyContext = _hv_{{name}}_new_with_options(this.sampleRate, {{pool_sizes_kb.internal}}, {{pool_sizes_kb.inputQueue}}, {{pool_sizes_kb.outputQueue}});
this.setPrintHook(options.printHook);
this.setSendHook(options.sendHook);
// allocate temporary buffers (pointer size is 4 bytes in javascript)
var lengthInSamples = this.blockSize * this.getNumOutputChannels();
this.processBuffer = new Float32Array(
Module.HEAPF32.buffer,
Module._malloc(lengthInSamples * Float32Array.BYTES_PER_ELEMENT),
lengthInSamples);
}
var parameterInHashes = {
{%- for k,v in externs.parameters.inParam %}
"{{v.display}}": {{v.hash}}, // {{v.display}}
{%- endfor %}
};
var parameterOutHashes = {
{%- for k,v in externs.parameters.outParam %}
"{{v.display}}": {{v.hash}}, // {{v.display}}
{%- endfor %}
};
var eventInHashes = {
{%- for k,v in externs.events.inEvent %}
"{{v.display}}": {{v.hash}}, // {{v.display}}
{%- endfor %}
};
var eventOutHashes = {
{%- for k,v in externs.events.outEvent %}
"{{v.display}}": {{v.hash}}, // {{v.display}}
{%- endfor %}
};
var tableHashes = {
{%- for k,v in externs.tables %}
"{{v.display}}": {{v.hash}}, // {{v.display}}
{%- endfor %}
};
{{name}}_AudioLib.prototype.process = function(event) {
_hv_processInline(this.heavyContext, null, this.processBuffer.byteOffset, this.blockSize);
for (var i = 0; i < this.getNumOutputChannels(); ++i) {
var output = event.outputBuffer.getChannelData(i);
var offset = i * this.blockSize;
for (var j = 0; j < this.blockSize; ++j) {
output[j] = this.processBuffer[offset+j];
}
}
}
{{name}}_AudioLib.prototype.getNumInputChannels = function() {
return (this.heavyContext) ? _hv_getNumInputChannels(this.heavyContext) : -1;
}
{{name}}_AudioLib.prototype.getNumOutputChannels = function() {
return (this.heavyContext) ? _hv_getNumOutputChannels(this.heavyContext) : -1;
}
{{name}}_AudioLib.prototype.setPrintHook = function(hook) {
if (!this.heavyContext) {
console.error("heavy: Can't set Print Hook, no Heavy Context instantiated");
return;
}
if (hook) {
// typedef void (HvPrintHook_t) (HeavyContextInterface *context, const char *printName, const char *str, const HvMessage *msg);
var printHook = addFunction(function(context, printName, str, msg) {
// Converts Heavy print callback to a printable message
var timeInSecs =_hv_samplesToMilliseconds(context, _hv_msg_getTimestamp(msg)) / 1000.0;
var m = UTF8ToString(printName) + " [" + timeInSecs.toFixed(3) + "]: " + UTF8ToString(str);
hook(m);
},
"viiii"
);
_hv_setPrintHook(this.heavyContext, printHook);
}
}
{{name}}_AudioLib.prototype.setSendHook = function(hook) {
if (!this.heavyContext) {
console.error("heavy: Can't set Send Hook, no Heavy Context instantiated");
return;
}
if (hook) {
// typedef void (HvSendHook_t) (HeavyContextInterface *context, const char *sendName, hv_uint32_t sendHash, const HvMessage *msg);
var sendHook = addFunction(function(context, sendName, sendHash, msg) {
const midiMessage = sendMidiOut(UTF8ToString(sendName), msg);
if (midiMessage.length > 0) {
hook("midiOutMessage", midiMessage);
} else {
// Converts sendhook callback to (sendName, float) message
hook(UTF8ToString(sendName), _hv_msg_getFloat(msg, 0));
}
},
"viiii"
);
_hv_setSendHook(this.heavyContext, sendHook);
}
}
{{name}}_AudioLib.prototype.sendEvent = function(name) {
if (this.heavyContext) {
_hv_sendBangToReceiver(this.heavyContext, eventInHashes[name]);
}
}
{{name}}_AudioLib.prototype.sendMidi = function(message) {
sendMidiIn(this.heavyContext, message);
}
{{name}}_AudioLib.prototype.setFloatParameter = function(name, floatValue) {
if (this.heavyContext) {
_hv_sendFloatToReceiver(this.heavyContext, parameterInHashes[name], parseFloat(floatValue));
}
}
{{name}}_AudioLib.prototype.sendStringToReceiver = function(name, message) {
// Note(joe): it's not a good idea to call this frequently it is possible for
// the stack memory to run out over time.
if (this.heavyContext) {
var r = allocate(intArrayFromString(name), 'i8', ALLOC_STACK);
var m = allocate(intArrayFromString(message), 'i8', ALLOC_STACK);
_hv_sendSymbolToReceiver(this.heavyContext, _hv_stringToHash(r), m);
}
}
{{name}}_AudioLib.prototype.fillTableWithFloatBuffer = function(name, buffer) {
var tableHash = tableHashes[name];
if (_hv_table_getBuffer(this.heavyContext, tableHash) !== 0) {
// resize current table to new buffer length
_hv_table_setLength(this.heavyContext, tableHash, buffer.length);
// access internal float buffer from table
tableBuffer = new Float32Array(
Module.HEAPF32.buffer,
_hv_table_getBuffer(this.heavyContext, tableHash),
buffer.length);
// set the table buffer with the data from the 1st channel (mono)
tableBuffer.set(buffer);
} else {
console.error("heavy: Table '" + name + "' doesn't exist in the patch context.");
}
}
Module.{{name}}_AudioLib = {{name}}_AudioLib;
// midi_utils
function sendMidiIn(hv_context, message) {
if (hv_context) {
var command = message[0] & 0xF0;
var channel = message[0] & 0x0F;
var data1 = message[1];
var data2 = message[2];
// all events to [midiin]
for (var i = 1; i <= 2; i++) {
_hv_sendMessageToReceiverFF(hv_context, HV_HASH_MIDIIN, 0,
message[i],
channel
);
}
// realtime events to [midirealtimein]
if (MIDI_REALTIME.includes(message[0])) {
_hv_sendMessageToReceiverFF(hv_context, HV_HASH_MIDIREALTIMEIN, 0,
message[0]
);
}
switch(command) {
case 0x80: // note off
_hv_sendMessageToReceiverFFF(hv_context, HV_HASH_NOTEIN, 0,
data1,
0,
channel);
break;
case 0x90: // note on
_hv_sendMessageToReceiverFFF(hv_context, HV_HASH_NOTEIN, 0,
data1,
data2,
channel);
break;
case 0xA0: // polyphonic aftertouch
_hv_sendMessageToReceiverFFF(hv_context, HV_HASH_POLYTOUCHIN, 0,
data2, // pressure
data1, // note
channel);
break;
case 0xB0: // control change
_hv_sendMessageToReceiverFFF(hv_context, HV_HASH_CTLIN, 0,
data2, // value
data1, // cc number
channel);
break;
case 0xC0: // program change
_hv_sendMessageToReceiverFF(hv_context, HV_HASH_PGMIN, 0,
data1,
channel);
break;
case 0xD0: // aftertouch
_hv_sendMessageToReceiverFF(hv_context, HV_HASH_TOUCHIN, 0,
data1,
channel);
break;
case 0xE0: // pitch bend
// combine 7bit lsb and msb into 32bit int
var value = (data2 << 7) | data1;
_hv_sendMessageToReceiverFF(hv_context, HV_HASH_BENDIN, 0,
value,
channel);
break;
default:
// console.error('No handler for midi message: ', message);
}
}
}
function sendMidiOut(sendName, msg) {
switch (sendName) {
case "__hv_noteout":
var note = _hv_msg_getFloat(msg, 0);
var velocity = _hv_msg_getFloat(msg, 1);
var channel = _hv_msg_getFloat(msg, 2) % 16; // no pd midi ports
return [
((velocity > 0) ? 144 : 128) | channel,
note,
velocity
]
case "__hv_ctlout":
var value = _hv_msg_getFloat(msg, 0);
var cc = _hv_msg_getFloat(msg, 1);
var channel = _hv_msg_getFloat(msg, 2) % 16; // no pd midi ports
return [
176 | channel,
cc,
value
]
case "__hv_pgmout":
var program = _hv_msg_getFloat(msg, 0);
var channel = _hv_msg_getFloat(msg, 1) % 16; // no pd midi ports
return [
192 | channel,
program
]
case "__hv_touchout":
var pressure = _hv_msg_getFloat(msg, 0);
var channel = _hv_msg_getFloat(msg, 1) % 16; // no pd midi ports
return [
208 | channel,
pressure,
]
case "__hv_polytouchout":
var value = _hv_msg_getFloat(msg, 0);
var note = _hv_msg_getFloat(msg, 1);
var channel = _hv_msg_getFloat(msg, 2) % 16; // no pd midi ports
return[
160 | channel,
note,
value
]
case "__hv_bendout":
var value = _hv_msg_getFloat(msg, 0);
let lsb = value & 0x7F;
let msb = (value >> 7) & 0x7F;
var channel = _hv_msg_getFloat(msg, 1) % 16; // no pd midi ports
return [
224 | channel,
lsb,
msb
]
case "__hv_midiout":
let firstByte = _hv_msg_getFloat(msg, 0);
return (firstByte === 192 || firstByte === 208) ?
[_hv_msg_getFloat(msg, 0), _hv_msg_getFloat(msg, 1)] :
[_hv_msg_getFloat(msg, 0), _hv_msg_getFloat(msg, 1), _hv_msg_getFloat(msg, 2)];
default:
console.warn(`Unhandled sendName: ${sendName}`);
return [];
}
}
/*
* MIDI Constants
*/
const HV_HASH_NOTEIN = 0x67E37CA3;
const HV_HASH_CTLIN = 0x41BE0f9C;
const HV_HASH_POLYTOUCHIN = 0xBC530F59;
const HV_HASH_PGMIN = 0x2E1EA03D;
const HV_HASH_TOUCHIN = 0x553925BD;
const HV_HASH_BENDIN = 0x3083F0F7;
const HV_HASH_MIDIIN = 0x149631bE;
const HV_HASH_MIDIREALTIMEIN = 0x6FFF0BCF;
const MIDI_REALTIME = [0xF8, 0xFA, 0xFB, 0xFC, 0xFE, 0xFF];
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1735754044.266823
hvcc-0.13.3/hvcc/generators/c2js/template/index.html 0000644 0000000 0000000 00000024742 14735300474 017234 0 ustar 00
{{name}}
{% for path in includes -%}
{% endfor -%}
{{name}}
{%- if events | length %}
Input Events:
{%- for k, v in events %}
{%- endfor %}
{%- endif %}
{%- if events_out | length %}
Output Events:
{%- for k, v in events_out %}
{{k}}: 0
{%- endfor %}