hvcc-0.16.0/LICENSE 0000644 0000000 0000000 00000076405 00000000000 010431 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
hvcc-0.16.0/README.md 0000644 0000000 0000000 00000020017 00000000000 010667 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/getting-started/index.md)
- [What is heavy?](docs/getting-started/index.md#what-is-heavy)
- [Supported patch formats](docs/getting-started/index.md#supported-patch-formats)
- [Supported platforms](docs/getting-started/index.md#supported-platforms)
- [Supported frameworks](docs/getting-started/index.md#supported-frameworks)
- [Licensing](docs/getting-started/index.md#licensing)
- [Getting Started](docs/getting-started/patching.md)
- [Generators](docs/generators/index.md)
- [MIDI](docs/reference/midi.md)
- [C API](docs/reference/c.md)
- [C++ API](docs/reference/cpp.md)
- [Heavy Lang Info](docs/reference/ir/heavy_lang.md)
- [Heavy IR Info](docs/reference/ir/heavy_ir.md)
- [Heavy GUI IR Info](docs/reference/ir/heavy_gui_ir.md)
- [Supported vanilla objects](docs/reference/objects/supported.md)
- [Unsupported vanilla objects](docs/reference/objects/unsupported.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.9 up to 3.14
- `jinja2` (for generator templating)
- `pydantic` (for data types)
- `pydantic-extra-types` (for data types)
- `arpeggio` (for `expr~` translation)
For tests:
- `tox` (python install)
- `numpy/scipy` (dev dependencies)
- `midifile` (git submodule)
- `tinywav` (git submodule)
- `clang/clang++` (system install)
## Installation
hvcc is available from pypi.org and can be installed using python3 pip:
```sh
pip3 install hvcc
```
If you want to develop hvcc you can install it from the source directory:
```sh
git clone https://github.com/Wasted-Audio/hvcc.git
cd hvcc/
pip3 install -e .
```
Also review our [Contribution Guide](CONTRIBUTING.md) before opening a pull request.
## 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/`
```sh
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:
```sh
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.
```sh
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:
```sh
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:
```sh
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/generators/index.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:
```sh
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
```sh
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
The Heavy community aims to be safe and inclusive, please read our [Code of Conduct](CODE_OF_CONDUCT.md) before engaging.
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
## Funding
This project is partially funded through [NGI0 Commons Fund](https://nlnet.nl/commonsfund), a fund established by [NLnet](https://nlnet.nl) with financial support from the European Commission's [Next Generation Internet](https://ngi.eu) program. Learn more at the [NLnet project page](https://nlnet.nl/project/HVCC).
[](https://nlnet.nl)
[](https://nlnet.nl/commonsfund)
hvcc-0.16.0/hvcc/__init__.py 0000644 0000000 0000000 00000000126 00000000000 012443 0 ustar 00 from hvcc.compiler import compile_dataflow # noqa
from hvcc.main import main # noqa
hvcc-0.16.0/hvcc/compiler.py 0000644 0000000 0000000 00000032341 00000000000 012522 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2021-2026 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 pathlib import Path
from hvcc.interpreters.pd2gui import pd2gui
from hvcc.interpreters.pd2hv import pd2hv
from hvcc.core.hv2ir import hv2ir
from hvcc.generators.ir2c import ir2c, 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.generators.c2fmod import c2fmod
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:
print(f"---> Module {module_name} not found")
return None
def compile_dataflow(
in_path: Path,
out_dir: Path,
patch_name: str = "heavy",
patch_meta_file: Optional[Path] = None,
search_paths: Optional[List[Path]] = None,
generators: Optional[List[str]] = None,
ext_generators: Optional[List[str]] = None,
verbose: bool = False,
copyright: Optional[str] = None,
nodsp: Optional[bool] = False,
gui: Optional[bool] = False
) -> CompilerResults:
results = CompilerResults(root={})
patch_meta = Meta()
# basic error checking on input
if in_path.is_file():
if not str(in_path).endswith((".pd")):
return add_error(results, "Can only process Pd files.")
elif in_path.is_dir():
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 is not None:
if patch_meta_file.is_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=Path(out_dir, "hv"),
search_paths=search_paths,
verbose=verbose)
# ensure that the ir filenames have no funky characters in it
subst_name = re.sub(r'\W', '_', patch_name)
if gui:
if verbose:
print("--> Generating GUI IR")
results.root["pd2gui"] = pd2gui.pd2gui.compile(
pd_path=in_path,
ir_file=Path(out_dir, "ir", f"{subst_name}.heavy.gui.json"),
search_paths=search_paths,
verbose=verbose)
# check for errors
response: CompilerResp = list(results.root.values())[0]
if response.notifs.has_error:
return results
results.root["hv2ir"] = hv2ir.hv2ir.compile(
hv_file=Path(response.out_dir, response.out_file),
# ensure that the ir filename has no funky characters in it
ir_file=Path(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 = Path(sys._MEIPASS, 'hvcc')
elif __file__:
application_path = Path(__file__).parent
c_src_dir = Path(out_dir, "c")
results.root["ir2c"] = ir2c.ir2c.compile(
hv_ir_path=Path(results.root["hv2ir"].out_dir, results.root["hv2ir"].out_file),
static_dir=Path(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 "fmod" in generators:
if verbose:
print("--> Generating Fmod plugin")
results.root["c2fmod"] = c2fmod.c2fmod.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
hvcc-0.16.0/hvcc/core/__init__.py 0000644 0000000 0000000 00000000000 00000000000 013362 0 ustar 00 hvcc-0.16.0/hvcc/core/hv2ir/BufferPool.py 0000644 0000000 0000000 00000010230 00000000000 014726 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: tuple[str, int], 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__()
hvcc-0.16.0/hvcc/core/hv2ir/Connection.py 0000644 0000000 0000000 00000006213 00000000000 014770 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}]"
hvcc-0.16.0/hvcc/core/hv2ir/HIrConvolution.py 0000644 0000000 0000000 00000003272 00000000000 015615 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
hvcc-0.16.0/hvcc/core/hv2ir/HIrExpr.py 0000644 0000000 0000000 00000003245 00000000000 014214 0 ustar 00 # Copyright (C) 2022-2025 Daniel Billotte, 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 .HeavyException import HeavyException
from .HeavyGraph import HeavyGraph
class HIrExpr(HeavyIrObject):
""" This needs little logic as the expression is forwarded to ir2c
"""
def __init__(
self,
obj_type: str,
args: Optional[Dict] = None,
graph: Optional[HeavyGraph] = None,
annotations: Optional[Dict] = None
) -> None:
super().__init__(obj_type, args=args, graph=graph,
num_inlets=args["num_inlets"] if args is not None else 1,
num_outlets=1,
annotations=annotations)
def inlet_requires_signal(self, inlet_index: int = 0) -> bool:
""" Overriding HeavyIrObject to deal with variable number of inlets
"""
if inlet_index >= self.num_inlets:
raise HeavyException(f"inlet_index: {inlet_index} is greater than number of inlets")
return True
hvcc-0.16.0/hvcc/core/hv2ir/HIrInlet.py 0000644 0000000 0000000 00000004004 00000000000 014343 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
hvcc-0.16.0/hvcc/core/hv2ir/HIrLorenz.py 0000644 0000000 0000000 00000002267 00000000000 014552 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)
hvcc-0.16.0/hvcc/core/hv2ir/HIrNam.py 0000644 0000000 0000000 00000003367 00000000000 014016 0 ustar 00 # Copyright (C) 2026 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 pathlib import Path
from .HeavyIrObject import HeavyIrObject
from .HeavyGraph import HeavyGraph
from hvcc.generators.ir2c.SignalNamHeader import ModelNet, load_nam_file
class HIrNam(HeavyIrObject):
""" __nam~f
"""
def __init__(
self,
obj_type: str,
args: Optional[Dict] = None,
graph: Optional[HeavyGraph] = None,
annotations: Optional[Dict] = None
) -> None:
assert obj_type == "__nam~f"
# load the nam file to retrieve the model type
assert args is not None
model_net, _, _ = load_nam_file(Path(args["nam"]))
# overload the object type based on the model
if model_net == ModelNet.Nano:
obj_type = "__nam_nano~f"
elif model_net == ModelNet.Feather:
obj_type = "__nam_feather~f"
elif model_net == ModelNet.Lite:
obj_type = "__nam_lite~f"
elif model_net == ModelNet.Standard:
obj_type = "__nam_standard~f"
super().__init__(obj_type, args=args, graph=graph, annotations=annotations)
hvcc-0.16.0/hvcc/core/hv2ir/HIrOutlet.py 0000644 0000000 0000000 00000003007 00000000000 014546 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
hvcc-0.16.0/hvcc/core/hv2ir/HIrPack.py 0000644 0000000 0000000 00000002527 00000000000 014156 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)
hvcc-0.16.0/hvcc/core/hv2ir/HIrReceive.py 0000644 0000000 0000000 00000003245 00000000000 014660 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']}'")
hvcc-0.16.0/hvcc/core/hv2ir/HIrSend.py 0000644 0000000 0000000 00000004663 00000000000 014174 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 []
hvcc-0.16.0/hvcc/core/hv2ir/HIrSwitchcase.py 0000644 0000000 0000000 00000002733 00000000000 015374 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)
hvcc-0.16.0/hvcc/core/hv2ir/HIrTabhead.py 0000644 0000000 0000000 00000003313 00000000000 014622 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
hvcc-0.16.0/hvcc/core/hv2ir/HIrTabread.py 0000644 0000000 0000000 00000003423 00000000000 014636 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"].split(" ")[0],
["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
hvcc-0.16.0/hvcc/core/hv2ir/HIrTabwrite.py 0000644 0000000 0000000 00000003350 00000000000 015054 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"].split(" ")[0],
["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
hvcc-0.16.0/hvcc/core/hv2ir/HLangAdc.py 0000644 0000000 0000000 00000004322 00000000000 014271 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)
hvcc-0.16.0/hvcc/core/hv2ir/HLangBinop.py 0000644 0000000 0000000 00000025160 00000000000 014654 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
hvcc-0.16.0/hvcc/core/hv2ir/HLangBiquad.py 0000644 0000000 0000000 00000004007 00000000000 015007 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}")
hvcc-0.16.0/hvcc/core/hv2ir/HLangDac.py 0000644 0000000 0000000 00000004151 00000000000 014271 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)
hvcc-0.16.0/hvcc/core/hv2ir/HLangDelay.py 0000644 0000000 0000000 00000002763 00000000000 014647 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))
hvcc-0.16.0/hvcc/core/hv2ir/HLangIf.py 0000644 0000000 0000000 00000004121 00000000000 014135 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023-2026 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 .
# moved to HeavyParser.py because of circular dependency
# from pathlib import Path
# 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: 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(Path("./hvlib/if~f.hv.json"))
# elif self.has_inlet_connection_format("ii"):
# # TODO(mhroth): implement this
# x = HeavyParser.graph_from_file(Path("./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))
hvcc-0.16.0/hvcc/core/hv2ir/HLangLine.py 0000644 0000000 0000000 00000003307 00000000000 014473 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))
hvcc-0.16.0/hvcc/core/hv2ir/HLangMessage.py 0000644 0000000 0000000 00000002711 00000000000 015166 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))
hvcc-0.16.0/hvcc/core/hv2ir/HLangNoise.py 0000644 0000000 0000000 00000003307 00000000000 014661 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023-2026 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 .
# moved to HeavyParser.py because of circular dependency
# import random
# from pathlib import Path
# from .HeavyLangObject import HeavyLangObject
# from .HeavyParser import HeavyParser
# 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 = Path(Path(__file__).parent, "./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))
hvcc-0.16.0/hvcc/core/hv2ir/HLangPhasor.py 0000644 0000000 0000000 00000003070 00000000000 015035 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))
hvcc-0.16.0/hvcc/core/hv2ir/HLangPrint.py 0000644 0000000 0000000 00000002701 00000000000 014675 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))
hvcc-0.16.0/hvcc/core/hv2ir/HLangRandom.py 0000644 0000000 0000000 00000003033 00000000000 015020 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))
hvcc-0.16.0/hvcc/core/hv2ir/HLangReceive.py 0000644 0000000 0000000 00000004212 00000000000 015162 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
hvcc-0.16.0/hvcc/core/hv2ir/HLangSend.py 0000644 0000000 0000000 00000004701 00000000000 014474 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
hvcc-0.16.0/hvcc/core/hv2ir/HLangSequence.py 0000644 0000000 0000000 00000006361 00000000000 015357 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)
hvcc-0.16.0/hvcc/core/hv2ir/HLangSlice.py 0000644 0000000 0000000 00000002701 00000000000 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 .
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))
hvcc-0.16.0/hvcc/core/hv2ir/HLangSystem.py 0000644 0000000 0000000 00000002705 00000000000 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 .
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))
hvcc-0.16.0/hvcc/core/hv2ir/HLangTable.py 0000644 0000000 0000000 00000004167 00000000000 014640 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))
hvcc-0.16.0/hvcc/core/hv2ir/HLangUnop.py 0000644 0000000 0000000 00000007711 00000000000 014530 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
hvcc-0.16.0/hvcc/core/hv2ir/HLangVar.py 0000644 0000000 0000000 00000005557 00000000000 014345 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))
hvcc-0.16.0/hvcc/core/hv2ir/HLangVario.py 0000644 0000000 0000000 00000005054 00000000000 014665 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
hvcc-0.16.0/hvcc/core/hv2ir/HeavyException.py 0000644 0000000 0000000 00000002100 00000000000 015613 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()
hvcc-0.16.0/hvcc/core/hv2ir/HeavyGraph.py 0000644 0000000 0000000 00000125646 00000000000 014743 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023-2026 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 collections import Counter
from typing import Optional, Union, Dict, List, Set, Tuple
from pathlib import Path
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: Path = Path(),
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[Path]:
""" 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}({self.file.name}/{self.xname})"
else:
return f"__graph.{self.id}({self.file.name})"
#
# 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()]
hvcc-0.16.0/hvcc/core/hv2ir/HeavyIrObject.py 0000644 0000000 0000000 00000024614 00000000000 015374 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023-2026 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
from typing import Dict, List, Optional, TYPE_CHECKING
from pathlib import Path
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(Path(Path(__file__).parent, "../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: list[tuple[str, int]] = [("zero", 0)] * self.num_inlets
self.outlet_buffers: list[tuple[str, int]] = [("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: Connection = 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)
]
)]
hvcc-0.16.0/hvcc/core/hv2ir/HeavyLangObject.py 0000644 0000000 0000000 00000041306 00000000000 015700 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023-2026 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 random
import string
from struct import unpack, pack
from typing import Optional, Union, List, Dict, Any, TYPE_CHECKING
from pathlib import Path
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(Path(Path(__file__).parent, "../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 as exc:
raise HeavyException(f"Connection {c} connects to out-of-range let: {exc}")
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
hvcc-0.16.0/hvcc/core/hv2ir/HeavyParser.py 0000644 0000000 0000000 00000027170 00000000000 015127 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023-2026 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
from typing import Any, Dict, List, Optional, Set, Tuple
from pathlib import Path
from .HIrConvolution import HIrConvolution
from .HIrExpr import HIrExpr
from .HIrInlet import HIrInlet
from .HIrLorenz import HIrLorenz
from .HIrNam import HIrNam
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: Path,
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 = hv_file.expanduser().absolute()
# 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: Path,
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([hv_file.parent])
# 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(Path("./hvlib/if~f.hv.json"))
elif self.has_inlet_connection_format("ii"):
# TODO(mhroth): implement this
x = HeavyParser.graph_from_file(Path("./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 = Path(Path(__file__).parent, "./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,
"__nam~f": HIrNam,
"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,
"__expr": HIrExpr,
"__expr~": HIrExpr,
}
hvcc-0.16.0/hvcc/core/hv2ir/LocalVars.py 0000644 0000000 0000000 00000007602 00000000000 014562 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023-2026 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 pathlib import Path
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[Path]:
# 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 = Path(d, file_name)
if file_path.exists():
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
hvcc-0.16.0/hvcc/core/hv2ir/__init__.py 0000644 0000000 0000000 00000000000 00000000000 014414 0 ustar 00 hvcc-0.16.0/hvcc/core/hv2ir/hv2ir.py 0000644 0000000 0000000 00000012075 00000000000 013726 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023-2026 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 pathlib import Path
from hvcc.core.hv2ir.HeavyException import HeavyException
from hvcc.core.hv2ir.HeavyParser import HeavyParser
from hvcc.types.compiler import CompilerResp, CompilerNotif, CompilerMsg
class hv2ir:
@classmethod
def compile(
cls,
hv_file: Path,
ir_file: Path,
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 = hv_file.expanduser().absolute()
ir_file = ir_file.expanduser().absolute()
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=hv_file,
in_dir=hv_file.parent,
out_file=ir_file,
out_dir=ir_file.parent
)
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 ir_file.parent.exists():
os.makedirs(ir_file.parent)
# 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=hv_file,
in_dir=hv_file.parent,
out_file=ir_file,
out_dir=ir_file.parent,
obj_counter=hv_counter
)
if ir is not None:
# write the hv.ir file
with open(ir_file, "w") as f:
json.dump(ir.model_dump(), f, indent=4)
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=hv_file,
in_dir=hv_file.parent,
out_file=ir_file,
out_dir=ir_file.parent,
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()
hvcc-0.16.0/hvcc/core/json/heavy.ir.json 0000644 0000000 0000000 00000151606 00000000000 014665 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": [
]
},
"__exc_or~f": {
"inlets": [
"~f>",
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
],
"args": [],
"perf": {}
},
"__expr": {
"inlets": [],
"ir": {
"control": true,
"signal": false,
"init": true
},
"outlets": [
"-->"
],
"args": [{
"name": "expressions",
"value_type": "stringarray",
"description": "The expression to evaluate",
"default": "",
"required": true
},
{
"name": "num_inlets",
"value_type": "int",
"description": "Number of inlets for this instance",
"default": "",
"required": true
}]
},
"__expr~": {
"inlets": [],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
],
"args": [{
"name": "expressions",
"value_type": "stringarray",
"description": "The expression to evaluate",
"default": "",
"required": true
},
{
"name": "num_inlets",
"value_type": "int",
"description": "Number of inlets for this instance",
"default": "",
"required": true
}]
},
"__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
}]
},
"__nam~f": {
"inlets": [
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": true
},
"outlets": [
"~f>"
],
"args": [{
"name": "nam",
"value_type": "string",
"description": "path to NAM file",
"default": "",
"required": true
}],
"perf": {}
},
"__nam_nano~f": {
"inlets": [
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": true
},
"outlets": [
"~f>"
],
"args": [{
"name": "nam",
"value_type": "string",
"description": "path to NAM file",
"default": "",
"required": true
}],
"perf": {}
},
"__nam_feather~f": {
"inlets": [
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": true
},
"outlets": [
"~f>"
],
"args": [{
"name": "nam",
"value_type": "string",
"description": "path to NAM file",
"default": "",
"required": true
}],
"perf": {}
},
"__nam_lite~f": {
"inlets": [
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": true
},
"outlets": [
"~f>"
],
"args": [{
"name": "nam",
"value_type": "string",
"description": "path to NAM file",
"default": "",
"required": true
}],
"perf": {}
},
"__nam_standard~f": {
"inlets": [
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": true
},
"outlets": [
"~f>"
],
"args": [{
"name": "nam",
"value_type": "string",
"description": "path to NAM file",
"default": "",
"required": true
}],
"perf": {}
},
"__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": "mixedarray",
"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~f": {
"inlets": [
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
],
"args": [
]
},
"__asin~f": {
"inlets": [
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
]
},
"__atanh~f": {
"inlets": [
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
]
},
"__atan~f": {
"inlets": [
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
]
},
"__bit_and~f": {
"inlets": [
"~f>",
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
]
},
"__bit_or~f": {
"inlets": [
"~f>",
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
]
},
"__bit_not~f": {
"inlets": [
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
]
},
"__bitsafe~f": {
"inlets": [
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
]
},
"__exp~f": {
"inlets": [
"~f>"
],
"args": [],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
],
"perf": {
"avx": 0,
"sse": 0,
"neon": 0
}
},
"__sinh~f": {
"inlets": [
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
],
"args": [
]
},
"__tanh~f": {
"inlets": [
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
]
},
"__tan~f": {
"inlets": [
"~f>"
],
"ir": {
"control": false,
"signal": true,
"init": false
},
"outlets": [
"~f>"
]
}
}
hvcc-0.16.0/hvcc/core/json/heavy.lang.json 0000644 0000000 0000000 00000123611 00000000000 015167 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": [
"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": 0,
"required": false
}
],
"alias": [
"add"
],
"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": 1,
"required": false
}
],
"alias": [
"divide"
],
"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"
]
}
}
hvcc-0.16.0/hvcc/generators/__init__.py 0000644 0000000 0000000 00000000000 00000000000 014603 0 ustar 00 hvcc-0.16.0/hvcc/generators/c2daisy/__init__.py 0000644 0000000 0000000 00000000000 00000000000 016141 0 ustar 00 hvcc-0.16.0/hvcc/generators/c2daisy/c2daisy.py 0000644 0000000 0000000 00000017515 00000000000 015763 0 ustar 00 # Copyright (C) 2021-2026 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 jinja2
import shutil
import time
from typing import Any, Dict, Optional
from pathlib import Path
from ..copyright import copyright_manager
from .parameters import parse_parameters, display_parameters, display_processor
from .json2daisy import generate_header_from_file, generate_header_from_name
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: Path,
out_dir: Path,
externs: ExternInfo,
patch_name: str,
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()
warnings = []
out_dir = Path(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 = out_dir.absolute()
if out_dir.exists():
shutil.rmtree(out_dir)
# copy over static files
shutil.copytree(Path(Path(__file__).parent, "static"), out_dir)
# copy over generated C source files
source_dir = Path(out_dir, "source")
shutil.copytree(c_src_dir, source_dir)
if daisy_meta.board_file is not None:
header, board_info = generate_header_from_file(Path(daisy_meta.board_file))
display_params = display_parameters(daisy_meta.board_file)
else:
header, board_info = generate_header_from_name(board)
display_params = {}
warnings.append(
CompilerMsg(
enum=NotificationEnum.WARNING_GENERIC,
message=f"Unable to load board description from {daisy_meta.board_file}. Using fallback."
)
)
# inject display process code
try:
display_process = display_processor(daisy_meta.board_file)
except (FileNotFoundError, KeyError, ValueError):
display_process = board_info['displayprocess']
warnings.append(
CompilerMsg(
enum=NotificationEnum.WARNING_GENERIC,
message=f"Unable to load display code from {daisy_meta.board_file}. Using fallback."
)
)
# 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 + list(display_params.keys())) for y in t)
]
component_glue = 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['debug_printing'] = daisy_meta.debug_printing
component_glue['usb_midi'] = daisy_meta.usb_midi
component_glue['pool_sizes_kb'] = externs.memoryPoolSizesKb
component_glue['display_params'] = display_params
component_glue['display_process'] = display_process
# 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 = Path(source_dir, f"HeavyDaisy_{patch_name}.hpp")
with open(daisy_h_path, "w") as f:
f.write(header)
loader = jinja2.FileSystemLoader(Path(Path(__file__).parent, 'templates'))
env = jinja2.Environment(loader=loader, trim_blocks=True, lstrip_blocks=True)
daisy_cpp_path = Path(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(Path(source_dir, "Makefile"), "w") as f:
f.write(rendered_makefile)
# ======================================================================================
return CompilerResp(
stage="c2daisy",
notifs=CompilerNotif(
warnings=warnings
),
in_dir=c_src_dir,
out_dir=out_dir,
out_file=daisy_h_path,
compile_time=time.time() - tick
)
except Exception as e:
return CompilerResp(
stage="c2daisy",
notifs=CompilerNotif(
has_error=True,
exception=e,
warnings=warnings,
errors=[CompilerMsg(
enum=NotificationEnum.ERROR_EXCEPTION,
message=str(e)
)]
),
in_dir=c_src_dir,
out_dir=out_dir,
compile_time=time.time() - tick
)
hvcc-0.16.0/hvcc/generators/c2daisy/json2daisy/LICENSE.md 0000644 0000000 0000000 00000002122 00000000000 017530 0 ustar 00 MIT License
Copyright (c) 2021 Electrosmith
Copyright (c) 2025-2026 Wasted Audio
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
hvcc-0.16.0/hvcc/generators/c2daisy/json2daisy/__init__.py 0000644 0000000 0000000 00000000042 00000000000 020234 0 ustar 00 from .json2daisy import * # noqa
hvcc-0.16.0/hvcc/generators/c2daisy/json2daisy/json2daisy.py 0000644 0000000 0000000 00000032336 00000000000 020575 0 ustar 00 import jinja2
import json
from importlib import resources
from typing import Optional
from pathlib import Path
def map_load(pair: list, json_defs_file: Path):
"""
Helper for loading and processing the definitions, component list, etc
"""
# load the default components
comp_string = Path(Path(__file__).parent, json_defs_file)
component_defs = json.loads(comp_string.read_bytes())
pair[1]['name'] = pair[0]
# the default if it exists
component = component_defs.get(pair[1]['component'], None)
if (component):
# copy component defs into the def
# TODO this should be recursive for object structures..
for k in component:
if k not in pair[1]:
pair[1][k] = component[k]
else:
raise Exception(f'unknown component "{pair[1]["component"]}"')
return pair[1]
def filter_match(
component_set: list,
key: str,
match: str,
key_exclude: Optional[str] = None,
match_exclude: bool = False
) -> filter:
if (key_exclude is not None and match_exclude):
return filter(lambda x: x.get(key, '') == match and x.get(key_exclude, '') != match_exclude, component_set)
else:
return filter(lambda x: x.get(key, '') == match, component_set)
def filter_matches(
component_set: list,
key: str,
matches: list,
key_exclude: Optional[str] = None,
match_exclude: bool = False
) -> filter:
if (key_exclude is not None and match_exclude):
return filter(lambda x: x.get(key, '') in matches and x.get(key_exclude, '') != match_exclude, component_set)
else:
return filter(lambda x: x.get(key, '') in matches, component_set)
def filter_has(
component_set: list,
key: str,
key_exclude: Optional[str] = None,
match_exclude: bool = False
) -> filter:
if (key_exclude is not None and match_exclude):
return filter(lambda x: x.get(key, '') != '' and x.get(key_exclude, '') != match_exclude, component_set)
else:
return filter(lambda x: x.get(key, '') != '', component_set)
# filter out the components we need, then map them onto the init for that part
def filter_map_init(
component_set: list,
key: str,
match: str,
key_exclude: Optional[str] = None,
match_exclude: bool = False
) -> str:
filtered = filter_match(component_set, key, match, key_exclude=key_exclude, match_exclude=match_exclude)
return "\n ".join(map(lambda x: x['map_init'].format_map(x), filtered))
# this function is not used anywhere?
def filter_map_set(
component_set: list,
key: str,
match: str,
key_exclude: Optional[str] = None,
match_exclude: bool = False
) -> str:
filtered = filter_match(component_set, key, match, key_exclude=key_exclude, match_exclude=match_exclude)
return "\n ".join(
map(lambda x: x['mapping'][0]['set'].format_map(x['mapping'][0]['name'].format_map(x)), filtered)
)
def filter_map_ctrl(
component_set: list,
key: str,
matches: list,
init_key: str,
key_exclude: Optional[str] = None,
match_exclude: bool = False
) -> str:
component_filt = filter_matches(component_set, key, matches, key_exclude=key_exclude, match_exclude=match_exclude)
component_map = map(lambda x, i: {**x, 'i': i}, component_filt, range(1000))
return "\n ".join(map(lambda x: x[init_key].format_map(x), component_map))
# filter out the components with a certain field, then fill in the template
def filter_map_template(
component_set: list,
name: str,
key_exclude: Optional[str] = None,
match_exclude: bool = False
) -> str:
filtered = filter_has(component_set, name, key_exclude=key_exclude, match_exclude=match_exclude)
return "\n ".join(map(lambda x: x[name].format_map(x), filtered))
def flatten_pin_dicts(comp: dict) -> dict:
newcomp = {}
for key, val in comp.items():
if (isinstance(val, dict) and key == 'pin'):
for subkey, subval in val.items():
newcomp[f'{key}_{subkey}'] = subval
else:
newcomp[key] = val
return newcomp
def flatten_index_dicts(comp: dict) -> dict:
newcomp = {}
for key, val in comp.items():
if (isinstance(val, dict) and key == 'index'):
for subkey, subval in val.items():
newcomp[f'{key}_{subkey}'] = subval
else:
newcomp[key] = val
return newcomp
def generate_header(board_description_dict: dict) -> 'tuple[str, dict]':
"""
Generate a C++ Daisy board header from a dictionary board description.
Returns a tuple containing the board
header as a string and an information dictionary.
The dictionary provides sufficient information to
generate interface code, including component getters
and setters, audio channel count, etc.
"""
target = board_description_dict
# flesh out target components:
components = target.get('components', {})
parents = target.get('parents', {})
for key in parents:
parents[key]['is_parent'] = True
components.update(parents)
seed_defs = Path("resources", 'component_defs.json')
patchsm_defs = Path("resources", 'component_defs_patchsm.json')
petalsm_defs = Path("resources", 'component_defs_petalsm.json')
definitions = {'seed': seed_defs, 'patch_sm': patchsm_defs, 'petal_125b_sm': petalsm_defs}
som = target.get('som', 'seed')
try:
json_defs_file = definitions[som]
except KeyError:
raise NameError(f'Unkown som "{som}"')
# alphabetize by component name
components = sorted(components.items(), key=lambda x: x[1]['component'])
components = list(map(lambda p: map_load(p, json_defs_file), components))
# flatten pin dicts into multiple entries
# e.g. "pin": {"a": 12} => "pin_a": 12
components = [flatten_pin_dicts(comp) for comp in components]
components = [flatten_index_dicts(comp) for comp in components]
target['components'] = components
if 'name' not in target:
target['name'] = 'custom'
if 'aliases' not in target:
target['aliases'] = {}
if 'display' in target:
# apply defaults if not present in config
target['display']['driver'] = target['display'].get('driver', "daisy::SSD130x4WireSpi128x64Driver")
target['display']['config'] = target['display'].get('config', [])
target['display']['dim'] = target['display'].get('dim', [128, 64])
target['displayprocess'] = target['display'].get('process', '')
replacements = {}
replacements['name'] = target['name']
replacements['som'] = som
replacements['external_codecs'] = target.get('external_codecs', [])
som_classes = {
'seed': 'daisy::DaisySeed',
'patch_sm': 'daisy::patch_sm::DaisyPatchSM',
'petal_125b_sm': 'daisy::Petal125BSM',
}
replacements['som_class'] = som_classes.get(som, som_classes['seed'])
replacements['display_conditional'] = ('#include "dev/oled_ssd130x.h"' if ('display' in target) else "")
replacements['target_name'] = target['name']
replacements['init'] = filter_map_template(components, 'init', key_exclude='default', match_exclude=True)
replacements['analogcount'] = len(list(filter_matches(
components, 'component', ['AnalogControl', 'AnalogControlBipolar', 'CD4051'],
key_exclude='default', match_exclude=True)))
replacements['init_single'] = filter_map_ctrl(
components, 'component', ['AnalogControl', 'AnalogControlBipolar', 'CD4051'],
'init_single', key_exclude='default', match_exclude=True)
replacements['ctrl_init'] = filter_map_ctrl(
components, 'component', ['AnalogControl', 'AnalogControlBipolar'],
'map_init', key_exclude='default', match_exclude=True)
comp_string = Path(Path(__file__).parent, json_defs_file)
definitions_dict = json.loads(comp_string.read_bytes())
for name in definitions_dict:
if name not in ('AnalogControl', 'AnalogControlBipolar', 'CD4051'):
replacements[name] = filter_map_init(
components, 'component', name, key_exclude='default', match_exclude=True)
if 'display' in target:
replacements['dispdec'] = f'daisy::OledDisplay<{target["display"]["driver"]}> display;'
replacements['display'] = f"""
daisy::OledDisplay<{target['display']['driver']}>::Config display_config;
display_config.driver_config.transport_config.Defaults();
{"".join(map(lambda x: x, target['display'].get('config', {})))}
display.Init(display_config);
display.Fill(0);
display.Update();
"""
else:
replacements['display'] = ''
if 'defines' in target:
# deprecate old oopsy syntax at some point
if target['defines'].get('HAS_MIDI') or target['defines'].get('OOPSY_TARGET_HAS_MIDI_INPUT'):
target['has_midi'] = True
replacements['midi'] = """daisy::MidiUartHandler midi;"""
replacements['process'] = filter_map_template(
components, 'process', key_exclude='default', match_exclude=True)
replacements['loopprocess'] = filter_map_template(
components, 'loopprocess', key_exclude='default', match_exclude=True)
replacements['postprocess'] = filter_map_template(
components, 'postprocess', key_exclude='default', match_exclude=True)
replacements['displayprocess'] = filter_map_template(
components, 'display', key_exclude='default', match_exclude=True)
replacements['hidupdaterates'] = filter_map_template(
components, 'updaterate', key_exclude='default', match_exclude=True)
license_string = resources.files(__package__).joinpath('resources/LICENSE').read_text()
replacements['license'] = '/*\n * ' + '\n * '.join([line for line in license_string.split('\n')]) + '\n */'
component_declarations = list(filter(lambda x: not x.get('default', False), components))
component_declarations = list(filter(lambda x: x.get('typename', '') != '', component_declarations))
if len(component_declarations) > 0:
replacements['comps'] = ";\n ".join(
map(lambda x: x['typename'].format_map(x) + ' ' + x['name'], component_declarations)
) + ';'
non_class_declarations = list(filter(lambda x: 'non_class_decl' in x, component_declarations))
if len(non_class_declarations) > 0:
replacements['non_class_declarations'] = "\n".join(
map(lambda x: x['non_class_decl'].format_map(x), non_class_declarations)
)
# env_opts = {"trim_blocks": True, "lstrip_blocks": True}
# Ideally, this would be what we use, but we'll need to get the jinja PackageLoader class working
# loader = jinja2.PackageLoader(__name__)
# env = jinja2.Environment(loader=loader, **env_opts)
# rendered_header = env.get_template('daisy.h').render(replacements)
# This following works, but is really annoying
header_str = Path(Path(__file__).parent, Path('templates', 'daisy.h')).read_text()
header_env = jinja2.Environment(
loader=jinja2.BaseLoader(),
trim_blocks=True,
lstrip_blocks=True
).from_string(header_str)
rendered_header = header_env.render(replacements)
rendered_header_lines = rendered_header.splitlines()
rendered_header = "\n".join(line.rstrip() for line in rendered_header_lines) + "\n"
# removing all unnecessary fields
for comp in components:
if 'map_init' in comp:
del comp['map_init']
if 'typename' in comp:
del comp['typename']
audio_info = target.get('audio', None)
audio_channels = audio_info.get('channels', 2) if audio_info is not None else 2
# This dictionary contains the necessary information to automatically (or manually)
# write code to interface with the generated board
board_info = {
'name': target['name'],
'components': components,
'aliases': target['aliases'],
'channels': audio_channels,
'has_midi': target.get('has_midi', False),
'displayprocess': target.get('displayprocess', '')
}
return rendered_header, board_info
def generate_header_from_file(description_file: Path) -> 'tuple[str, dict]':
"""
Generate a C++ Daisy board header from a JSON description file.
Returns a tuple containing the board
header as a string and an information dictionary.
The dictionary provides sufficient information to
generate interface code, including component getters
and setters, audio channel count, etc.
"""
with open(description_file, 'rb') as file:
daisy_description = json.load(file)
return generate_header(daisy_description)
def generate_header_from_name(board_name: str) -> 'tuple[str, dict]':
"""
Generate a C++ Daisy board header for an existing daisy board.
Returns a tuple containing the board
header as a string and an information dictionary.
The dictionary provides sufficient information to
generate interface code, including component getters
and setters, audio channel count, etc.
"""
try:
description_file = Path('resources', f'{board_name}.json')
daisy_description = Path(Path(__file__).parent, description_file)
daisy_description_dict = json.loads(daisy_description.read_bytes())
except FileNotFoundError:
raise FileNotFoundError(f'Unknown Daisy board "{board_name}"')
return generate_header(daisy_description_dict)
hvcc-0.16.0/hvcc/generators/c2daisy/json2daisy/resources/LICENSE 0000644 0000000 0000000 00000002055 00000000000 021150 0 ustar 00 MIT License
Copyright (c) 2021 Electrosmith
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
hvcc-0.16.0/hvcc/generators/c2daisy/json2daisy/resources/component_defs.json 0000644 0000000 0000000 00000070224 00000000000 024044 0 ustar 00 {
"UserLed": {
"map_init": "",
"typename": "",
"direction": "out",
"mapping": [
{
"name": "{name}",
"set": "{class_name}.som.SetLed({value});",
"where": "hook"
}
]
},
"Switch": {
"map_init": "{name}.Init(som.GetPin({pin}), som.AudioCallbackRate(), {type}, {polarity});",
"typename": "daisy::Switch",
"direction": "in",
"pin": "a",
"type": "daisy::Switch::TYPE_MOMENTARY",
"polarity": "daisy::Switch::POLARITY_INVERTED",
"process": "{name}.Debounce();",
"updaterate": "{name}.SetUpdateRate(som.AudioCallbackRate());",
"mapping": [
{
"name": "{name}_press",
"get": "({class_name}.{name}.Pressed()?1.f:0.f)",
"range": [
0,
1
],
"bool": false
},
{
"name": "{name}",
"get": "({class_name}.{name}.RisingEdge()?1.f:0.f)",
"range": [
0,
1
],
"bool": true
},
{
"name": "{name}_fall",
"get": "({class_name}.{name}.FallingEdge()?1.f:0.f)",
"range": [
0,
1
],
"bool": true
},
{
"name": "{name}_seconds",
"get": "({class_name}.{name}.TimeHeldMs()*0.001f)",
"range": null,
"bool": false
}
]
},
"Switch3": {
"map_init": "{name}.Init(som.GetPin({pin_a}), som.GetPin({pin_b}));",
"typename": "daisy::Switch3",
"direction": "in",
"pin": "a,b",
"mapping": [
{
"name": "{name}",
"get": "({class_name}.{name}.Read()*0.5f+0.5f)",
"range": [
0,
2
],
"bool": false
}
]
},
"Encoder": {
"map_init": "{name}.Init(som.GetPin({pin_a}), som.GetPin({pin_b}), som.GetPin({pin_click}), som.AudioCallbackRate());",
"typename": "daisy::Encoder",
"direction": "in",
"pin": "a,b,click",
"process": "{name}.Debounce();",
"updaterate": "{name}.SetUpdateRate(som.AudioCallbackRate());",
"mapping": [
{
"name": "{name}",
"get": "({class_name}.{name}.Increment())",
"range": [
-1,
1
],
"bool": false
},
{
"name": "{name}_press",
"get": "({class_name}.{name}.Pressed()?1.f:0.f)",
"range": [
0,
1
],
"bool": false
},
{
"name": "{name}_rise",
"get": "({class_name}.{name}.RisingEdge()?1.f:0.f)",
"range": [
0,
1
],
"bool": true
},
{
"name": "{name}_fall",
"get": "({class_name}.{name}.FallingEdge()?1.f:0.f)",
"range": [
0,
1
],
"bool": true
},
{
"name": "{name}_seconds",
"get": "({class_name}.{name}.TimeHeldMs()*0.001f)",
"range": null,
"bool": false
}
]
},
"GateIn": {
"map_init": "{name}.Init(som.GetPin({pin}), {invert});",
"typename": "daisy::GateIn",
"direction": "in",
"pin": "a",
"invert": "true",
"mapping": [
{
"name": "{name}",
"get": "({class_name}.{name}.State()?1.f:0.f)",
"range": [
0,
1
],
"bool": false
},
{
"name": "{name}_trig",
"get": "({class_name}.{name}.Trig()?1.f:0.f)",
"range": [
0,
1
],
"bool": true
}
]
},
"AnalogControl": {
"init_single": "cfg[{i}].InitSingle(som.GetPin({pin}));",
"map_init": "{name}.Init(som.adc.GetPtr({i}), som.AudioCallbackRate(), {flip}, {invert});",
"typename": "daisy::AnalogControl",
"direction": "in",
"pin": "a",
"flip": "false",
"invert": "false",
"slew": "1.0/som.AudioCallbackRate()",
"process": "{name}.Process();",
"updaterate": "{name}.SetSampleRate(som.AudioCallbackRate());",
"mapping": [
{
"name": "{name}",
"get": "({class_name}.{name}.Value())",
"range": [
0,
1
],
"bool": false
}
]
},
"Led": {
"map_init": "{name}.Init(som.GetPin({pin}), {invert});\n {name}.Set(0.0f);",
"typename": "daisy::Led",
"direction": "out",
"pin": "a",
"invert": "true",
"postprocess": "{name}.Update();",
"mapping": [
{
"name": "{name}",
"set": "{class_name}.{name}.Set({value});"
}
]
},
"RgbLed": {
"map_init": "{name}.Init(som.GetPin({pin_r}), som.GetPin({pin_g}), som.GetPin({pin_b}), {invert});\n {name}.Set(0.0f, 0.0f, 0.0f);",
"typename": "daisy::RgbLed",
"direction": "out",
"pin": "r,g,b",
"invert": "true",
"postprocess": "{name}.Update();",
"mapping": [
{
"name": "{name}_red",
"set": "{class_name}.{name}.SetRed({value});"
},
{
"name": "{name}_green",
"set": "{class_name}.{name}.SetGreen({value});"
},
{
"name": "{name}_blue",
"set": "{class_name}.{name}.SetBlue({value});"
},
{
"name": "{name}",
"set": "{class_name}.{name}.Set({value},{value},{value});"
},
{
"name": "{name}_white",
"set": "{class_name}.{name}.Set({value},{value},{value});"
}
]
},
"GateOut": {
"map_init": "{name}.Init(som.GetPin({pin}), {mode}, {pull});",
"typename": "daisy::GPIO",
"direction": "out",
"pin": "a",
"mode": "daisy::GPIO::Mode::OUTPUT",
"pull": "daisy::GPIO::Pull::NOPULL",
"mapping": [
{
"name": "{name}",
"set": "{class_name}.{name}.Write({value});",
"where": "hook"
}
]
},
"CVOuts": {
"map_init": "{name}.bitdepth = {bitdepth};\n {name}.buff_state = {buff_state};\n {name}.mode = {mode};\n {name}.chn = {channel};\n som.dac.Init({name});\n som.dac.WriteValue({channel}, 0);",
"typename": "daisy::DacHandle::Config",
"direction": "out",
"pin": "",
"bitdepth": "daisy::DacHandle::BitDepth::BITS_12",
"buff_state": "daisy::DacHandle::BufferState::ENABLED",
"mode": "daisy::DacHandle::Mode::POLLING",
"channel": "daisy::DacHandle::Channel::BOTH",
"mapping": [
{
"name": "{name}1",
"set": "{class_name}.som.dac.WriteValue(daisy::DacHandle::Channel::ONE, {value} * 4095);",
"where": "main"
},
{
"name": "{name}2",
"set": "{class_name}.som.dac.WriteValue(daisy::DacHandle::Channel::TWO, {value} * 4095);",
"where": "main"
}
]
},
"i2c": {
"map_init": "{name}.Init({{{peripheral}, {{som.GetPin({pin_scl}), som.GetPin({pin_sda})}}, {speed}, {mode}}});",
"typename": "daisy::I2CHandle",
"pin": "scl,sda",
"peripheral": "daisy::I2CHandle::Config::Peripheral::I2C_1",
"speed": "daisy::I2CHandle::Config::Speed::I2C_1MHZ",
"mode": "daisy::I2CHandle::Config::Mode::I2C_MASTER",
"mapping": []
},
"PCA9685": {
"map_init": "{name}.Init({parent}, {address}, {name}_dma_buffer_a, {name}_dma_buffer_b);",
"typename": "daisy::LedDriverPca9685<{driver_count}, true>",
"non_class_decl": "daisy::LedDriverPca9685<{driver_count}, true>::DmaBuffer DMA_BUFFER_MEM_SECTION {name}_dma_buffer_a, {name}_dma_buffer_b;",
"driver_count": 1,
"address": "{0x00}",
"parent": "",
"pin": "",
"loopprocess": "{name}.SwapBuffersAndTransmit();",
"mapping": []
},
"PCA9685Led": {
"map_init": "",
"pin": "",
"typename": "",
"parent": "",
"direction": "out",
"index": 0,
"mapping": [
{
"name": "{name}",
"set": "{class_name}.{parent}.SetLed({index}, {value});"
}
]
},
"PCA9685RgbLed": {
"map_init": "",
"typename": "",
"direction": "out",
"parent": "",
"pin": "",
"index": {
"red": 0,
"green": 1,
"blue": 2
},
"mapping": [
{
"name": "{name}_red",
"set": "{class_name}.{parent}.SetLed({index_red}, {value});"
},
{
"name": "{name}_green",
"set": "{class_name}.{parent}.SetLed({index_green}, {value});"
},
{
"name": "{name}_blue",
"set": "{class_name}.{parent}.SetLed({index_blue}, {value});"
},
{
"name": "{name}",
"set": "{class_name}.{parent}.SetLed({index_red}, {value});\n {class_name}.{parent}.SetLed({index_green}, {value});\n {class_name}.{parent}.SetLed({index_blue}, {value});"
},
{
"name": "{name}_white",
"set": "{class_name}.{parent}.SetLed({index_red}, {value});\n {class_name}.{parent}.SetLed({index_green}, {value});\n {class_name}.{parent}.SetLed({index_blue}, {value});"
}
]
},
"CD4021": {
"map_init": "{name}.Init({{ som.GetPin({pin_clk}), som.GetPin({pin_cs}), {{ som.GetPin({pin_data}) }} }});",
"typename": "daisy::ShiftRegister4021<{driver_count}>",
"non_class_decl": "uint8_t {name}_debounced[8*{driver_count}];",
"driver_count": 1,
"pin": "clk,cs,data",
"postprocess": "{name}.Update();",
"mapping": []
},
"CD4021Switch": {
"map_init": "",
"typename": "",
"direction": "in",
"parent": "",
"index": 0,
"postprocess": "{parent}_debounced[{index}] = {parent}.State({index}) | ({parent}_debounced[{index}] << 1);",
"mapping": [
{
"name": "{name}",
"get": "(json2daisy::{parent}_debounced[{index}] == 0xFE)",
"bool": true
},
{
"name": "{name}_press",
"get": "(json2daisy::{parent}_debounced[{index}] != 0xFF)",
"bool": false
},
{
"name": "{name}_fall",
"get": "(json2daisy::{parent}_debounced[{index}] == 0x7F)",
"bool": true
}
]
},
"CD4051": {
"init_single": "size_t {name}_index = {i};\n cfg[{name}_index].InitMux(som.GetPin({pin_adc}), {mux_count}, som.GetPin({pin_sel0}), som.GetPin({pin_sel1}), som.GetPin({pin_sel2}));",
"typename": "",
"mux_count": 1,
"pin": "adc,sel0,sel1,sel2",
"mapping": []
},
"CD4051AnalogControl": {
"map_init": "{name}.Init(som.adc.GetMuxPtr({parent}_index, {index}), som.AudioCallbackRate(), {flip}, {invert});",
"parent": "",
"index": 0,
"typename": "daisy::AnalogControl",
"direction": "in",
"flip": "false",
"invert": "false",
"slew": "1.0/som.AudioCallbackRate()",
"process": "{name}.Process();",
"updaterate": "{name}.SetSampleRate(som.AudioCallbackRate());",
"mapping": [
{
"name": "{name}",
"get": "({class_name}.{name}.Value())",
"range": [
0,
1
],
"bool": false
}
]
},
"AnalogControlBipolar": {
"init_single": "cfg[{i}].InitSingle(som.GetPin({pin}));",
"map_init": "{name}.InitBipolarCv(som.adc.GetPtr({i}), som.AudioCallbackRate());",
"typename": "daisy::AnalogControl",
"direction": "in",
"pin": "a",
"slew": "1.0/som.AudioCallbackRate()",
"process": "{name}.Process();",
"updaterate": "{name}.SetSampleRate(som.AudioCallbackRate());",
"mapping": [
{
"name": "{name}",
"get": "({class_name}.{name}.Value())",
"range": [
0,
1
],
"bool": false
}
]
},
"Tlv493d": {
"map_init": "daisy::Tlv493dI2CTransport::Config {name}_config;\n {name}_config.address = {address};\n {name}_config.periph = {periph};\n {name}_config.speed = {speed};\n {name}_config.scl = som.GetPin({pin_scl});\n {name}_config.sda = som.GetPin({pin_sda});\n daisy::Tlv493dI2C::Config {name}_main_conf;\n {name}_main_conf.transport_config = {name}_config;\n {name}.Init({name}_main_conf);",
"typename": "daisy::Tlv493dI2C",
"direction": "in",
"pin": "scl,sda",
"address": "TLV493D_ADDRESS1",
"periph": "daisy::I2CHandle::Config::Peripheral::I2C_1",
"speed": "daisy::I2CHandle::Config::Speed::I2C_400KHZ",
"loopprocess": "{name}.UpdateData();",
"mapping": [
{
"name": "{name}",
"get": "{class_name}.{name}.GetAmount()"
},
{
"name": "{name}_x",
"get": "{class_name}.{name}.GetX()"
},
{
"name": "{name}_y",
"get": "{class_name}.{name}.GetY()"
},
{
"name": "{name}_z",
"get": "{class_name}.{name}.GetZ()"
},
{
"name": "{name}_amount",
"get": "{class_name}.{name}.GetAmount()"
},
{
"name": "{name}_azimuth",
"get": "{class_name}.{name}.GetAzimuth()"
},
{
"name": "{name}_polar",
"get": "{class_name}.{name}.GetPolar()"
}
]
},
"Mpr121": {
"map_init": "daisy::Mpr121I2CTransport::Config {name}_config;\n {name}_config.periph = {periph};\n {name}_config.speed = {speed};\n {name}_config.dev_addr = {address};\n daisy::Mpr121I2C::Config {name}_main_conf;\n {name}_main_conf.transport_config = {name}_config;\n {name}_main_conf.touch_threshold = {touch_threshold};\n {name}_main_conf.release_threshold = {release_threshold};\n {name}.Init({name}_main_conf);",
"typename": "daisy::Mpr121I2C",
"direction": "in",
"pin": "scl,sda",
"address": "MPR121_I2CADDR_DEFAULT",
"periph": "daisy::I2CHandle::Config::Peripheral::I2C_1",
"speed": "daisy::I2CHandle::Config::Speed::I2C_400KHZ",
"touch_threshold": "MPR121_TOUCH_THRESHOLD_DEFAULT",
"release_threshold": "MPR121_RELEASE_THRESHOLD_DEFAULT",
"mapping": [
{
"name": "{name}",
"get": "(({class_name}.{name}.Touched() & 0x001) ? 1.0f : 0.f)",
"where": "main"
},
{
"name": "{name}_ch0",
"get": "(({class_name}.{name}.Touched() & 0x001) ? 1.0f : 0.f)",
"where": "main"
},
{
"name": "{name}_ch1",
"get": "(({class_name}.{name}.Touched() & 0x002) ? 1.0f : 0.f)",
"where": "main"
},
{
"name": "{name}_ch2",
"get": "(({class_name}.{name}.Touched() & 0x004) ? 1.0f : 0.f)",
"where": "main"
},
{
"name": "{name}_ch3",
"get": "(({class_name}.{name}.Touched() & 0x008) ? 1.0f : 0.f)",
"where": "main"
},
{
"name": "{name}_ch4",
"get": "(({class_name}.{name}.Touched() & 0x010) ? 1.0f : 0.f)",
"where": "main"
},
{
"name": "{name}_ch5",
"get": "(({class_name}.{name}.Touched() & 0x020) ? 1.0f : 0.f)",
"where": "main"
},
{
"name": "{name}_ch6",
"get": "(({class_name}.{name}.Touched() & 0x040) ? 1.0f : 0.f)",
"where": "main"
},
{
"name": "{name}_ch7",
"get": "(({class_name}.{name}.Touched() & 0x080) ? 1.0f : 0.f)",
"where": "main"
},
{
"name": "{name}_ch8",
"get": "(({class_name}.{name}.Touched() & 0x100) ? 1.0f : 0.f)",
"where": "main"
},
{
"name": "{name}_ch9",
"get": "(({class_name}.{name}.Touched() & 0x200) ? 1.0f : 0.f)",
"where": "main"
},
{
"name": "{name}_ch10",
"get": "(({class_name}.{name}.Touched() & 0x400) ? 1.0f : 0.f)",
"where": "main"
},
{
"name": "{name}_ch11",
"get": "(({class_name}.{name}.Touched() & 0x800) ? 1.0f : 0.f)",
"where": "main"
},
{
"name": "{name}_ch0_raw",
"get": "{class_name}.{name}.FilteredData(0)",
"where": "main",
"range": [
0,
1023
]
},
{
"name": "{name}_ch1_raw",
"get": "{class_name}.{name}.FilteredData(1)",
"where": "main",
"range": [
0,
1023
]
},
{
"name": "{name}_ch2_raw",
"get": "{class_name}.{name}.FilteredData(2)",
"where": "main",
"range": [
0,
1023
]
},
{
"name": "{name}_ch3_raw",
"get": "{class_name}.{name}.FilteredData(3)",
"where": "main",
"range": [
0,
1023
]
},
{
"name": "{name}_ch4_raw",
"get": "{class_name}.{name}.FilteredData(4)",
"where": "main",
"range": [
0,
1023
]
},
{
"name": "{name}_ch5_raw",
"get": "{class_name}.{name}.FilteredData(5)",
"where": "main",
"range": [
0,
1023
]
},
{
"name": "{name}_ch6_raw",
"get": "{class_name}.{name}.FilteredData(6)",
"where": "main",
"range": [
0,
1023
]
},
{
"name": "{name}_ch7_raw",
"get": "{class_name}.{name}.FilteredData(7)",
"where": "main",
"range": [
0,
1023
]
},
{
"name": "{name}_ch8_raw",
"get": "{class_name}.{name}.FilteredData(8)",
"where": "main",
"range": [
0,
1023
]
},
{
"name": "{name}_ch9_raw",
"get": "{class_name}.{name}.FilteredData(9)",
"where": "main",
"range": [
0,
1023
]
},
{
"name": "{name}_ch10_raw",
"get": "{class_name}.{name}.FilteredData(10)",
"where": "main",
"range": [
0,
1023
]
},
{
"name": "{name}_ch11_raw",
"get": "{class_name}.{name}.FilteredData(11)",
"where": "main",
"range": [
0,
1023
]
}
]
},
"Apds9960": {
"map_init": "daisy::Apds9960I2CTransport::Config {name}_config;\n {name}_config.periph = {periph};\n {name}_config.speed = {speed};\n {name}_config.scl = som.GetPin({pin_scl});\n {name}_config.sda = som.GetPin({pin_sda});\n daisy::Apds9960I2C::Config {name}_main_conf;\n {name}_main_conf.transport_config = {name}_config;\n {name}.Init({name}_main_conf);",
"typename": "daisy::Apds9960I2C",
"direction": "in",
"pin": "scl,sda",
"periph": "daisy::I2CHandle::Config::Peripheral::I2C_1",
"speed": "daisy::I2CHandle::Config::Speed::I2C_400KHZ",
"mapping": [
{
"name": "{name}",
"get": "{class_name}.{name}.ReadGesture()",
"where": "main"
},
{
"name": "{name}_gest",
"get": "{class_name}.{name}.ReadGesture()",
"where": "main"
},
{
"name": "{name}_prox",
"get": "{class_name}.{name}.ReadProximity()",
"where": "main"
},
{
"name": "{name}_red",
"get": "{class_name}.{name}.GetColorDataRed()",
"where": "main"
},
{
"name": "{name}_green",
"get": "{class_name}.{name}.GetColorDataGreen()",
"where": "main"
},
{
"name": "{name}_blue",
"get": "{class_name}.{name}.GetColorDataBlue()",
"where": "main"
},
{
"name": "{name}_clear",
"get": "{class_name}.{name}.GetColorDataClear()",
"where": "main"
}
]
},
"NeoTrellis": {
"map_init": "daisy::NeoTrellisI2CTransport::Config {name}_config;\n {name}_config.periph = {periph};\n {name}_config.speed = {speed};\n {name}_config.scl = som.GetPin({pin_scl});\n {name}_config.sda = som.GetPin({pin_sda});\n {name}_config.address = {address};\n daisy::NeoTrellisI2C::Config {name}_main_conf;\n {name}_main_conf.transport_config = {name}_config;\n {name}.Init({name}_main_conf);",
"typename": "daisy::NeoTrellisI2C",
"direction": "in",
"pin": "scl,sda",
"address": "NEO_TRELLIS_ADDR",
"periph": "daisy::I2CHandle::Config::Peripheral::I2C_1",
"speed": "daisy::I2CHandle::Config::Speed::I2C_400KHZ",
"loopprocess": "{name}.Process();",
"mapping": [
{
"name": "{name}",
"get": "{class_name}.{name}.GetRising(0)",
"bool": true
},
{
"name": "{name}_0",
"get": "{class_name}.{name}.GetRising(0)",
"bool": true
},
{
"name": "{name}_1",
"get": "{class_name}.{name}.GetRising(1)",
"bool": true
},
{
"name": "{name}_2",
"get": "{class_name}.{name}.GetRising(2)",
"bool": true
},
{
"name": "{name}_3",
"get": "{class_name}.{name}.GetRising(3)",
"bool": true
},
{
"name": "{name}_4",
"get": "{class_name}.{name}.GetRising(4)",
"bool": true
},
{
"name": "{name}_5",
"get": "{class_name}.{name}.GetRising(5)",
"bool": true
},
{
"name": "{name}_6",
"get": "{class_name}.{name}.GetRising(6)",
"bool": true
},
{
"name": "{name}_7",
"get": "{class_name}.{name}.GetRising(7)",
"bool": true
},
{
"name": "{name}_8",
"get": "{class_name}.{name}.GetRising(8)",
"bool": true
},
{
"name": "{name}_9",
"get": "{class_name}.{name}.GetRising(9)",
"bool": true
},
{
"name": "{name}_10",
"get": "{class_name}.{name}.GetRising(10)",
"bool": true
},
{
"name": "{name}_11",
"get": "{class_name}.{name}.GetRising(11)",
"bool": true
},
{
"name": "{name}_12",
"get": "{class_name}.{name}.GetRising(12)",
"bool": true
},
{
"name": "{name}_13",
"get": "{class_name}.{name}.GetRising(13)",
"bool": true
},
{
"name": "{name}_14",
"get": "{class_name}.{name}.GetRising(14)",
"bool": true
},
{
"name": "{name}_15",
"get": "{class_name}.{name}.GetRising(15)",
"bool": true
},
{
"name": "{name}_0_falling",
"get": "{class_name}.{name}.GetFalling(0)",
"bool": true
},
{
"name": "{name}_1_falling",
"get": "{class_name}.{name}.GetFalling(1)",
"bool": true
},
{
"name": "{name}_2_falling",
"get": "{class_name}.{name}.GetFalling(2)",
"bool": true
},
{
"name": "{name}_3_falling",
"get": "{class_name}.{name}.GetFalling(3)",
"bool": true
},
{
"name": "{name}_4_falling",
"get": "{class_name}.{name}.GetFalling(4)",
"bool": true
},
{
"name": "{name}_5_falling",
"get": "{class_name}.{name}.GetFalling(5)",
"bool": true
},
{
"name": "{name}_6_falling",
"get": "{class_name}.{name}.GetFalling(6)",
"bool": true
},
{
"name": "{name}_7_falling",
"get": "{class_name}.{name}.GetFalling(7)",
"bool": true
},
{
"name": "{name}_8_falling",
"get": "{class_name}.{name}.GetFalling(8)",
"bool": true
},
{
"name": "{name}_9_falling",
"get": "{class_name}.{name}.GetFalling(9)",
"bool": true
},
{
"name": "{name}_10_falling",
"get": "{class_name}.{name}.GetFalling(10)",
"bool": true
},
{
"name": "{name}_11_falling",
"get": "{class_name}.{name}.GetFalling(11)",
"bool": true
},
{
"name": "{name}_12_falling",
"get": "{class_name}.{name}.GetFalling(12)",
"bool": true
},
{
"name": "{name}_13_falling",
"get": "{class_name}.{name}.GetFalling(13)",
"bool": true
},
{
"name": "{name}_14_falling",
"get": "{class_name}.{name}.GetFalling(14)",
"bool": true
},
{
"name": "{name}_15_falling",
"get": "{class_name}.{name}.GetFalling(15)",
"bool": true
},
{
"name": "{name}_0_state",
"get": "({class_name}.{name}.GetState(0) ? 1.0f : 0.0f)"
},
{
"name": "{name}_1_state",
"get": "({class_name}.{name}.GetState(1) ? 1.0f : 0.0f)"
},
{
"name": "{name}_2_state",
"get": "({class_name}.{name}.GetState(2) ? 1.0f : 0.0f)"
},
{
"name": "{name}_3_state",
"get": "({class_name}.{name}.GetState(3) ? 1.0f : 0.0f)"
},
{
"name": "{name}_4_state",
"get": "({class_name}.{name}.GetState(4) ? 1.0f : 0.0f)"
},
{
"name": "{name}_5_state",
"get": "({class_name}.{name}.GetState(5) ? 1.0f : 0.0f)"
},
{
"name": "{name}_6_state",
"get": "({class_name}.{name}.GetState(6) ? 1.0f : 0.0f)"
},
{
"name": "{name}_7_state",
"get": "({class_name}.{name}.GetState(7) ? 1.0f : 0.0f)"
},
{
"name": "{name}_8_state",
"get": "({class_name}.{name}.GetState(8) ? 1.0f : 0.0f)"
},
{
"name": "{name}_9_state",
"get": "({class_name}.{name}.GetState(9) ? 1.0f : 0.0f)"
},
{
"name": "{name}_10_state",
"get": "({class_name}.{name}.GetState(10) ? 1.0f : 0.0f)"
},
{
"name": "{name}_11_state",
"get": "({class_name}.{name}.GetState(11) ? 1.0f : 0.0f)"
},
{
"name": "{name}_12_state",
"get": "({class_name}.{name}.GetState(12) ? 1.0f : 0.0f)"
},
{
"name": "{name}_13_state",
"get": "({class_name}.{name}.GetState(13) ? 1.0f : 0.0f)"
},
{
"name": "{name}_14_state",
"get": "({class_name}.{name}.GetState(14) ? 1.0f : 0.0f)"
},
{
"name": "{name}_15_state",
"get": "({class_name}.{name}.GetState(15) ? 1.0f : 0.0f)"
}
]
},
"NeoTrellisLeds": {
"map_init": "",
"parent": "",
"direction": "out",
"loopprocess": "{parent}.pixels.Show();",
"mapping": [
{
"name": "{name}",
"set": "{class_name}.{parent}.pixels.SetPixelColor(0, {class_name}.{parent}.pixels.Color({value}, {value}, {value}));",
"where": "main"
},
{
"name": "{name}_0",
"set": "{class_name}.{parent}.pixels.SetPixelColor(0, {class_name}.{parent}.pixels.Color({value}, {value}, {value}));",
"where": "main"
},
{
"name": "{name}_1",
"set": "{class_name}.{parent}.pixels.SetPixelColor(1, {class_name}.{parent}.pixels.Color({value}, {value}, {value}));",
"where": "main"
},
{
"name": "{name}_2",
"set": "{class_name}.{parent}.pixels.SetPixelColor(2, {class_name}.{parent}.pixels.Color({value}, {value}, {value}));",
"where": "main"
},
{
"name": "{name}_3",
"set": "{class_name}.{parent}.pixels.SetPixelColor(3, {class_name}.{parent}.pixels.Color({value}, {value}, {value}));",
"where": "main"
},
{
"name": "{name}_4",
"set": "{class_name}.{parent}.pixels.SetPixelColor(4, {class_name}.{parent}.pixels.Color({value}, {value}, {value}));",
"where": "main"
},
{
"name": "{name}_5",
"set": "{class_name}.{parent}.pixels.SetPixelColor(5, {class_name}.{parent}.pixels.Color({value}, {value}, {value}));",
"where": "main"
},
{
"name": "{name}_6",
"set": "{class_name}.{parent}.pixels.SetPixelColor(6, {class_name}.{parent}.pixels.Color({value}, {value}, {value}));",
"where": "main"
},
{
"name": "{name}_7",
"set": "{class_name}.{parent}.pixels.SetPixelColor(7, {class_name}.{parent}.pixels.Color({value}, {value}, {value}));",
"where": "main"
},
{
"name": "{name}_8",
"set": "{class_name}.{parent}.pixels.SetPixelColor(8, {class_name}.{parent}.pixels.Color({value}, {value}, {value}));",
"where": "main"
},
{
"name": "{name}_9",
"set": "{class_name}.{parent}.pixels.SetPixelColor(9, {class_name}.{parent}.pixels.Color({value}, {value}, {value}));",
"where": "main"
},
{
"name": "{name}_10",
"set": "{class_name}.{parent}.pixels.SetPixelColor(10, {class_name}.{parent}.pixels.Color({value}, {value}, {value}));",
"where": "main"
},
{
"name": "{name}_11",
"set": "{class_name}.{parent}.pixels.SetPixelColor(11, {class_name}.{parent}.pixels.Color({value}, {value}, {value}));",
"where": "main"
},
{
"name": "{name}_12",
"set": "{class_name}.{parent}.pixels.SetPixelColor(12, {class_name}.{parent}.pixels.Color({value}, {value}, {value}));",
"where": "main"
},
{
"name": "{name}_13",
"set": "{class_name}.{parent}.pixels.SetPixelColor(13, {class_name}.{parent}.pixels.Color({value}, {value}, {value}));",
"where": "main"
},
{
"name": "{name}_14",
"set": "{class_name}.{parent}.pixels.SetPixelColor(14, {class_name}.{parent}.pixels.Color({value}, {value}, {value}));",
"where": "main"
},
{
"name": "{name}_15",
"set": "{class_name}.{parent}.pixels.SetPixelColor(15, {class_name}.{parent}.pixels.Color({value}, {value}, {value}));",
"where": "main"
}
]
},
"Icm20948": {
"map_init": "daisy::Icm20948I2CTransport::Config {name}_config;\n {name}_config.periph = {periph};\n {name}_config.speed = {speed};\n {name}_config.scl = som.GetPin({pin_scl});\n {name}_config.sda = som.GetPin({pin_sda});\n {name}_config.address = {address};\n daisy::Icm20948I2C::Config {name}_main_conf;\n {name}_main_conf.transport_config = {name}_config;\n {name}.Init({name}_main_conf);",
"typename": "daisy::Icm20948I2C",
"direction": "in",
"pin": "scl,sda",
"address": "ICM20948_I2CADDR_DEFAULT",
"periph": "daisy::I2CHandle::Config::Peripheral::I2C_1",
"speed": "daisy::I2CHandle::Config::Speed::I2C_400KHZ",
"loopprocess": "{name}.Process();",
"mapping": [
{
"name": "{name}",
"get": "{class_name}.{name}.GetAccelVect().x"
},
{
"name": "{name}_accel_x",
"get": "{class_name}.{name}.GetAccelVect().x"
},
{
"name": "{name}_accel_y",
"get": "{class_name}.{name}.GetAccelVect().y"
},
{
"name": "{name}_accel_z",
"get": "{class_name}.{name}.GetAccelVect().z"
},
{
"name": "{name}_magnet_x",
"get": "{class_name}.{name}.GetMagVect().x"
},
{
"name": "{name}_magnet_y",
"get": "{class_name}.{name}.GetMagVect().y"
},
{
"name": "{name}_magnet_z",
"get": "{class_name}.{name}.GetMagVect().z"
},
{
"name": "{name}_gyro_x",
"get": "{class_name}.{name}.GetGyroVect().x"
},
{
"name": "{name}_gyro_y",
"get": "{class_name}.{name}.GetGyroVect().y"
},
{
"name": "{name}_gyro_z",
"get": "{class_name}.{name}.GetGyroVect().z"
}
]
},
"Dps310": {
"map_init": "daisy::Dps310I2CTransport::Config {name}_config;\n {name}_config.address = {address};\n {name}_config.periph = {periph};\n {name}_config.speed = {speed};\n {name}_config.scl = som.GetPin({pin_scl});\n {name}_config.sda = som.GetPin({pin_sda});\n daisy::Dps310I2C::Config {name}_config_main;\n {name}_config_main.transport_config = {name}_config;\n {name}.Init({name}_config_main);",
"typename": "daisy::Dps310I2C",
"direction": "in",
"pin": "scl,sda",
"periph": "daisy::I2CHandle::Config::Peripheral::I2C_1",
"speed": "daisy::I2CHandle::Config::Speed::I2C_400KHZ",
"address": "DPS310_I2CADDR_DEFAULT",
"pressure": 1013,
"loopprocess": "{name}.Process();",
"mapping": [
{
"name": "{name}",
"get": "{class_name}.{name}.GetTemperature()"
},
{
"name": "{name}_temp",
"get": "{class_name}.{name}.GetTemperature()"
},
{
"name": "{name}_press",
"get": "{class_name}.{name}.GetPressure()"
},
{
"name": "{name}_alt",
"get": "{class_name}.{name}.GetAltitude({pressure})"
}
]
}
}
hvcc-0.16.0/hvcc/generators/c2daisy/json2daisy/resources/component_defs_patchsm.json 0000644 0000000 0000000 00000052742 00000000000 025570 0 ustar 00 {
"UserLed": {
"map_init": "",
"typename": "",
"direction": "out",
"mapping": [
{
"name": "{name}",
"set": "{class_name}.som.SetLed({value});",
"where": "hook"
}
]
},
"Switch": {
"map_init": "{name}.Init(daisy::patch_sm::DaisyPatchSM::{pin}, som.AudioCallbackRate(), {type}, {polarity});",
"typename": "daisy::Switch",
"direction": "in",
"pin": "a",
"type": "daisy::Switch::TYPE_MOMENTARY",
"polarity": "daisy::Switch::POLARITY_INVERTED",
"process": "{name}.Debounce();",
"updaterate": "{name}.SetUpdateRate(som.AudioCallbackRate());",
"mapping": [
{
"name": "{name}_press",
"get": "({class_name}.{name}.Pressed()?1.f:0.f)",
"range": [
0,
1
],
"bool": false
},
{
"name": "{name}",
"get": "({class_name}.{name}.RisingEdge()?1.f:0.f)",
"range": [
0,
1
],
"bool": true
},
{
"name": "{name}_fall",
"get": "({class_name}.{name}.FallingEdge()?1.f:0.f)",
"range": [
0,
1
],
"bool": true
},
{
"name": "{name}_seconds",
"get": "({class_name}.{name}.TimeHeldMs()*0.001f)",
"range": null,
"bool": false
}
]
},
"Switch3": {
"map_init": "{name}.Init(daisy::patch_sm::DaisyPatchSM::{pin_a}, daisy::patch_sm::DaisyPatchSM::{pin_b}));",
"typename": "daisy::Switch3",
"direction": "in",
"pin": "a,b",
"mapping": [
{
"name": "{name}",
"get": "({class_name}.{name}.Read()*0.5f+0.5f)",
"range": [
0,
2
],
"bool": false
}
]
},
"Encoder": {
"map_init": "{name}.Init(daisy::patch_sm::DaisyPatchSM::{pin_a}, daisy::patch_sm::DaisyPatchSM::{pin_b}, daisy::patch_sm::DaisyPatchSM::{pin_click}, som.AudioCallbackRate());",
"typename": "daisy::Encoder",
"direction": "in",
"pin": "a,b,click",
"process": "{name}.Debounce();",
"updaterate": "{name}.SetUpdateRate(som.AudioCallbackRate());",
"mapping": [
{
"name": "{name}",
"get": "({class_name}.{name}.Increment())",
"range": [
-1,
1
],
"bool": false
},
{
"name": "{name}_press",
"get": "({class_name}.{name}.Pressed()?1.f:0.f)",
"range": [
0,
1
],
"bool": false
},
{
"name": "{name}_rise",
"get": "({class_name}.{name}.RisingEdge()?1.f:0.f)",
"range": [
0,
1
],
"bool": true
},
{
"name": "{name}_fall",
"get": "({class_name}.{name}.FallingEdge()?1.f:0.f)",
"range": [
0,
1
],
"bool": true
},
{
"name": "{name}_seconds",
"get": "({class_name}.{name}.TimeHeldMs()*0.001f)",
"range": null,
"bool": false
}
]
},
"GateIn": {
"map_init": "{name}.Init(&daisy::patch_sm::DaisyPatchSM::{pin});",
"typename": "daisy::GateIn",
"direction": "in",
"pin": "a",
"default_prefix": "som.",
"mapping": [
{
"name": "{name}",
"get": "({class_name}.{default_prefix}{name}.State()?1.f:0.f)",
"range": [
0,
1
],
"bool": false
},
{
"name": "{name}_trig",
"get": "({class_name}.{default_prefix}{name}.Trig()?1.f:0.f)",
"range": [
0,
1
],
"bool": true
}
]
},
"AnalogControl": {
"init_single": "cfg[{i}].InitSingle(daisy::patch_sm::DaisyPatchSM::{pin});",
"map_init": "{name}.Init(som.adc.GetPtr({i}), som.AudioCallbackRate(), {flip}, {invert});",
"typename": "daisy::AnalogControl",
"direction": "in",
"pin": "a",
"flip": "false",
"invert": "false",
"slew": "1.0/som.AudioCallbackRate()",
"process": "{name}.Process();",
"updaterate": "{name}.SetSampleRate(som.AudioCallbackRate());",
"mapping": [
{
"name": "{name}",
"get": "({class_name}.som.GetAdcValue((int)daisy::patch_sm::{name_upper}))",
"range": [
0,
1
],
"bool": false
}
]
},
"Led": {
"map_init": "{name}.Init(daisy::patch_sm::DaisyPatchSM::{pin}, {invert});\n\t\t{name}.Set(0.0f);",
"typename": "daisy::Led",
"direction": "out",
"pin": "a",
"invert": "true",
"postprocess": "{name}.Update();",
"mapping": [
{
"name": "{name}",
"set": "{class_name}.{name}.Set({value});"
}
]
},
"RgbLed": {
"map_init": "{name}.Init(daisy::patch_sm::DaisyPatchSM::{pin_r}, daisy::patch_sm::DaisyPatchSM::{pin_g}, daisy::patch_sm::DaisyPatchSM::{pin_b}, {invert});\n\t\t{name}.Set(0.0f, 0.0f, 0.0f);",
"typename": "daisy::RgbLed",
"direction": "out",
"pin": "r,g,b",
"invert": "true",
"postprocess": "{name}.Update();",
"mapping": [
{
"name": "{name}_red",
"set": "{class_name}.{name}.SetRed({value});"
},
{
"name": "{name}_green",
"set": "{class_name}.{name}.SetGreen({value});"
},
{
"name": "{name}_blue",
"set": "{class_name}.{name}.SetBlue({value});"
},
{
"name": "{name}",
"set": "{class_name}.{name}.Set(clamp(-{value}, 0.f, 1.f), 0.f, clamp({value}, 0.f, 1.f));"
},
{
"name": "{name}_white",
"set": "{class_name}.{name}.Set({value},{value},{value});"
}
]
},
"GateOut": {
"map_init": "{name}.Init(daisy::patch_sm::DaisyPatchSM::{pin}, {mode}, {pull});",
"typename": "daisy::GPIO",
"direction": "out",
"pin": "a",
"default_prefix": "som.",
"mode": "daisy::GPIO::Mode::OUTPUT",
"pull": "daisy::GPIO::Pull::NOPULL",
"mapping": [
{
"name": "{name}",
"set": "{class_name}.{default_prefix}{name}.Write({value});"
}
]
},
"CVOuts": {
"map_init": "{name}.bitdepth = {bitdepth};\n\t\t{name}.buff_state = {buff_state};\n\t\t{name}.mode = {mode};\n\t\t{name}.chn = {channel};\n\t\tsom.dac.Init({name});\n\t\tsom.dac.WriteValue({channel}, 0);",
"typename": "daisy::DacHandle::Config",
"direction": "out",
"pin": "",
"bitdepth": "daisy::DacHandle::BitDepth::BITS_12",
"buff_state": "daisy::DacHandle::BufferState::ENABLED",
"mode": "daisy::DacHandle::Mode::POLLING",
"channel": "daisy::DacHandle::Channel::BOTH",
"mapping": [
{
"name": "{name}1",
"set": "{class_name}.som.WriteCvOut(daisy::patch_sm::CV_OUT_1, {value} * 5.f);",
"where": "main"
},
{
"name": "{name}2",
"set": "{class_name}.som.WriteCvOut(daisy::patch_sm::CV_OUT_2, {value} * 5.f);",
"where": "main"
}
]
},
"Tlv493d": {
"map_init": "daisy::Tlv493dI2CTransport::Config {name}_config;\n {name}_config.periph = {periph};\n {name}_config.speed = {speed};\n {name}_config.scl = som.GetPin({pin_scl});\n {name}_config.sda = som.GetPin({pin_sda});\n daisy::Tlv493dI2C::Config {name}_main_conf;\n {name}_main_conf.transport_config = {name}_config;\n {name}_main_conf.address = {address};\n {name}.Init({name}_main_conf);",
"typename": "daisy::Tlv493dI2C",
"direction": "in",
"pin": "scl,sda",
"address": "TLV493D_ADDRESS1",
"periph": "daisy::I2CHandle::Config::Peripheral::I2C_1",
"speed": "daisy::I2CHandle::Config::Speed::I2C_400KHZ",
"loopprocess": "{name}.UpdateData();",
"mapping": [
{
"name": "{name}",
"get": "{class_name}.{name}.GetAmount()"
},
{
"name": "{name}_x",
"get": "{class_name}.{name}.GetX()"
},
{
"name": "{name}_y",
"get": "{class_name}.{name}.GetY()"
},
{
"name": "{name}_z",
"get": "{class_name}.{name}.GetZ()"
},
{
"name": "{name}_amount",
"get": "{class_name}.{name}.GetAmount()"
},
{
"name": "{name}_azimuth",
"get": "{class_name}.{name}.GetAzimuth()"
},
{
"name": "{name}_polar",
"get": "{class_name}.{name}.GetPolar()"
}
]
},
"Mpr121": {
"map_init": "daisy::Mpr121I2CTransport::Config {name}_config;\n {name}_config.periph = {periph};\n {name}_config.speed = {speed};\n {name}_config.dev_addr = {address};\n daisy::Mpr121I2C::Config {name}_main_conf;\n {name}_main_conf.transport_config = {name}_config;\n {name}_main_conf.touch_threshold = {touch_threshold};\n {name}_main_conf.release_threshold = {release_threshold};\n {name}.Init({name}_main_conf);",
"typename": "daisy::Mpr121I2C",
"direction": "in",
"pin": "scl,sda",
"address": "MPR121_I2CADDR_DEFAULT",
"periph": "daisy::I2CHandle::Config::Peripheral::I2C_1",
"speed": "daisy::I2CHandle::Config::Speed::I2C_400KHZ",
"touch_threshold": "MPR121_TOUCH_THRESHOLD_DEFAULT",
"release_threshold": "MPR121_RELEASE_THRESHOLD_DEFAULT",
"mapping": [
{
"name": "{name}",
"get": "(({class_name}.{name}.Touched() & 0x001) ? 1.0f : 0.f)",
"where": "main"
},
{
"name": "{name}_ch0",
"get": "(({class_name}.{name}.Touched() & 0x001) ? 1.0f : 0.f)",
"where": "main"
},
{
"name": "{name}_ch1",
"get": "(({class_name}.{name}.Touched() & 0x002) ? 1.0f : 0.f)",
"where": "main"
},
{
"name": "{name}_ch2",
"get": "(({class_name}.{name}.Touched() & 0x004) ? 1.0f : 0.f)",
"where": "main"
},
{
"name": "{name}_ch3",
"get": "(({class_name}.{name}.Touched() & 0x008) ? 1.0f : 0.f)",
"where": "main"
},
{
"name": "{name}_ch4",
"get": "(({class_name}.{name}.Touched() & 0x010) ? 1.0f : 0.f)",
"where": "main"
},
{
"name": "{name}_ch5",
"get": "(({class_name}.{name}.Touched() & 0x020) ? 1.0f : 0.f)",
"where": "main"
},
{
"name": "{name}_ch6",
"get": "(({class_name}.{name}.Touched() & 0x040) ? 1.0f : 0.f)",
"where": "main"
},
{
"name": "{name}_ch7",
"get": "(({class_name}.{name}.Touched() & 0x080) ? 1.0f : 0.f)",
"where": "main"
},
{
"name": "{name}_ch8",
"get": "(({class_name}.{name}.Touched() & 0x100) ? 1.0f : 0.f)",
"where": "main"
},
{
"name": "{name}_ch9",
"get": "(({class_name}.{name}.Touched() & 0x200) ? 1.0f : 0.f)",
"where": "main"
},
{
"name": "{name}_ch10",
"get": "(({class_name}.{name}.Touched() & 0x400) ? 1.0f : 0.f)",
"where": "main"
},
{
"name": "{name}_ch11",
"get": "(({class_name}.{name}.Touched() & 0x800) ? 1.0f : 0.f)",
"where": "main"
},
{
"name": "{name}_ch0_raw",
"get": "{class_name}.{name}.FilteredData(0)",
"where": "main",
"range": [
0,
1023
]
},
{
"name": "{name}_ch1_raw",
"get": "{class_name}.{name}.FilteredData(1)",
"where": "main",
"range": [
0,
1023
]
},
{
"name": "{name}_ch2_raw",
"get": "{class_name}.{name}.FilteredData(2)",
"where": "main",
"range": [
0,
1023
]
},
{
"name": "{name}_ch3_raw",
"get": "{class_name}.{name}.FilteredData(3)",
"where": "main",
"range": [
0,
1023
]
},
{
"name": "{name}_ch4_raw",
"get": "{class_name}.{name}.FilteredData(4)",
"where": "main",
"range": [
0,
1023
]
},
{
"name": "{name}_ch5_raw",
"get": "{class_name}.{name}.FilteredData(5)",
"where": "main",
"range": [
0,
1023
]
},
{
"name": "{name}_ch6_raw",
"get": "{class_name}.{name}.FilteredData(6)",
"where": "main",
"range": [
0,
1023
]
},
{
"name": "{name}_ch7_raw",
"get": "{class_name}.{name}.FilteredData(7)",
"where": "main",
"range": [
0,
1023
]
},
{
"name": "{name}_ch8_raw",
"get": "{class_name}.{name}.FilteredData(8)",
"where": "main",
"range": [
0,
1023
]
},
{
"name": "{name}_ch9_raw",
"get": "{class_name}.{name}.FilteredData(9)",
"where": "main",
"range": [
0,
1023
]
},
{
"name": "{name}_ch10_raw",
"get": "{class_name}.{name}.FilteredData(10)",
"where": "main",
"range": [
0,
1023
]
},
{
"name": "{name}_ch11_raw",
"get": "{class_name}.{name}.FilteredData(11)",
"where": "main",
"range": [
0,
1023
]
}
]
},
"Apds9960": {
"map_init": "daisy::Apds9960I2CTransport::Config {name}_config;\n {name}_config.periph = {periph};\n {name}_config.speed = {speed};\n {name}_config.scl = som.GetPin({pin_scl});\n {name}_config.sda = som.GetPin({pin_sda});\n daisy::Apds9960I2C::Config {name}_main_conf;\n {name}_main_conf.transport_config = {name}_config;\n {name}.Init({name}_main_conf);",
"typename": "daisy::Apds9960I2C",
"direction": "in",
"pin": "scl,sda",
"periph": "daisy::I2CHandle::Config::Peripheral::I2C_1",
"speed": "daisy::I2CHandle::Config::Speed::I2C_400KHZ",
"mapping": [
{
"name": "{name}",
"get": "{class_name}.{name}.ReadGesture()",
"where": "main"
},
{
"name": "{name}_gest",
"get": "{class_name}.{name}.ReadGesture()",
"where": "main"
},
{
"name": "{name}_prox",
"get": "{class_name}.{name}.ReadProximity()",
"where": "main"
},
{
"name": "{name}_red",
"get": "{class_name}.{name}.GetColorDataRed()",
"where": "main"
},
{
"name": "{name}_green",
"get": "{class_name}.{name}.GetColorDataGreen()",
"where": "main"
},
{
"name": "{name}_blue",
"get": "{class_name}.{name}.GetColorDataBlue()",
"where": "main"
},
{
"name": "{name}_clear",
"get": "{class_name}.{name}.GetColorDataClear()",
"where": "main"
}
]
},
"NeoTrellis": {
"map_init": "daisy::NeoTrellisI2CTransport::Config {name}_config;\n {name}_config.periph = {periph};\n {name}_config.speed = {speed};\n {name}_config.scl = som.GetPin({pin_scl});\n {name}_config.sda = som.GetPin({pin_sda});\n {name}_config.address = {address};\n daisy::NeoTrellisI2C::Config {name}_main_conf;\n {name}_main_conf.transport_config = {name}_config;\n {name}.Init({name}_main_conf);",
"typename": "daisy::NeoTrellisI2C",
"direction": "in",
"pin": "scl,sda",
"address": "NEO_TRELLIS_ADDR",
"periph": "daisy::I2CHandle::Config::Peripheral::I2C_1",
"speed": "daisy::I2CHandle::Config::Speed::I2C_400KHZ",
"loopprocess": "{name}.Process();",
"mapping": [
{
"name": "{name}",
"get": "{class_name}.{name}.GetRising(0)",
"bool": true
},
{
"name": "{name}_0",
"get": "{class_name}.{name}.GetRising(0)",
"bool": true
},
{
"name": "{name}_1",
"get": "{class_name}.{name}.GetRising(1)",
"bool": true
},
{
"name": "{name}_2",
"get": "{class_name}.{name}.GetRising(2)",
"bool": true
},
{
"name": "{name}_3",
"get": "{class_name}.{name}.GetRising(3)",
"bool": true
},
{
"name": "{name}_4",
"get": "{class_name}.{name}.GetRising(4)",
"bool": true
},
{
"name": "{name}_5",
"get": "{class_name}.{name}.GetRising(5)",
"bool": true
},
{
"name": "{name}_6",
"get": "{class_name}.{name}.GetRising(6)",
"bool": true
},
{
"name": "{name}_7",
"get": "{class_name}.{name}.GetRising(7)",
"bool": true
},
{
"name": "{name}_8",
"get": "{class_name}.{name}.GetRising(8)",
"bool": true
},
{
"name": "{name}_9",
"get": "{class_name}.{name}.GetRising(9)",
"bool": true
},
{
"name": "{name}_10",
"get": "{class_name}.{name}.GetRising(10)",
"bool": true
},
{
"name": "{name}_11",
"get": "{class_name}.{name}.GetRising(11)",
"bool": true
},
{
"name": "{name}_12",
"get": "{class_name}.{name}.GetRising(12)",
"bool": true
},
{
"name": "{name}_13",
"get": "{class_name}.{name}.GetRising(13)",
"bool": true
},
{
"name": "{name}_14",
"get": "{class_name}.{name}.GetRising(14)",
"bool": true
},
{
"name": "{name}_15",
"get": "{class_name}.{name}.GetRising(15)",
"bool": true
},
{
"name": "{name}_0_falling",
"get": "{class_name}.{name}.GetFalling(0)",
"bool": true
},
{
"name": "{name}_1_falling",
"get": "{class_name}.{name}.GetFalling(1)",
"bool": true
},
{
"name": "{name}_2_falling",
"get": "{class_name}.{name}.GetFalling(2)",
"bool": true
},
{
"name": "{name}_3_falling",
"get": "{class_name}.{name}.GetFalling(3)",
"bool": true
},
{
"name": "{name}_4_falling",
"get": "{class_name}.{name}.GetFalling(4)",
"bool": true
},
{
"name": "{name}_5_falling",
"get": "{class_name}.{name}.GetFalling(5)",
"bool": true
},
{
"name": "{name}_6_falling",
"get": "{class_name}.{name}.GetFalling(6)",
"bool": true
},
{
"name": "{name}_7_falling",
"get": "{class_name}.{name}.GetFalling(7)",
"bool": true
},
{
"name": "{name}_8_falling",
"get": "{class_name}.{name}.GetFalling(8)",
"bool": true
},
{
"name": "{name}_9_falling",
"get": "{class_name}.{name}.GetFalling(9)",
"bool": true
},
{
"name": "{name}_10_falling",
"get": "{class_name}.{name}.GetFalling(10)",
"bool": true
},
{
"name": "{name}_11_falling",
"get": "{class_name}.{name}.GetFalling(11)",
"bool": true
},
{
"name": "{name}_12_falling",
"get": "{class_name}.{name}.GetFalling(12)",
"bool": true
},
{
"name": "{name}_13_falling",
"get": "{class_name}.{name}.GetFalling(13)",
"bool": true
},
{
"name": "{name}_14_falling",
"get": "{class_name}.{name}.GetFalling(14)",
"bool": true
},
{
"name": "{name}_15_falling",
"get": "{class_name}.{name}.GetFalling(15)",
"bool": true
},
{
"name": "{name}_0_state",
"get": "({class_name}.{name}.GetState(0) ? 1.0f : 0.0f)"
},
{
"name": "{name}_1_state",
"get": "({class_name}.{name}.GetState(1) ? 1.0f : 0.0f)"
},
{
"name": "{name}_2_state",
"get": "({class_name}.{name}.GetState(2) ? 1.0f : 0.0f)"
},
{
"name": "{name}_3_state",
"get": "({class_name}.{name}.GetState(3) ? 1.0f : 0.0f)"
},
{
"name": "{name}_4_state",
"get": "({class_name}.{name}.GetState(4) ? 1.0f : 0.0f)"
},
{
"name": "{name}_5_state",
"get": "({class_name}.{name}.GetState(5) ? 1.0f : 0.0f)"
},
{
"name": "{name}_6_state",
"get": "({class_name}.{name}.GetState(6) ? 1.0f : 0.0f)"
},
{
"name": "{name}_7_state",
"get": "({class_name}.{name}.GetState(7) ? 1.0f : 0.0f)"
},
{
"name": "{name}_8_state",
"get": "({class_name}.{name}.GetState(8) ? 1.0f : 0.0f)"
},
{
"name": "{name}_9_state",
"get": "({class_name}.{name}.GetState(9) ? 1.0f : 0.0f)"
},
{
"name": "{name}_10_state",
"get": "({class_name}.{name}.GetState(10) ? 1.0f : 0.0f)"
},
{
"name": "{name}_11_state",
"get": "({class_name}.{name}.GetState(11) ? 1.0f : 0.0f)"
},
{
"name": "{name}_12_state",
"get": "({class_name}.{name}.GetState(12) ? 1.0f : 0.0f)"
},
{
"name": "{name}_13_state",
"get": "({class_name}.{name}.GetState(13) ? 1.0f : 0.0f)"
},
{
"name": "{name}_14_state",
"get": "({class_name}.{name}.GetState(14) ? 1.0f : 0.0f)"
},
{
"name": "{name}_15_state",
"get": "({class_name}.{name}.GetState(15) ? 1.0f : 0.0f)"
}
]
},
"NeoTrellisLeds": {
"map_init": "",
"parent": "",
"direction": "out",
"loopprocess": "{parent}.Show();",
"mapping": [
{
"name": "{name}",
"set": "{class_name}.{parent}.SetPixelColor(0, {class_name}.{parent}.pixels.Color({value}, {value}, {value}));",
"where": "main"
},
{
"name": "{name}_0",
"set": "{class_name}.{parent}.SetPixelColor(0, {class_name}.{parent}.pixels.Color({value}, {value}, {value}));",
"where": "main"
},
{
"name": "{name}_1",
"set": "{class_name}.{parent}.SetPixelColor(1, {class_name}.{parent}.pixels.Color({value}, {value}, {value}));",
"where": "main"
},
{
"name": "{name}_2",
"set": "{class_name}.{parent}.SetPixelColor(2, {class_name}.{parent}.pixels.Color({value}, {value}, {value}));",
"where": "main"
},
{
"name": "{name}_3",
"set": "{class_name}.{parent}.SetPixelColor(3, {class_name}.{parent}.pixels.Color({value}, {value}, {value}));",
"where": "main"
},
{
"name": "{name}_4",
"set": "{class_name}.{parent}.SetPixelColor(4, {class_name}.{parent}.pixels.Color({value}, {value}, {value}));",
"where": "main"
},
{
"name": "{name}_5",
"set": "{class_name}.{parent}.SetPixelColor(5, {class_name}.{parent}.pixels.Color({value}, {value}, {value}));",
"where": "main"
},
{
"name": "{name}_6",
"set": "{class_name}.{parent}.SetPixelColor(6, {class_name}.{parent}.pixels.Color({value}, {value}, {value}));",
"where": "main"
},
{
"name": "{name}_7",
"set": "{class_name}.{parent}.SetPixelColor(7, {class_name}.{parent}.pixels.Color({value}, {value}, {value}));",
"where": "main"
},
{
"name": "{name}_8",
"set": "{class_name}.{parent}.SetPixelColor(8, {class_name}.{parent}.pixels.Color({value}, {value}, {value}));",
"where": "main"
},
{
"name": "{name}_9",
"set": "{class_name}.{parent}.SetPixelColor(9, {class_name}.{parent}.pixels.Color({value}, {value}, {value}));",
"where": "main"
},
{
"name": "{name}_10",
"set": "{class_name}.{parent}.SetPixelColor(10, {class_name}.{parent}.pixels.Color({value}, {value}, {value}));",
"where": "main"
},
{
"name": "{name}_11",
"set": "{class_name}.{parent}.SetPixelColor(11, {class_name}.{parent}.pixels.Color({value}, {value}, {value}));",
"where": "main"
},
{
"name": "{name}_12",
"set": "{class_name}.{parent}.SetPixelColor(12, {class_name}.{parent}.pixels.Color({value}, {value}, {value}));",
"where": "main"
},
{
"name": "{name}_13",
"set": "{class_name}.{parent}.SetPixelColor(13, {class_name}.{parent}.pixels.Color({value}, {value}, {value}));",
"where": "main"
},
{
"name": "{name}_14",
"set": "{class_name}.{parent}.SetPixelColor(14, {class_name}.{parent}.pixels.Color({value}, {value}, {value}));",
"where": "main"
},
{
"name": "{name}_15",
"set": "{class_name}.{parent}.SetPixelColor(15, {class_name}.{parent}.pixels.Color({value}, {value}, {value}));",
"where": "main"
}
]
}
}
hvcc-0.16.0/hvcc/generators/c2daisy/json2daisy/resources/component_defs_petalsm.json 0000644 0000000 0000000 00000005154 00000000000 025571 0 ustar 00 {
"Switch": {
"map_init": "",
"typename": "",
"direction": "in",
"index": 1,
"updaterate": "",
"mapping": [
{
"name": "{name}_press",
"get": "({class_name}.som.footswitch{index}.Pressed()?1.f:0.f)",
"range": [
0,
1
],
"bool": false
},
{
"name": "{name}",
"get": "({class_name}.som.footswitch{index}.RisingEdge()?1.f:0.f)",
"range": [
0,
1
],
"bool": true
},
{
"name": "{name}_fall",
"get": "({class_name}.som.footswitch{index}.FallingEdge()?1.f:0.f)",
"range": [
0,
1
],
"bool": true
},
{
"name": "{name}_seconds",
"get": "({class_name}.som.footswitch{index}.TimeHeldMs()*0.001f)",
"bool": false
}
]
},
"Switch3": {
"map_init": "",
"typename": "",
"direction": "in",
"index": 0,
"mapping": [
{
"name": "{name}",
"get": "({class_name}.som.toggle[{index}].Read())",
"range": [
0,
2
],
"bool": false
}
]
},
"AnalogControl": {
"init_single": "",
"map_init": "",
"typename": "",
"direction": "in",
"index": 0,
"updaterate": "",
"mapping": [
{
"name": "{name}",
"get": "({class_name}.som.knob[{index}].Value())",
"range": [
0,
1
],
"bool": false
}
]
},
"Expression": {
"map_init": "",
"typename": "",
"direction": "in",
"mapping": [
{
"name": "{name}",
"get": "({class_name}.som.GetExpressionValue())"
}
]
},
"Led": {
"map_init": "",
"typename": "",
"direction": "out",
"index": 2,
"postprocess": "",
"mapping": [
{
"name": "{name}",
"set": "{class_name}.som.led[{index}].Set({value});"
}
]
},
"RgbLed": {
"map_init": "",
"typename": "",
"direction": "out",
"index": "r,g,b",
"mapping": [
{
"name": "{name}",
"set": "{class_name}.som.led[{index_r}].Set({value}); \n{class_name}.som.led[{index_g}].Set({value}); \n{class_name}.som.led[{index_b}].Set({value});"
},
{
"name": "{name}_red",
"set": "{class_name}.som.led[{index_r}].Set({value});"
},
{
"name": "{name}_green",
"set": "{class_name}.som.led[{index_g}].Set({value});"
},
{
"name": "{name}_blue",
"set": "{class_name}.som.led[{index_b}].Set({value});"
},
{
"name": "{name}_white",
"set": "{class_name}.som.led[{index_r}].Set({value}); \n{class_name}.som.led[{index_g}].Set({value}); \n{class_name}.som.led[{index_b}].Set({value});"
}
]
},
"Relay": {
"map_init": "",
"typename": "",
"direction": "out",
"mapping": [
{
"name": "{name}",
"set": "{class_name}.som.SetBypassState({value});"
}
]
}
} hvcc-0.16.0/hvcc/generators/c2daisy/json2daisy/resources/field.json 0000644 0000000 0000000 00000015425 00000000000 022126 0 ustar 00 {
"name": "field",
"som": "seed",
"defines": {
"HAS_MIDI": 1
},
"display": {},
"audio": {
"channels": 2
},
"parents": {
"i2c": {
"component": "i2c",
"pin": {
"scl": 11,
"sda": 12
}
},
"led_driver": {
"component": "PCA9685",
"address": "{0x00, 0x02}",
"parent": "i2c",
"driver_count": 2
},
"pad_shift": {
"component": "CD4021",
"driver_count": 2,
"pin": {
"clk": 28,
"cs": 27,
"data": 26
}
},
"pot_mux": {
"component": "CD4051",
"mux_count": 8,
"pin": {
"adc": 16,
"sel0": 21,
"sel1": 20,
"sel2": 19
}
}
},
"components": {
"sw1": {
"component": "Switch",
"pin": 30
},
"sw2": {
"component": "Switch",
"pin": 29
},
"cv1": {
"component": "AnalogControlBipolar",
"pin": 17
},
"cv2": {
"component": "AnalogControlBipolar",
"pin": 18
},
"cv3": {
"component": "AnalogControlBipolar",
"pin": 25
},
"cv4": {
"component": "AnalogControlBipolar",
"pin": 24
},
"knob1": {
"component": "CD4051AnalogControl",
"index": 0,
"parent": "pot_mux"
},
"knob2": {
"component": "CD4051AnalogControl",
"index": 3,
"parent": "pot_mux"
},
"knob3": {
"component": "CD4051AnalogControl",
"index": 1,
"parent": "pot_mux"
},
"knob4": {
"component": "CD4051AnalogControl",
"index": 4,
"parent": "pot_mux"
},
"knob5": {
"component": "CD4051AnalogControl",
"index": 2,
"parent": "pot_mux"
},
"knob6": {
"component": "CD4051AnalogControl",
"index": 5,
"parent": "pot_mux"
},
"knob7": {
"component": "CD4051AnalogControl",
"index": 6,
"parent": "pot_mux"
},
"knob8": {
"component": "CD4051AnalogControl",
"index": 7,
"parent": "pot_mux"
},
"cvout1": {
"component": "CVOuts"
},
"cvout2": {
"component": "CVOuts"
},
"gatein": {
"component": "GateIn",
"pin": 0
},
"gateout": {
"component": "GateOut",
"pin": 15
},
"pada1": {
"component": "CD4021Switch",
"parent": "pad_shift",
"index": 15
},
"pada2": {
"component": "CD4021Switch",
"parent": "pad_shift",
"index": 14
},
"pada3": {
"component": "CD4021Switch",
"parent": "pad_shift",
"index": 13
},
"pada4": {
"component": "CD4021Switch",
"parent": "pad_shift",
"index": 12
},
"pada5": {
"component": "CD4021Switch",
"parent": "pad_shift",
"index": 11
},
"pada6": {
"component": "CD4021Switch",
"parent": "pad_shift",
"index": 10
},
"pada7": {
"component": "CD4021Switch",
"parent": "pad_shift",
"index": 9
},
"pada8": {
"component": "CD4021Switch",
"parent": "pad_shift",
"index": 8
},
"padb1": {
"component": "CD4021Switch",
"parent": "pad_shift",
"index": 7
},
"padb2": {
"component": "CD4021Switch",
"parent": "pad_shift",
"index": 6
},
"padb3": {
"component": "CD4021Switch",
"parent": "pad_shift",
"index": 5
},
"padb4": {
"component": "CD4021Switch",
"parent": "pad_shift",
"index": 4
},
"padb5": {
"component": "CD4021Switch",
"parent": "pad_shift",
"index": 3
},
"padb6": {
"component": "CD4021Switch",
"parent": "pad_shift",
"index": 2
},
"padb7": {
"component": "CD4021Switch",
"parent": "pad_shift",
"index": 1
},
"padb8": {
"component": "CD4021Switch",
"parent": "pad_shift",
"index": 0
},
"led_key_b1": {
"component": "PCA9685Led",
"index": 15,
"parent": "led_driver"
},
"led_key_b2": {
"component": "PCA9685Led",
"index": 14,
"parent": "led_driver"
},
"led_key_b3": {
"component": "PCA9685Led",
"index": 13,
"parent": "led_driver"
},
"led_key_b4": {
"component": "PCA9685Led",
"index": 12,
"parent": "led_driver"
},
"led_key_b5": {
"component": "PCA9685Led",
"index": 11,
"parent": "led_driver"
},
"led_key_b6": {
"component": "PCA9685Led",
"index": 10,
"parent": "led_driver"
},
"led_key_b7": {
"component": "PCA9685Led",
"index": 9,
"parent": "led_driver"
},
"led_key_b8": {
"component": "PCA9685Led",
"index": 8,
"parent": "led_driver"
},
"led_key_a1": {
"component": "PCA9685Led",
"index": 0,
"parent": "led_driver"
},
"led_key_a2": {
"component": "PCA9685Led",
"index": 1,
"parent": "led_driver"
},
"led_key_a3": {
"component": "PCA9685Led",
"index": 2,
"parent": "led_driver"
},
"led_key_a4": {
"component": "PCA9685Led",
"index": 3,
"parent": "led_driver"
},
"led_key_a5": {
"component": "PCA9685Led",
"index": 4,
"parent": "led_driver"
},
"led_key_a6": {
"component": "PCA9685Led",
"index": 5,
"parent": "led_driver"
},
"led_key_a7": {
"component": "PCA9685Led",
"index": 6,
"parent": "led_driver"
},
"led_key_a8": {
"component": "PCA9685Led",
"index": 7,
"parent": "led_driver"
},
"led_knob_1": {
"component": "PCA9685Led",
"index": 16,
"parent": "led_driver"
},
"led_knob_2": {
"component": "PCA9685Led",
"index": 17,
"parent": "led_driver"
},
"led_knob_3": {
"component": "PCA9685Led",
"index": 18,
"parent": "led_driver"
},
"led_knob_4": {
"component": "PCA9685Led",
"index": 19,
"parent": "led_driver"
},
"led_knob_5": {
"component": "PCA9685Led",
"index": 20,
"parent": "led_driver"
},
"led_knob_6": {
"component": "PCA9685Led",
"index": 21,
"parent": "led_driver"
},
"led_knob_7": {
"component": "PCA9685Led",
"index": 22,
"parent": "led_driver"
},
"led_knob_8": {
"component": "PCA9685Led",
"index": 23,
"parent": "led_driver"
}
},
"aliases": {
"cvout": "cvout1",
"switch": "sw1",
"button": "sw1",
"switch1": "sw1",
"button1": "sw1",
"switch2": "sw2",
"button2": "sw2",
"encswitch": "encoder_rise",
"enp": "encoder_press",
"switch3": "encoder_rise",
"press": "encoder_press",
"knob": "knob1",
"ctrl": "knob1",
"ctrl1": "knob1",
"ctrl2": "knob2",
"led": "led1"
}
}
hvcc-0.16.0/hvcc/generators/c2daisy/json2daisy/resources/patch.json 0000644 0000000 0000000 00000002573 00000000000 022142 0 ustar 00 {
"name": "patch",
"som": "seed",
"defines": {
"HAS_MIDI": 1
},
"display": {},
"audio": {
"channels": 4
},
"external_codecs": [
{
"periph": "SAI_2",
"a_sync": "SLAVE",
"b_sync": "MASTER",
"a_dir": "TRANSMIT",
"b_dir": "RECEIVE",
"pin": {
"fs": 27,
"mclk": 24,
"sck": 28,
"sb": 25,
"sa": 26
}
}
],
"components": {
"knob1": {
"component": "AnalogControl",
"pin": 15,
"flip": "true"
},
"knob2": {
"component": "AnalogControl",
"pin": 16,
"flip": "true"
},
"knob3": {
"component": "AnalogControl",
"pin": 21,
"flip": "true"
},
"knob4": {
"component": "AnalogControl",
"pin": 18,
"flip": "true"
},
"encoder": {
"component": "Encoder",
"pin": {
"a": 12,
"b": 11,
"click": 0
}
},
"gateout": {
"component": "GateOut",
"pin": 17,
"display": ""
},
"cvout1": {
"component": "CVOuts"
},
"cvout2": {
"component": "CVOuts"
},
"gatein1": {
"component": "GateIn",
"pin": 20
},
"gatein2": {
"component": "GateIn",
"pin": 19
}
},
"aliases": {
"gate": "gatein1",
"gate1": "gatein1",
"gate2": "gatein2",
"cvout": "cvout1",
"encswitch": "encoder_rise",
"enp": "encoder_press",
"press": "encoder_press",
"knob": "knob1",
"ctrl": "knob1",
"ctrl1": "knob1",
"ctrl2": "knob2",
"ctrl3": "knob3",
"ctrl4": "knob4"
}
}
hvcc-0.16.0/hvcc/generators/c2daisy/json2daisy/resources/patch_init.json 0000644 0000000 0000000 00000004601 00000000000 023157 0 ustar 00 {
"name": "patch_init",
"som": "patch_sm",
"defines": {},
"audio": {
"channels": 2
},
"components": {
"cv_1": {
"component": "AnalogControl",
"pin": "C5",
"default": true
},
"cv_2": {
"component": "AnalogControl",
"pin": "C4",
"default": true
},
"cv_3": {
"component": "AnalogControl",
"pin": "C3",
"default": true
},
"cv_4": {
"component": "AnalogControl",
"pin": "C2",
"default": true
},
"cv_5": {
"component": "AnalogControl",
"pin": "C6",
"default": true
},
"cv_6": {
"component": "AnalogControl",
"pin": "C7",
"default": true
},
"cv_7": {
"component": "AnalogControl",
"pin": "C8",
"default": true
},
"cv_8": {
"component": "AnalogControl",
"pin": "C9",
"default": true
},
"adc_9": {
"component": "AnalogControl",
"pin": "A2",
"default": true
},
"adc_10": {
"component": "AnalogControl",
"pin": "A3",
"default": true
},
"adc_11": {
"component": "AnalogControl",
"pin": "D9",
"default": true
},
"adc_12": {
"component": "AnalogControl",
"pin": "D8",
"default": true
},
"gate_out_1": {
"component": "GateOut",
"pin": "B5",
"display": "",
"default": true
},
"gate_out_2": {
"component": "GateOut",
"pin": "B6",
"display": "",
"default": true
},
"cvout1": {
"component": "CVOuts",
"default": true
},
"cvout2": {
"component": "CVOuts",
"default": true
},
"gate_in_1": {
"component": "GateIn",
"pin": "B10",
"default": true
},
"gate_in_2": {
"component": "GateIn",
"pin": "B9",
"default": true
},
"sw1": {
"component": "Switch",
"pin": "B7"
},
"sw2": {
"component": "Switch",
"pin": "B8"
}
},
"aliases": {
"switch": "sw1",
"switch1": "sw1",
"switch2": "sw2",
"button": "sw1",
"toggle": "sw2",
"gate": "gate_in_1",
"gate1": "gate_in_1",
"gate2": "gate_in_2",
"gateout": "gate_out_1",
"gateout1": "gate_out_1",
"gateout2": "gate_out_2",
"cvout": "cvout1",
"cv_out_1": "cvout1",
"cv_out_2": "cvout2",
"knob": "cv_1",
"knob1": "cv_1",
"knob2": "cv_2",
"knob3": "cv_3",
"knob4": "cv_4",
"knob5": "cv_5",
"knob6": "cv_6",
"knob7": "cv_7",
"knob8": "cv_8",
"ctrl": "cv_1",
"ctrl1": "cv_1",
"ctrl2": "cv_2",
"ctrl3": "cv_3",
"ctrl4": "cv_4",
"ctrl5": "cv_5",
"ctrl6": "cv_6",
"ctrl7": "cv_7",
"ctrl8": "cv_8"
}
}
hvcc-0.16.0/hvcc/generators/c2daisy/json2daisy/resources/petal.json 0000644 0000000 0000000 00000007252 00000000000 022147 0 ustar 00 {
"name": "petal",
"som": "seed",
"parents": {
"i2c": {
"component": "i2c",
"pin": {
"scl": 11,
"sda": 12
}
},
"led_driver": {
"component": "PCA9685",
"address": "{0x00, 0x01}",
"parent": "i2c",
"driver_count": 2
}
},
"audio": {
"channels": 2
},
"components": {
"sw1": {
"component": "Switch",
"pin": 8
},
"sw2": {
"component": "Switch",
"pin": 9
},
"sw3": {
"component": "Switch",
"pin": 10
},
"sw4": {
"component": "Switch",
"pin": 13
},
"sw5": {
"component": "Switch",
"pin": 25
},
"sw6": {
"component": "Switch",
"pin": 26
},
"sw7": {
"component": "Switch",
"pin": 7
},
"encoder": {
"component": "Encoder",
"pin": {
"a": 28,
"b": 27,
"click": 14
}
},
"knob1": {
"component": "AnalogControl",
"pin": 16
},
"knob2": {
"component": "AnalogControl",
"pin": 19
},
"knob3": {
"component": "AnalogControl",
"pin": 17
},
"knob4": {
"component": "AnalogControl",
"pin": 20
},
"knob5": {
"component": "AnalogControl",
"pin": 18
},
"knob6": {
"component": "AnalogControl",
"pin": 21
},
"expression": {
"component": "AnalogControl",
"pin": 15
},
"led_ring_1": {
"component": "PCA9685RgbLed",
"index": {
"red": 0,
"green": 1,
"blue": 2
},
"parent": "led_driver"
},
"led_ring_2": {
"component": "PCA9685RgbLed",
"index": {
"red": 6,
"green": 7,
"blue": 8
},
"parent": "led_driver"
},
"led_ring_3": {
"component": "PCA9685RgbLed",
"index": {
"red": 12,
"green": 13,
"blue": 14
},
"parent": "led_driver"
},
"led_ring_4": {
"component": "PCA9685RgbLed",
"index": {
"red": 16,
"green": 17,
"blue": 18
},
"parent": "led_driver"
},
"led_ring_5": {
"component": "PCA9685RgbLed",
"index": {
"red": 3,
"green": 4,
"blue": 5
},
"parent": "led_driver"
},
"led_ring_6": {
"component": "PCA9685RgbLed",
"index": {
"red": 9,
"green": 10,
"blue": 11
},
"parent": "led_driver"
},
"led_ring_7": {
"component": "PCA9685RgbLed",
"index": {
"red": 19,
"green": 20,
"blue": 21
},
"parent": "led_driver"
},
"led_ring_8": {
"component": "PCA9685RgbLed",
"index": {
"red": 22,
"green": 23,
"blue": 24
},
"parent": "led_driver"
},
"led_fs_1": {
"component": "PCA9685Led",
"index": 15,
"parent": "led_driver"
},
"led_fs_2": {
"component": "PCA9685Led",
"index": 25,
"parent": "led_driver"
},
"led_fs_3": {
"component": "PCA9685Led",
"index": 26,
"parent": "led_driver"
},
"led_fs_4": {
"component": "PCA9685Led",
"index": 27,
"parent": "led_driver"
}
},
"aliases": {
"switch": "sw1",
"switch1": "sw1",
"switch2": "sw2",
"switch3": "sw3",
"switch4": "sw4",
"switch5": "sw5",
"switch6": "sw6",
"switch7": "sw7",
"encswitch": "encoder_rise",
"enp": "encoder_press",
"press": "encoder_press",
"knob": "knob1",
"ctrl": "knob1",
"ctrl1": "knob1",
"ctrl2": "knob2",
"ctrl3": "knob3",
"ctrl4": "knob4",
"ctrl5": "knob5",
"ctrl6": "knob6"
}
}
hvcc-0.16.0/hvcc/generators/c2daisy/json2daisy/resources/petal_125b_sm.json 0000644 0000000 0000000 00000003612 00000000000 023373 0 ustar 00 {
"name": "petal_125b_sm",
"som": "petal_125b_sm",
"audio": {
"channels": 2
},
"components": {
"knob_1": {
"component": "AnalogControl",
"index": 0,
"is_default": true
},
"knob_2": {
"component": "AnalogControl",
"index": 1,
"is_default": true
},
"knob_3": {
"component": "AnalogControl",
"index": 2,
"is_default": true
},
"knob_4": {
"component": "AnalogControl",
"index": 3,
"is_default": true
},
"knob_5": {
"component": "AnalogControl",
"index": 4,
"is_default": true
},
"knob_6": {
"component": "AnalogControl",
"index": 5,
"is_default": true
},
"expression": {
"component": "Expression",
"is_default": true
},
"toggle_1": {
"component": "Switch3",
"index": 0,
"is_default": true
},
"toggle_2": {
"component": "Switch3",
"index": 1,
"is_default": true
},
"toggle_3": {
"component": "Switch3",
"index": 2,
"is_default": true
},
"footswitch_1": {
"component": "Switch",
"index": 1,
"is_default": true
},
"footswitch_2": {
"component": "Switch",
"index": 2,
"is_default": true
},
"led1": {
"component": "RgbLed",
"index": {"r": 0, "g": 1, "b": 2},
"is_default": true
},
"led2": {
"component": "Led",
"index": 3,
"is_default": true
},
"led3": {
"component": "RgbLed",
"index": {"r": 4, "g": 5, "b": 6},
"is_default": true
},
"relay": {
"component": "Relay",
"is_default": true
}
},
"aliases": {
"knob1": "knob_1",
"knob2": "knob_2",
"knob3": "knob_3",
"knob4": "knob_4",
"knob5": "knob_5",
"knob6": "knob_6",
"sw1": "toggle_1",
"sw2": "toggle_2",
"sw3": "toggle_3"
}
}
hvcc-0.16.0/hvcc/generators/c2daisy/json2daisy/resources/pod.json 0000644 0000000 0000000 00000001743 00000000000 021623 0 ustar 00 {
"name": "pod",
"som": "seed",
"defines": {
"HAS_MIDI": 1
},
"display": {},
"audio": {
"channels": 2
},
"components": {
"sw1": {
"component": "Switch",
"pin": 27
},
"sw2": {
"component": "Switch",
"pin": 28
},
"knob1": {
"component": "AnalogControl",
"pin": 21
},
"knob2": {
"component": "AnalogControl",
"pin": 15
},
"encoder": {
"component": "Encoder",
"pin": {
"a": 26,
"b": 25,
"click": 13
}
},
"led1": {
"component": "RgbLed",
"pin": {
"r": 20,
"g": 19,
"b": 18
}
},
"led2": {
"component": "RgbLed",
"pin": {
"r": 17,
"g": 24,
"b": 23
}
}
},
"aliases": {
"switch": "sw1",
"button": "sw1",
"switch1": "sw1",
"button1": "sw1",
"switch2": "sw2",
"button2": "sw2",
"encswitch": "encoder_rise",
"enp": "encoder_press",
"press": "encoder_press",
"knob": "knob1",
"ctrl": "knob1",
"ctrl1": "knob1",
"ctrl2": "knob2",
"led": "led1"
}
}
hvcc-0.16.0/hvcc/generators/c2daisy/json2daisy/templates/daisy.h 0000644 0000000 0000000 00000017360 00000000000 021416 0 ustar 00 {{license}}
#ifndef __JSON2DAISY_{{name|upper}}_H__
#define __JSON2DAISY_{{name|upper}}_H__
{% if som == 'seed' %}
#include "daisy_seed.h"
#include "dev/codec_ak4556.h"
{% elif som == 'patch_sm' %}
#include "daisy_patch_sm.h"
{% elif som == 'petal_125b_sm' %}
#include "daisy_petal_125b_sm.h"
{% endif %}
{{display_conditional}}
#define ANALOG_COUNT {{analogcount}}
namespace json2daisy {
{{non_class_declarations}}
struct Daisy{{ name|capitalize }} {
/** Initializes the board according to the JSON board description
* \param boost boosts the clock speed from 400 to 480 MHz
*/
void Init(bool boost=true)
{
{% if som == 'seed' %}
som.Configure();
som.Init(boost);
{% else %}
som.Init();
{% endif %}
{% if init != '' %}
{{init}}
{% endif %}
{% if i2c != '' %}
// i2c
{{ i2c }}
{% endif %}
{% if PCA9685 != '' %}
// LED Drivers
{{ PCA9685 }}
{% endif %}
{% if Switch != '' %}
// Switches
{{ Switch }}
{% endif %}
{% if Switch3 != '' %}
// SPDT Switches
{{ Switch3 }}
{% endif %}
{% if CD4021 != '' %}
// Muxes
{{ CD4021 }}
{% endif %}
{% if GateIn != '' %}
// Gate ins
{{ GateIn }}
{% endif %}
{% if Encoder != '' %}
// Rotary encoders
{{ Encoder }}
{% endif %}
{% if init_single != '' %}
// Single channel ADC initialization
{{ init_single }}
{% endif %}
{% if som == 'seed' %}
{% if analogcount > 0 %}
som.adc.Init(cfg, ANALOG_COUNT);
{% endif %}
{% endif %}
{% if ctrl_init != '' %}
// AnalogControl objects
{{ ctrl_init }}
{% endif %}
{% if CD4051AnalogControl != '' %}
// Multiplexed AnlogControl objects
{{ CD4051AnalogControl }}
{% endif %}
{% if Led != '' %}
// LEDs
{{ Led }}
{% endif %}
{% if RgbLed != '' %}
// RBG LEDs
{{ RgbLed }}
{% endif %}
{% if GateOut != '' %}
// Gate outs
{{ GateOut }}
{% endif %}
{% if CVOuts != '' %}
// DAC
{{ CVOuts }}
{% endif %}
{% if display != '' %}
// Display
{{ display }}
{% endif %}
{% if Tlv493d != '' %}
// Accelerometer
{{ Tlv493d }}
{% endif %}
{% if Mpr121 != '' %}
// Capacitive sensor
{{ Mpr121 }}
{% endif %}
{% if Apds9960 != '' %}
// Gesture / color sensor
{{ Apds9960 }}
{% endif %}
{% if Dps310 != '' %}
// Dps310 pressure / temperature sensor
{{ Dps310 }}
{% endif %}
{% if NeoTrellis != '' %}
// Adafruit Neo Trellis
{{ NeoTrellis }}
{% endif %}
{% if Icm20948 != '' %}
// Icm20948 9-DOF sensor
{{ Icm20948 }}
{% endif %}
{% if som == 'seed' and external_codecs|length > 0 %}
// External Codec Initialization
daisy::SaiHandle::Config sai_config[{{ 1 + external_codecs|length }}];
// Internal Codec
sai_config[0].periph = daisy::SaiHandle::Config::Peripheral::SAI_1;
sai_config[0].sr = daisy::SaiHandle::Config::SampleRate::SAI_48KHZ;
sai_config[0].bit_depth = daisy::SaiHandle::Config::BitDepth::SAI_24BIT;
sai_config[0].a_sync = daisy::SaiHandle::Config::Sync::MASTER;
sai_config[0].b_sync = daisy::SaiHandle::Config::Sync::SLAVE;
sai_config[0].a_dir = daisy::SaiHandle::Config::Direction::TRANSMIT;
sai_config[0].b_dir = daisy::SaiHandle::Config::Direction::RECEIVE;
sai_config[0].pin_config.fs = daisy::Pin(daisy::PORTE, 4);
sai_config[0].pin_config.mclk = daisy::Pin(daisy::PORTE, 2);
sai_config[0].pin_config.sck = daisy::Pin(daisy::PORTE, 5);
sai_config[0].pin_config.sa = daisy::Pin(daisy::PORTE, 6);
sai_config[0].pin_config.sb = daisy::Pin(daisy::PORTE, 3);
{% for codec in external_codecs %}
sai_config[{{loop.index}}].periph = daisy::SaiHandle::Config::Peripheral::{{codec.periph}};
sai_config[{{loop.index}}].sr = daisy::SaiHandle::Config::SampleRate::SAI_48KHZ;
sai_config[{{loop.index}}].bit_depth = daisy::SaiHandle::Config::BitDepth::SAI_24BIT;
sai_config[{{loop.index}}].a_sync = daisy::SaiHandle::Config::Sync::{{codec.a_sync}};
sai_config[{{loop.index}}].b_sync = daisy::SaiHandle::Config::Sync::{{codec.b_sync}};
sai_config[{{loop.index}}].a_dir = daisy::SaiHandle::Config::Direction::{{codec.a_dir}};
sai_config[{{loop.index}}].b_dir = daisy::SaiHandle::Config::Direction::{{codec.b_dir}};
sai_config[{{loop.index}}].pin_config.fs = som.GetPin({{codec.pin.fs}});
sai_config[{{loop.index}}].pin_config.mclk = som.GetPin({{codec.pin.mclk}});
sai_config[{{loop.index}}].pin_config.sck = som.GetPin({{codec.pin.sck}});
sai_config[{{loop.index}}].pin_config.sa = som.GetPin({{codec.pin.sa}});
sai_config[{{loop.index}}].pin_config.sb = som.GetPin({{codec.pin.sb}});
{% endfor %}
daisy::SaiHandle sai_handle[{{ 1 + external_codecs|length }}];
sai_handle[0].Init(sai_config[0]);
{% for codec in external_codecs %}
sai_handle[{{loop.index}}].Init(sai_config[{{loop.index}}]);
{% endfor %}
daisy::Pin codec_reset_pin = som.GetPin(29);
daisy::Ak4556 codec;
codec.Init(codec_reset_pin);
daisy::AudioHandle::Config cfg;
cfg.blocksize = 48;
cfg.samplerate = daisy::SaiHandle::Config::SampleRate::SAI_48KHZ;
cfg.postgain = 0.5f;
som.audio_handle.Init(
cfg,
sai_handle[0]
{% for codec in external_codecs %}
,sai_handle[{{loop.index}}]
{% endfor %}
);
{% endif %}
{% if som == 'seed' %}
{% if analogcount > 0 %}
som.adc.Start();
{% endif %}
{% endif %}
}
/** Handles all the controls processing that needs to occur at the block rate
*
*/
void ProcessAllControls()
{
{% if process != '' %}
{{ process }}
{% endif %}
{% if som != 'seed' %}
som.ProcessAllControls();
{% endif %}
}
/** Handles all the maintenance processing. This should be run last within the audio callback.
*
*/
void PostProcess()
{
{{postprocess}}
{% if som == 'petal_125b_sm' %}
som.UpdateLeds();
{% endif %}
}
/** Handles processing that shouldn't occur in the audio block, such as blocking transfers
*
*/
void LoopProcess()
{
{{loopprocess}}
}
/** Sets the audio sample rate
* \param sample_rate the new sample rate in Hz
*/
void SetAudioSampleRate(size_t sample_rate)
{
{% if som == 'seed' or som == 'petal_125b_sm' %}
daisy::SaiHandle::Config::SampleRate enum_rate;
if (sample_rate >= 96000)
enum_rate = daisy::SaiHandle::Config::SampleRate::SAI_96KHZ;
else if (sample_rate >= 48000)
enum_rate = daisy::SaiHandle::Config::SampleRate::SAI_48KHZ;
else if (sample_rate >= 32000)
enum_rate = daisy::SaiHandle::Config::SampleRate::SAI_32KHZ;
else if (sample_rate >= 16000)
enum_rate = daisy::SaiHandle::Config::SampleRate::SAI_16KHZ;
else
enum_rate = daisy::SaiHandle::Config::SampleRate::SAI_8KHZ;
som.SetAudioSampleRate(enum_rate);
{% elif som == 'patch_sm' %}
som.SetAudioSampleRate(sample_rate);
{% endif %}
{{hidupdaterates}}
}
/** Sets the audio block size
* \param block_size the new block size in words
*/
inline void SetAudioBlockSize(size_t block_size)
{
som.SetAudioBlockSize(block_size);
}
/** Starts up the audio callback process with the given callback
*
*/
inline void StartAudio(daisy::AudioHandle::AudioCallback cb)
{
som.StartAudio(cb);
}
/** This is the board's "System On Module" */
{{som_class}} som;
{% if som == 'seed' %}
daisy::AdcChannelConfig cfg[ANALOG_COUNT];
{% endif %}
// I/O Components
{{comps}}
{{dispdec}}
{{midi}}
};
} // namspace json2daisy
#endif // __JSON2DAISY_{{name|upper}}_H__
hvcc-0.16.0/hvcc/generators/c2daisy/parameters.py 0000644 0000000 0000000 00000024566 00000000000 016574 0 ustar 00 import json
from copy import deepcopy
from typing import Any, Dict, Optional
from hvcc.core.hv2ir.HeavyLangObject import HeavyLangObject
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
def display_parameters(description_file: str) -> dict[str, str]:
"""
Optional list of externed parameters that will be filtered out of the hardware configuration.
These will be passed on to the sendHook callback and can be used by the Display function.
"""
with open(description_file, 'rb') as file:
daisy_description = json.load(file)
try:
params = {
param: "0x{0:X}".format(HeavyLangObject.get_hash(param))
for param in daisy_description['display']['params']
}
except KeyError:
return {}
return params
def display_processor(description_file: Optional[str] = None) -> str:
"""
Try to load display processing code from file.
"""
if description_file is None:
raise ValueError('description_file not provided')
with open(description_file, 'rb') as file:
daisy_description = json.load(file)
process_file = daisy_description['display']['process_file']
with open(process_file, 'rb') as f:
return f.read().decode('utf-8')
hvcc-0.16.0/hvcc/generators/c2daisy/static/README.md 0000644 0000000 0000000 00000017253 00000000000 016620 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 | --- |
hvcc-0.16.0/hvcc/generators/c2daisy/templates/HeavyDaisy.cpp 0000644 0000000 0000000 00000037626 00000000000 020630 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 %}
{% for k, v in display_params.items() %}
#define HV_HASH_{{k|upper}} {{v}}
{% endfor %}
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 %}
{% for k, v in display_params.items() %}
float f{{k}};
{% endfor %}
{% 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 %}
{% if display_params|length > 0 %}
void HandleDisplayParams(uint32_t sendHash, const HvMessage *m)
{
switch(sendHash){
{% for k, v in display_params.items() %}
case HV_HASH_{{k|upper}}:
f{{k}} = msg_getFloat(m, 0);
break;
{% endfor %}
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 display_params|length > 0 %}
HandleDisplayParams(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() {
{{display_process}}
}
hvcc-0.16.0/hvcc/generators/c2daisy/templates/Makefile 0000644 0000000 0000000 00000001032 00000000000 017474 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
CFLAGS += -DHV_BARE_METAL
hvcc-0.16.0/hvcc/generators/c2dpf/__init__.py 0000644 0000000 0000000 00000000000 00000000000 015601 0 ustar 00 hvcc-0.16.0/hvcc/generators/c2dpf/c2dpf.py 0000644 0000000 0000000 00000020215 00000000000 015052 0 ustar 00 # Copyright (C) 2021-2026 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 jinja2
import shutil
import time
from typing import Optional
from pathlib import Path
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, DPFUIType, DPFUISize
from .nanovg_render import open_gui_json, nanovg_render
class c2dpf(Generator):
""" Generates a DPF wrapper for a given patch.
"""
@classmethod
def compile(
cls,
c_src_dir: Path,
out_dir: Path,
externs: ExternInfo,
patch_name: str,
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 = Path(out_dir, "plugin")
receiver_list = externs.parameters.inParam
sender_list = externs.parameters.outParam
event_list = externs.events.inEvent
out_event_list = externs.events.outEvent
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 = out_dir.absolute()
if out_dir.exists():
shutil.rmtree(out_dir)
# copy over static files
shutil.copytree(Path(Path(__file__).parent, "static"), out_dir)
shutil.copy(Path(Path(__file__).parent, "static/README.md"), f'{out_dir}/../')
# copy over generated C source files
source_dir = Path(out_dir, "source")
shutil.copytree(c_src_dir, source_dir)
if dpf_meta.enable_ui == DPFUIType.NANOVG:
gui_json = open_gui_json(patch_name, c_src_dir)
dpf_meta.ui_size = DPFUISize(
width=gui_json.size.x,
height=gui_json.size.y
)
# initialize the jinja template environment
env = jinja2.Environment()
env.filters["uniqueid"] = filter_uniqueid
env.loader = jinja2.FileSystemLoader(
Path(Path(__file__).parent, "templates"))
# generate DPF wrapper from template
dpf_h_path = Path(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,
events=event_list,
out_events=out_event_list,
copyright=copyright_c))
dpf_cpp_path = Path(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,
events=event_list,
out_events=out_event_list,
pool_sizes_kb=externs.memoryPoolSizesKb,
copyright=copyright_c))
if dpf_meta.enable_ui == DPFUIType.IMGUI:
dpf_ui_path = Path(source_dir, f"HeavyDPF_{patch_name}_UI.cpp")
with open(dpf_ui_path, "w") as f:
f.write(env.get_template("HeavyDPF_ImGui_UI.cpp").render(
name=patch_name,
meta=dpf_meta,
class_name=f"HeavyDPF_{patch_name}",
receivers=receiver_list,
senders=sender_list,
copyright=copyright_c))
elif dpf_meta.enable_ui == DPFUIType.NANOVG:
gui_json, widgets, gui_objects_render = nanovg_render(
patch_name, c_src_dir, env, receiver_list, sender_list
)
dpf_ui_header = Path(source_dir, f"HeavyDPF_{patch_name}_UI.hpp")
with open(dpf_ui_header, "w") as f:
f.write(env.get_template("HeavyDPF_NanoVG_UI.hpp").render(
name=patch_name,
meta=dpf_meta,
class_name=f"HeavyDPF_{patch_name}_UI",
gui_json=gui_json,
widgets=widgets,
copyright=copyright_c))
dpf_ui_path = Path(source_dir, f"HeavyDPF_{patch_name}_UI.cpp")
with open(dpf_ui_path, "w") as f:
f.write(env.get_template("HeavyDPF_NanoVG_UI.cpp").render(
name=patch_name,
meta=dpf_meta,
class_name=f"HeavyDPF_{patch_name}_UI",
gui_json=gui_json,
widgets=widgets,
gui_objects=gui_objects_render,
receivers=receiver_list,
senders=sender_list,
events=event_list,
copyright=copyright_c))
dpf_h_path = Path(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(Path(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(Path(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=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
)
hvcc-0.16.0/hvcc/generators/c2dpf/nanovg_render.py 0000644 0000000 0000000 00000004245 00000000000 016710 0 ustar 00 import json
from pathlib import Path
import jinja2
from hvcc.types.GUI import Canvas, Comment, GraphRoot, Graph, GUIObjects
def open_gui_json(
patch_name: str,
c_src_dir: Path,
) -> GraphRoot:
# load GUI from json file
gui_json_path = Path(c_src_dir, "../ir/", f"{patch_name}.heavy.gui.json")
with open(gui_json_path, "r") as f:
gui_json = GraphRoot(**json.load(f))
return gui_json
def nanovg_render(
patch_name: str,
c_src_dir: Path,
env: jinja2.Environment,
recv_list: list,
send_list: list
) -> tuple[
GraphRoot,
dict[str, list[str]],
list[str]
]:
""" Generate nanovg components from the GUI json
"""
gui_json = open_gui_json(patch_name, c_src_dir)
# widget overview
widgets: dict[str, list[str]] = {
"graph": [],
"canvas": [],
"comment": [],
"bang": [],
"toggle": [],
"vradio": [],
"hradio": [],
"vslider": [],
"hslider": [],
"knob": [],
"number": [],
"float": []
}
# render gui objects
gui_objects_render = []
def generate_gui_objects(graphs: list[Graph], objects: list[GUIObjects], parent: str):
for w in objects:
widgets[w.type].append(w.id if isinstance(w, (Canvas, Comment)) else w.parameter)
gui_objects_render.append(env.get_template("gui_objects.cpp").render(
parent=parent,
gui_objects=objects,
receivers=recv_list,
senders=send_list
))
for graph in graphs:
widgets["graph"].append(graph.id)
gui_objects_render.append(
f"""
// subpatch
{graph.id} = new PDSubpatch({parent});
{graph.id}->setSize({graph.gop_size.x} * scaleFactor, {graph.gop_size.y} * scaleFactor);
{graph.id}->setAbsolutePos({graph.position.x} * scaleFactor, {graph.position.y} * scaleFactor);
{parent}->addManagedChild({graph.id});
"""
)
generate_gui_objects(graph.graphs, graph.objects, graph.id)
generate_gui_objects(gui_json.graphs, gui_json.objects, "mainPatch")
return gui_json, widgets, gui_objects_render
hvcc-0.16.0/hvcc/generators/c2dpf/static/README.md 0000644 0000000 0000000 00000002057 00000000000 016254 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.
hvcc-0.16.0/hvcc/generators/c2dpf/templates/DistrhoPluginInfo.h 0000644 0000000 0000000 00000005673 00000000000 021273 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.maker != None %}
#define DISTRHO_PLUGIN_BRAND "{{meta.maker}}"
{% else %}
#define DISTRHO_PLUGIN_BRAND "Heavy"
{%- endif %}
{%- if meta.brand_id != None %}
#define DISTRHO_PLUGIN_BRAND_ID {{meta.brand_id}}
{%- endif %}
{%- if meta.brand_id_no_vst3 is sameas true %}
#define DPF_VST3_DONT_USE_BRAND_ID 1
{%- endif %}
{%- if meta.unique_id != None %}
#define DISTRHO_PLUGIN_UNIQUE_ID {{meta.unique_id}}
{%- 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 > 0 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 > 0 %}
// if you are using a UI you'll probably want to modify these settings to your needs
{%- if meta.enable_ui == 1 %}
#define DISTRHO_UI_USE_CUSTOM 1
#define DISTRHO_UI_CUSTOM_INCLUDE_PATH "DearImGui.hpp"
#define DISTRHO_UI_CUSTOM_WIDGET_TYPE DGL_NAMESPACE::ImGuiTopLevelWidget
{%- elif meta.enable_ui == 2 %}
#define DISTRHO_UI_USE_NANOVG 1
{%- endif %}
{%- 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 %}
hvcc-0.16.0/hvcc/generators/c2dpf/templates/HeavyDPF.cpp 0000644 0000000 0000000 00000015535 00000000000 017623 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_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 + events -%}
{%- if v.attributes %}
_parameters[{{loop.index-1}}] = {{v.attributes.default}}f;
{%- else %}
_parameters[{{loop.index-1}}] = 0.0f;
{%- endif %}
{%- 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 + events %}
{% 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) or (events|length > 0) %}
switch (index) {
{%- for k, v in receivers + senders + events %}
case {{loop.index-1}}: {
{%- if v.type == "send" %}
// do nothing for {{k|upper}}
{%- elif v.extern == "event" %}
if (value == 1)
_context->sendBangToReceiver(Heavy_{{name}}::Event::In::{{k|upper}});
{%- else %}
_context->sendFloatToReceiver(
Heavy_{{name}}::Parameter::In::{{k|upper}},
value);
{%- endif %}
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.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
hvcc-0.16.0/hvcc/generators/c2dpf/templates/HeavyDPF.hpp 0000644 0000000 0000000 00000010402 00000000000 017614 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
#define HV_DPF_NUM_PARAMETER {{receivers|length + senders|length + events|length}}
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 + senders + events %}
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) or (events|length > 0) %}
// parameters
float _parameters[HV_DPF_NUM_PARAMETER];
{%- 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 #}
hvcc-0.16.0/hvcc/generators/c2dpf/templates/HeavyDPF_ImGui_UI.cpp 0000644 0000000 0000000 00000013136 00000000000 021305 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
hvcc-0.16.0/hvcc/generators/c2dpf/templates/HeavyDPF_NanoVG_UI.cpp 0000644 0000000 0000000 00000006123 00000000000 021421 0 ustar 00 {{copyright}}
#include "DistrhoUI.hpp"
#include "DistrhoPluginInfo.h"
#include "nanovg.h"
#include "{{class_name}}.hpp"
START_NAMESPACE_DISTRHO
// -----------------------------------------------------------------------------------------------------------
{%- if (receivers|length > 0) or (senders|length > 0) or (events|length > 0) %}
enum HeavyParams {
{%- for k, v in receivers + senders + events %}
k{{v.display|capitalize}},
{%- endfor %}
kParameterCount
};
{%- endif %}
{{class_name}}::{{class_name}}()
: UI(DISTRHO_UI_DEFAULT_WIDTH, DISTRHO_UI_DEFAULT_HEIGHT)
{
const float width = getWidth();
const float height = getHeight();
const double scaleFactor = getScaleFactor();
// mainpatch
mainPatch = new PDMainpatch(this);
mainPatch->setSize(width * scaleFactor, height * scaleFactor);
{%- for i in gui_objects -%}
{{i}}
{%- endfor %}
}
{{class_name}}::~{{class_name}}()
{
}
void {{class_name}}::onNanoDisplay()
{
const float width = getWidth();
const float height = getHeight();
const double scaleFactor = getScaleFactor();
NVGcontext* nvg = getContext();
nvgFillColor(nvg, Colors::cnvColor);
nvgBeginPath(nvg);
nvgRect(nvg, 0, 0, width * scaleFactor, height * scaleFactor);
nvgFill(nvg);
nvgStroke(nvg);
}
void {{class_name}}::parameterChanged(uint32_t index, float value)
{
switch (index)
{
{%- for k, v in receivers %}
case k{{v.display|capitalize}}:
{%- if v.attributes.type == "bool" %}
{{v.display|lower}}->setDown(static_cast(value));
{%- else %}
{{v.display|lower}}->setValue(value);
{%- endif %}
break;
{%- endfor %}
default:
break;
}
repaint();
}
void {{class_name}}::sliderValueChanged(SubWidget *const widget, float value)
{
// printf("value changed: %f\n", value);
const uint id = widget->getId();
setParameterValue(id, value);
}
void {{class_name}}::switchClicked(SubWidget *const widget, bool down)
{
// printf("switch clicked: %d\n", down);
const uint id = widget->getId();
setParameterValue(id, static_cast(down));
}
void {{class_name}}::bangClicked(SubWidget *const widget)
{
// printf("bang clicked\n");
const uint id = widget->getId();
setParameterValue(id, 1.0f);
}
void {{class_name}}::radioValueChanged(SubWidget *const widget, uint index)
{
// printf("radio clicked: %d\n", index);
const uint id = widget->getId();
setParameterValue(id, static_cast(index));
}
void {{class_name}}::numberValueChanged(SubWidget *const widget, float value)
{
// printf("number value changed: %f\n", value);
const uint id = widget->getId();
setParameterValue(id, value);
}
void {{class_name}}::knobValueChanged(SubWidget *const widget, float value)
{
// printf("knob value changed: %f\n", value);
const uint id = widget->getId();
setParameterValue(id, value);
}
UI *createUI()
{
return new {{class_name}}();
}
// -----------------------------------------------------------------------------------------------------------
END_NAMESPACE_DISTRHO
hvcc-0.16.0/hvcc/generators/c2dpf/templates/HeavyDPF_NanoVG_UI.hpp 0000644 0000000 0000000 00000004536 00000000000 021434 0 ustar 00 {{copyright}}
#include "DistrhoUI.hpp"
#include "DistrhoPluginInfo.h"
#include "nanovg.h"
#include "pdvg.hpp"
START_NAMESPACE_DISTRHO
class {{class_name}} : public UI,
public PDSliderEventHandler::Callback,
public PDToggleEventHandler::Callback,
public PDRadioEventHandler::Callback,
public PDNumberEventHandler::Callback,
public PDKnobEventHandler::Callback,
public PDBangEventHandler::Callback
{
public:
{{class_name}}();
~{{class_name}}();
protected:
void parameterChanged(uint32_t index, float value) override;
void onNanoDisplay() override;
void sliderValueChanged(SubWidget *const widget, float value) override;
void switchClicked(SubWidget *const widget, bool down) override;
void bangClicked(SubWidget *const widget) override;
void radioValueChanged(SubWidget *const widget, uint index) override;
void numberValueChanged(SubWidget *const widget, float value) override;
void knobValueChanged(SubWidget *const widget, float value) override;
private:
ScopedPointer mainPatch;
{%- for type in widgets.keys() %}
{%- for widget in widgets[type] %}
{%- if type == 'graph' %}
{%- set object = 'PDSubpatch' %}
{%- elif type == 'canvas' %}
{%- set object = 'PDCanvas' %}
{%- elif type == 'comment' %}
{%- set object = 'PDComment' %}
{%- elif type == 'bang' %}
{%- set object = 'PDBang' %}
{%- elif type == 'toggle' %}
{%- set object = 'PDToggle' %}
{%- elif type in ['vradio', 'hradio'] %}
{%- set object = 'PDRadio' %}
{%- elif type in ['vslider', 'hslider'] %}
{%- set object = 'PDSlider' %}
{%- elif type == 'knob' %}
{%- set object = 'PDKnob' %}
{%- elif type == 'number' %}
{%- set object = 'PDNumber' %}
{%- elif type == 'float' %}
{%- set object = 'PDFloat' %}
{%- endif %}
ScopedPointer<{{object}}> {{widget}};
{%- endfor %}
{%- endfor %}
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR({{class_name}})
};
END_NAMESPACE_DISTRHO
hvcc-0.16.0/hvcc/generators/c2dpf/templates/Makefile_plugin 0000644 0000000 0000000 00000003266 00000000000 020525 0 ustar 00 NAME = {{name}}
{%- if meta.enable_modgui is sameas true %}
MODGUI_CLASS_NAME = hv_{{name}}
{%- endif %}
{%- if meta.enable_ui > 0 %}
FILES_DSP = $(filter-out HeavyDPF_{{name}}_UI.cpp, $(wildcard *.cpp))
{%- else %}
FILES_DSP = $(wildcard *.cpp)
{%- endif %}
FILES_DSP += $(wildcard *.c)
{%- if meta.enable_ui > 0 %}
FILES_UI = HeavyDPF_{{name}}_UI.cpp
{%- if meta.enable_ui == 1 %}
FILES_UI += ../../{{dpf_path}}dpf-widgets/opengl/DearImGui.cpp
{%- elif meta.enable_ui == 2 %}
FILES_UI += $(wildcard ../../{{dpf_path}}pdvg/src/*.cpp)
FILES_UI += ../../{{dpf_path}}pdvg/src/Fonts/*.cpp
{%- endif %}
{%- 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 == 1 %}
BUILD_CXX_FLAGS += -I ../../{{dpf_path}}dpf-widgets/generic
BUILD_CXX_FLAGS += -I ../../{{dpf_path}}dpf-widgets/opengl
{%- elif meta.enable_ui == 2 %}
BUILD_CXX_FLAGS += -I ../../{{dpf_path}}dpf/dgl/src/nanovg
BUILD_CXX_FLAGS += -I ../../{{dpf_path}}pdvg
{%- 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)
hvcc-0.16.0/hvcc/generators/c2dpf/templates/Makefile_project 0000644 0000000 0000000 00000001562 00000000000 020672 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
hvcc-0.16.0/hvcc/generators/c2dpf/templates/gui_objects.cpp 0000644 0000000 0000000 00000023351 00000000000 020505 0 ustar 00 {%- for object in gui_objects -%}
{%- if object.type == 'canvas' %}
// canvas
{{object.id}} = new PDCanvas({{parent}});
{{object.id}}->setSize({{object.size.x}} * scaleFactor, {{object.size.y}} * scaleFactor);
{{object.id}}->setAbsolutePos({{object.position.x}} * scaleFactor, {{object.position.y}} * scaleFactor);
{{object.id}}->setColors(nvgRGB{{object.bg_color.as_rgb_tuple()}});
{%- if object.label != None %}
{{object.id}}->setLabel("{{object.label.text}}", nvgRGB{{object.label.color.as_rgb_tuple()}}, {{object.label.position.x}} * scaleFactor, {{object.label.position.y}} * scaleFactor, {{object.label.font_size}} * scaleFactor);
{%- endif %}
{%- elif object.type == 'comment' %}
// comment
{{object.id}} = new PDComment({{parent}});
std::string {{object.id}}String = "{{object.text}}";
{{object.id}}->setText({{object.id}}String);
{{object.id}}->setFontSize(15 * scaleFactor);
{{object.id}}->setAbsolutePos({{object.position.x}} * scaleFactor, {{object.position.y}} * scaleFactor);
{%- if object.width != None %}
{{object.id}}->setSize((7.4 * {{object.width}}) * scaleFactor, 15 * scaleFactor);
{%- else %}
{{object.id}}->setSize((7.4 * {{object.id}}String.length()) * scaleFactor, 15 * scaleFactor);
{%- endif -%}
{%- elif object.type == 'bang' %}
// bang
{{object.parameter}} = new PDBang({{parent}}, this);
{{object.parameter}}->setId(k{{object.parameter|capitalize}});
{{object.parameter}}->setSize({{object.size.x}} * scaleFactor, {{object.size.y}} * scaleFactor);
{{object.parameter}}->setAbsolutePos({{object.position.x}} * scaleFactor, {{object.position.y}} * scaleFactor);
{{object.parameter}}->setColors(
nvgRGB{{object.bg_color.as_rgb_tuple()}},
nvgRGB{{object.fg_color.as_rgb_tuple()}}
);
{{object.parameter}}->setInterval({{object.flash_time}});
{%- if object.label != None %}
{{object.parameter}}->setLabel("{{object.label.text}}", nvgRGB{{object.label.color.as_rgb_tuple()}}, {{object.label.position.x}} * scaleFactor, {{object.label.position.y}} * scaleFactor, {{object.label.font_size}} * scaleFactor);
{%- endif %}
{{parent}}->addManagedChild({{object.parameter}});
{%- elif object.type == 'toggle' %}
// toggle
{{object.parameter}} = new PDToggle({{parent}}, this);
{{object.parameter}}->setId(k{{object.parameter|capitalize}});
{{object.parameter}}->setSize({{object.size.x}} * scaleFactor, {{object.size.y}} * scaleFactor);
{{object.parameter}}->setAbsolutePos({{object.position.x}} * scaleFactor, {{object.position.y}} * scaleFactor);
{{object.parameter}}->setColors(
nvgRGB{{object.bg_color.as_rgb_tuple()}},
nvgRGB{{object.fg_color.as_rgb_tuple()}}
);
{{parent}}->addManagedChild({{object.parameter}});
{%- elif object.type in ['vradio', 'hradio'] %}
// {{object.type}}
{{object.parameter}} = new PDRadio({{parent}}, this);
{{object.parameter}}->setId(k{{object.parameter|capitalize}});
{{object.parameter}}->setSize({{object.size.x}} * scaleFactor, {{object.size.y}} * scaleFactor);
{{object.parameter}}->setAbsolutePos({{object.position.x}} * scaleFactor, {{object.position.y}} * scaleFactor);
{{object.parameter}}->setStep({{object.options}});
{%- if object.type == "hradio" %}
{{object.parameter}}->setHorizontal();
{%- endif %}
{{object.parameter}}->setColors(
nvgRGB{{object.bg_color.as_rgb_tuple()}},
nvgRGB{{object.fg_color.as_rgb_tuple()}}
);
{%- if object.label != None %}
{{object.parameter}}->setLabel("{{object.label.text}}", nvgRGB{{object.label.color.as_rgb_tuple()}}, {{object.label.position.x}} * scaleFactor, {{object.label.position.y}} * scaleFactor, {{object.label.font_size}} * scaleFactor);
{%- endif %}
{{parent}}->addManagedChild({{object.parameter}});
{%- elif object.type in ['vslider', 'hslider'] %}
// {{object.type}}
{{object.parameter}} = new PDSlider({{parent}}, this);
{{object.parameter}}->setId(k{{object.parameter|capitalize}});
{{object.parameter}}->setSize({{object.size.x}} * scaleFactor, {{object.size.y}} * scaleFactor);
{{object.parameter}}->setAbsolutePos({{object.position.x}} * scaleFactor, {{object.position.y}} * scaleFactor);
{{object.parameter}}->setSliderArea(0, 0, {{object.size.x}} * scaleFactor, {{object.size.y}} * scaleFactor);
{%- if object.type == "vslider" %}
{{object.parameter}}->setStartPos(0, {{object.size.y}} * scaleFactor);
{{object.parameter}}->setEndPos(0, 0);
{{object.parameter}}->setInverted(true);
{%- elif object.type == "hslider" %}
{{object.parameter}}->setStartPos(0, 0);
{{object.parameter}}->setEndPos({{object.size.x}} * scaleFactor, 0);
{{object.parameter}}->setHorizontal();
{%- endif %}
{{object.parameter}}->setRange({{object.min}}f, {{object.max}}f);
{%- for k, v in receivers + senders %}
{%- if v.display == object.parameter %}
{{object.parameter}}->setDefault({{v.attributes.default}}f);
{%- endif %}
{%- endfor %}
{{object.parameter}}->setUsingLogScale({{object.logarithmic|lower}});
{{object.parameter}}->setSteadyOnClick({{object.steady|lower}});
{{object.parameter}}->setColors(
nvgRGB{{object.bg_color.as_rgb_tuple()}},
nvgRGB{{object.fg_color.as_rgb_tuple()}}
);
{%- if object.label != None %}
{{object.parameter}}->setLabel("{{object.label.text}}", nvgRGB{{object.label.color.as_rgb_tuple()}}, {{object.label.position.x}} * scaleFactor, {{object.label.position.y}} * scaleFactor, {{object.label.font_size}} * scaleFactor);
{%- endif %}
{{parent}}->addManagedChild({{object.parameter}});
{%- elif object.type == 'knob' %}
// knob
{{object.parameter}} = new PDKnob({{parent}}, this);
{{object.parameter}}->setId(k{{object.parameter|capitalize}});
{{object.parameter}}->setSize({{object.size.x}} * scaleFactor, {{object.size.y}} * scaleFactor);
{{object.parameter}}->setAbsolutePos({{object.position.x}} * scaleFactor, {{object.position.y}} * scaleFactor);
{{object.parameter}}->setKnobArea(0.0f, 0.0f, {{object.size.x}} * scaleFactor, {{object.size.y}} * scaleFactor);
{{object.parameter}}->setRange({{object.min}}f, {{object.max}}f);
{%- for k, v in receivers + senders %}
{%- if v.display == object.parameter %}
{{object.parameter}}->setDefault({{v.attributes.default}}f);
{%- endif %}
{%- endfor %}
{{object.parameter}}->setShowArc({{object.arc_show|lower}});
{{object.parameter}}->setAngular({{object.ang_range}}, {{object.ang_offset}});
{{object.parameter}}->setDrawSquare({{object.square|lower}});
{{object.parameter}}->setShowTicks({{object.ticks|lower}});
{{object.parameter}}->setSteps({{object.steps}});
{{object.parameter}}->setShowArc({{object.arc_show|lower}});
{{object.parameter}}->setJumpOnClick({{object.jump|lower}});
{{object.parameter}}->setDiscrete({{object.discrete|lower}});
{{object.parameter}}->setUsingLogScale(PDKnobEventHandler::LogMode::{{object.log_mode|upper}});
{{object.parameter}}->setColors(
nvgRGB{{object.bg_color.as_rgb_tuple()}},
nvgRGB{{object.fg_color.as_rgb_tuple()}},
nvgRGB{{object.arc_color.as_rgb_tuple()}}
);
{{object.parameter}}->setLabelStyle({{object.label_pos.x}} * scaleFactor, {{object.label_pos.y}} * scaleFactor, {{object.label_size}} * scaleFactor);
{{object.parameter}}->setShowLabel(LabelShow::{{object.label_show.name|upper}});
{{parent}}->addManagedChild({{object.parameter}});
{%- elif object.type == 'number' %}
// number
{{object.parameter}} = new PDNumber({{parent}}, this);
{{object.parameter}}->setId(k{{object.parameter|capitalize}});
{{object.parameter}}->setSize({{object.size.x}} * scaleFactor, {{object.size.y}} * scaleFactor);
{{object.parameter}}->setAbsolutePos({{object.position.x}} * scaleFactor, {{object.position.y}} * scaleFactor);
{{object.parameter}}->setRange({{object.min}}f, {{object.max}}f);
{%- for k, v in receivers + senders %}
{%- if v.display == object.parameter %}
{{object.parameter}}->setDefault({{v.attributes.default}}f);
{%- endif %}
{%- endfor %}
{{object.parameter}}->setColors(
nvgRGB{{object.bg_color.as_rgb_tuple()}},
nvgRGB{{object.fg_color.as_rgb_tuple()}}
);
{%- if object.label != None %}
{{object.parameter}}->setLabel("{{object.label.text}}", nvgRGB{{object.label.color.as_rgb_tuple()}}, {{object.label.position.x}} * scaleFactor, {{object.label.position.y}} * scaleFactor, {{object.label.font_size}} * scaleFactor);
{%- endif %}
{{parent}}->addManagedChild({{object.parameter}});
{%- elif object.type == 'float' %}
// float
{{object.parameter}} = new PDFloat({{parent}}, this);
{{object.parameter}}->setId(k{{object.parameter|capitalize}});
{{object.parameter}}->setSize({{object.size.x}} * scaleFactor, ({{object.size.y}} * 1.5f) * scaleFactor);
{{object.parameter}}->setAbsolutePos({{object.position.x}} * scaleFactor, {{object.position.y}} * scaleFactor);
{{object.parameter}}->setRange({{object.min}}f, {{object.max}}f);
{%- for k, v in receivers + senders %}
{%- if v.display == object.parameter %}
{{object.parameter}}->setDefault({{v.attributes.default}}f);
{%- endif %}
{%- endfor %}
{{object.parameter}}->setLabel("{{object.label_text}}", ({{object.font_height}} + 2) * scaleFactor, LabelPos::{{object.label_pos.name|capitalize}});
{{parent}}->addManagedChild({{object.parameter}});
{%- endif %}
{%- endfor %}
hvcc-0.16.0/hvcc/generators/c2dpf/templates/hostTransportEvents.cpp 0000644 0000000 0000000 00000003654 00000000000 022273 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;
}
}
hvcc-0.16.0/hvcc/generators/c2dpf/templates/initParameter.cpp 0000644 0000000 0000000 00000004364 00000000000 021017 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' or v.extern == "event": -%}
| kParameterIsTrigger
{%- elif v.attributes.type == 'int': -%}
| kParameterIsInteger
{%- elif v.attributes.type in ['log', 'log_hz']: -%}
| kParameterIsLogarithmic
{%- endif %};
{%- if v.attributes %}
parameter.ranges.min = {{v.attributes.min}}f;
parameter.ranges.max = {{v.attributes.max}}f;
parameter.ranges.def = {{v.attributes.default}}f;
{%- else %}
parameter.ranges.min = 0.0f;
parameter.ranges.max = 1.0f;
parameter.ranges.def = 0.0f;
{%- endif %}
{%- 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;
hvcc-0.16.0/hvcc/generators/c2dpf/templates/midiInput.cpp 0000644 0000000 0000000 00000005507 00000000000 020155 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;
}
}
}
hvcc-0.16.0/hvcc/generators/c2dpf/templates/midiOutput.cpp 0000644 0000000 0000000 00000006104 00000000000 020350 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;
}
}
hvcc-0.16.0/hvcc/generators/c2dpf/templates/portGroups.cpp 0000644 0000000 0000000 00000005141 00000000000 020371 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] if value[0] is number else 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] if value[0] is number else 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 %}
}
}
hvcc-0.16.0/hvcc/generators/c2fmod/__init__.py 0000644 0000000 0000000 00000000000 00000000000 015755 0 ustar 00 hvcc-0.16.0/hvcc/generators/c2fmod/c2fmod.py 0000644 0000000 0000000 00000007625 00000000000 015414 0 ustar 00 # Heavy Compiler Collection
# Copyright (C) 2024-2026 Wasted Audio
#
# SPDX-License-Identifier: GPL-3.0-only
import jinja2
import os
import shutil
import time
from typing import Optional
from pathlib import Path
from hvcc.interpreters.pd2hv.NotificationEnum import NotificationEnum
from hvcc.types.compiler import Generator, CompilerResp, CompilerNotif, CompilerMsg, ExternInfo
from hvcc.types.meta import Meta
from ..copyright import copyright_manager
class c2fmod(Generator):
"""Generates a FMOD plugin wrapper for a given patch."""
@classmethod
def compile(
cls,
c_src_dir: Path,
out_dir: Path,
externs: ExternInfo,
patch_name: str,
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()
patch_name = patch_name.lower() if patch_name is not None else "heavy"
in_parameter_list = externs.parameters.inParam
out_parameter_list = externs.parameters.outParam
event_list = externs.events.inEvent
out_event_list = externs.events.outEvent
table_list = externs.tables
copyright_c = copyright_manager.get_copyright_for_c(copyright)
templates_dir = Path(Path(__file__).parent, "templates")
is_source_plugin = num_input_channels == 0
out_dir = Path(out_dir, "fmod")
if not out_dir.exists():
out_dir.mkdir(parents=True)
env = jinja2.Environment()
env.loader = jinja2.FileSystemLoader(
encoding="utf-8-sig", searchpath=[templates_dir]
)
try:
patch_src_dir = Path(out_dir, "include", "Heavy")
if patch_src_dir.exists():
shutil.rmtree(patch_src_dir)
shutil.copytree(c_src_dir, patch_src_dir)
heavy_src_files = [f for f in os.listdir(c_src_dir) if f.endswith(".c") or f.endswith(".cpp")]
src_ext_list = ["h", "hpp", "c", "cpp", "js", "md", "txt"]
for f in env.list_templates(extensions=src_ext_list):
file = Path(f)
file_dir = Path(out_dir, file.parent)
file_name = file.name.replace("{{name}}", patch_name)
file_path = Path(file_dir, file_name)
if not file_path.parent.exists():
file_path.parent.mkdir(parents=True)
with open(file_path, "w") as g:
g.write(env.get_template(f).render(
name=patch_name,
in_params=in_parameter_list,
out_params=out_parameter_list,
out_events=out_event_list,
events=event_list,
tables=table_list,
pool_sizes_kb=externs.memoryPoolSizesKb,
is_source_plugin=is_source_plugin,
num_input_channels=num_input_channels,
num_output_channels=num_output_channels,
heavy_src_files=heavy_src_files,
copyright=copyright_c))
return CompilerResp(
stage="c2fmod",
in_dir=c_src_dir,
out_dir=out_dir,
compile_time=time.time() - tick
)
except Exception as e:
return CompilerResp(
stage="c2fmod",
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
)
hvcc-0.16.0/hvcc/generators/c2fmod/templates/CMakeLists.txt 0000644 0000000 0000000 00000002505 00000000000 020416 0 ustar 00 cmake_minimum_required(VERSION 3.29.0)
project({{name}})
if(NOT IS_DIRECTORY ${CMAKE_SOURCE_DIR}/include/fmod)
message(FATAL_ERROR "FMOD SDK MISSING: ${CMAKE_SOURCE_DIR}/include/fmod")
endif()
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED 17)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set(CMAKE_STATIC_LIBRARY_PREFIX "")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build/$)
set(CMAKE_SHARED_LIBRARY_PREFIX "")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build/$)
add_library(
objlib OBJECT
${CMAKE_SOURCE_DIR}/src/{{name}}.cpp
)
set_target_properties(
objlib PROPERTIES
POSITION_INDEPENDENT_CODE True
)
target_sources(
objlib PRIVATE
{%- for file in heavy_src_files %}
${CMAKE_SOURCE_DIR}/include/Heavy/{{file}}
{%- endfor %}
)
target_include_directories(
objlib PRIVATE
${CMAKE_SOURCE_DIR}/include/Heavy
${CMAKE_SOURCE_DIR}/include/fmod/core/inc
${CMAKE_SOURCE_DIR}/include/fmod/studio/inc
)
add_library(${PROJECT_NAME} SHARED $)
add_library(lib${PROJECT_NAME} STATIC $)
add_custom_command(
TARGET ${PROJECT_NAME}
POST_BUILD
COMMAND ${CMAKE_COMMAND}
ARGS -E copy ${CMAKE_SOURCE_DIR}/src/{{name}}.plugin.js
${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/{{name}}.plugin.js
)
hvcc-0.16.0/hvcc/generators/c2fmod/templates/include/README.md 0000644 0000000 0000000 00000000415 00000000000 020556 0 ustar 00 # FMOD SDK
In order to build the project this folder needs to contain the
[FMOD SDK](https://www.fmod.com/download#fmodengine) in the sub folder `fmod`.
The directory structure should look like this.
```
include/fmod/core
include/fmod/fsbank
include/fmod/studio
```
hvcc-0.16.0/hvcc/generators/c2fmod/templates/src/{{name}}.cpp 0000644 0000000 0000000 00000022740 00000000000 021054 0 ustar 00 {{copyright}}
#include
#include "fmod.hpp"
#include "Heavy_{{name}}.hpp"
//------------------------------------------------------------------------------
enum class PluginParameterType { Float, Int, Bool };
struct PluginParameter {
PluginParameterType type;
union {
float f;
int i;
bool b;
};
explicit PluginParameter(const float val) : type(PluginParameterType::Float), f(val) {}
explicit PluginParameter(const int val) : type(PluginParameterType::Int), i(val) {}
explicit PluginParameter(const bool val) : type(PluginParameterType::Bool), b(val) {}
};
//------------------------------------------------------------------------------
struct Plugin{{name|capitalize}} {
int numInputs, numOutputs;
std::array paramArray;
std::array hashArray;
HeavyContextInterface *ctx;
};
//------------------------------------------------------------------------------
static FMOD_RESULT F_CALL PluginCreate(FMOD_DSP_STATE *dspState)
{
dspState->plugindata = static_cast(FMOD_DSP_ALLOC(dspState, sizeof(Plugin{{name|capitalize}})));
if (!dspState->plugindata)
{
return FMOD_ERR_MEMORY;
}
int samplerate;
dspState->functions->getsamplerate(dspState, &samplerate);
auto *plugin = static_cast(dspState->plugindata);
plugin->ctx = new Heavy_{{name}}(static_cast(samplerate), {{pool_sizes_kb.internal}}, {{pool_sizes_kb.inputQueue}}, {{pool_sizes_kb.outputQueue}});
plugin->numInputs = plugin->ctx->getNumInputChannels();
plugin->numOutputs = plugin->ctx->getNumOutputChannels();
{%- for param, i in in_params %}
{%- if i.attributes.type == "float" %}
plugin->paramArray[{{loop.index0}}] = PluginParameter({{i.attributes.default}}f);
{%- elif i.attributes.type == "int" %}
plugin->paramArray[{{loop.index0}}] = PluginParameter(static_cast({{i.attributes.default}}));
{%- elif i.attributes.type == "bool" %}
plugin->paramArray[{{loop.index0}}] = PluginParameter(static_cast({{i.attributes.default}}));
{%- endif %}
plugin->hashArray[{{loop.index0}}] = Heavy_{{name}}::Parameter::In::{{param|upper}};
{%- endfor %}
return FMOD_OK;
}
//------------------------------------------------------------------------------
static FMOD_RESULT F_CALL PluginRelease(FMOD_DSP_STATE *dspState)
{
auto plugin = static_cast(dspState->plugindata);
delete plugin->ctx;
FMOD_DSP_FREE(dspState, plugin);
return FMOD_OK;
}
//------------------------------------------------------------------------------
static FMOD_RESULT F_CALL PluginProcess
(
FMOD_DSP_STATE *dspState,
unsigned int length,
const FMOD_DSP_BUFFER_ARRAY *inBuffers,
FMOD_DSP_BUFFER_ARRAY *outBuffers,
FMOD_BOOL inputsIdle,
FMOD_DSP_PROCESS_OPERATION op
)
{
const auto plugin = static_cast(dspState->plugindata);
switch (op)
{
case FMOD_DSP_PROCESS_QUERY:
if (inBuffers && outBuffers)
{
outBuffers[0].bufferchannelmask[0] = inBuffers[0].bufferchannelmask[0];
outBuffers[0].speakermode = inBuffers[0].speakermode;
{%- if is_source_plugin %}
outBuffers[0].buffernumchannels[0] = plugin->numOutputs;
return FMOD_OK;
{%- else %}
if (plugin->numInputs > 0)
{
if (inputsIdle)
{
return FMOD_ERR_DSP_DONTPROCESS;
}
if (inBuffers[0].buffernumchannels[0] != plugin->numInputs
|| outBuffers[0].buffernumchannels[0] != plugin->numOutputs)
{
return FMOD_ERR_DSP_SILENCE;
}
}
{%- endif %}
}
break;
case FMOD_DSP_PROCESS_PERFORM:
if (inBuffers && outBuffers)
{
plugin->ctx->processInlineInterleaved(inBuffers[0].buffers[0], outBuffers[0].buffers[0], static_cast(length));
}
break;
}
return FMOD_OK;
}
//------------------------------------------------------------------------------
static FMOD_RESULT F_CALL PluginReset(FMOD_DSP_STATE *dspState)
{
// NOTE: reset plugin state here if needed
// auto plugin = static_cast(dspState->plugindata);
return FMOD_OK;
}
//------------------------------------------------------------------------------
static FMOD_RESULT F_CALL PluginSetFloat
(
FMOD_DSP_STATE *dspState,
int idx,
float val
)
{
auto plugin = static_cast(dspState->plugindata);
if (idx >= plugin->paramArray.size()
|| plugin->paramArray[idx].type != PluginParameterType::Float)
{
return FMOD_ERR_INVALID_PARAM;
}
plugin->paramArray[idx].f = val;
plugin->ctx->sendFloatToReceiver(plugin->hashArray[idx], val);
return FMOD_OK;
}
//------------------------------------------------------------------------------
static FMOD_RESULT F_CALL PluginGetFloat
(
FMOD_DSP_STATE *dspState,
int idx,
float *outVal,
char *outValStr
)
{
auto plugin = static_cast(dspState->plugindata);
if (idx >= plugin->paramArray.size()
|| plugin->paramArray[idx].type != PluginParameterType::Float)
{
return FMOD_ERR_INVALID_PARAM;
}
*outVal = plugin->paramArray[idx].f;
if (outValStr)
{
snprintf(outValStr, FMOD_DSP_GETPARAM_VALUESTR_LENGTH, "%.1f", plugin->paramArray[idx].f);
}
return FMOD_OK;
}
//------------------------------------------------------------------------------
static FMOD_RESULT F_CALL PluginSetInt
(
FMOD_DSP_STATE *dspState,
int idx,
int val
)
{
auto plugin = static_cast(dspState->plugindata);
if (idx >= plugin->paramArray.size()
|| plugin->paramArray[idx].type != PluginParameterType::Int)
{
return FMOD_ERR_INVALID_PARAM;
}
plugin->paramArray[idx].i = val;
plugin->ctx->sendFloatToReceiver(plugin->hashArray[idx], static_cast(val));
return FMOD_OK;
}
//------------------------------------------------------------------------------
static FMOD_RESULT F_CALL PluginGetInt
(
FMOD_DSP_STATE *dspState,
int idx,
int *outVal,
char *outValStr
)
{
auto plugin = static_cast(dspState->plugindata);
if (idx >= plugin->paramArray.size()
|| plugin->paramArray[idx].type != PluginParameterType::Int)
{
return FMOD_ERR_INVALID_PARAM;
}
*outVal = plugin->paramArray[idx].i;
if (outValStr)
{
snprintf(outValStr, FMOD_DSP_GETPARAM_VALUESTR_LENGTH, "%d", plugin->paramArray[idx].i);
}
return FMOD_OK;
}
//------------------------------------------------------------------------------
static FMOD_RESULT F_CALL PluginSetBool(
FMOD_DSP_STATE *dspState,
int idx,
FMOD_BOOL val
)
{
auto plugin = static_cast(dspState->plugindata);
if (idx >= plugin->paramArray.size()
|| plugin->paramArray[idx].type != PluginParameterType::Bool)
{
return FMOD_ERR_INVALID_PARAM;
}
plugin->paramArray[idx].b = static_cast(val);
plugin->ctx->sendFloatToReceiver(plugin->hashArray[idx], static_cast(val));
return FMOD_OK;
}
//------------------------------------------------------------------------------
static FMOD_RESULT F_CALL PluginGetBool(
FMOD_DSP_STATE *dspState,
int idx,
int *outVal,
char *outValStr
)
{
auto plugin = static_cast(dspState->plugindata);
if (idx >= plugin->paramArray.size()
|| plugin->paramArray[idx].type != PluginParameterType::Bool)
{
return FMOD_ERR_INVALID_PARAM;
}
*outVal = static_cast(plugin->paramArray[idx].b);
if (outValStr)
{
snprintf(outValStr, FMOD_DSP_GETPARAM_VALUESTR_LENGTH, "%d", plugin->paramArray[idx].b);
}
return FMOD_OK;
}
//------------------------------------------------------------------------------
extern "C"
{
F_EXPORT FMOD_DSP_DESCRIPTION *F_CALL FMODGetDSPDescription()
{
{%- for param, i in in_params %}
static FMOD_DSP_PARAMETER_DESC param_{{param}};
{%- endfor %}
static FMOD_DSP_PARAMETER_DESC *param_desc[{{in_params|length}}] = {
{%- for param, i in in_params %}
¶m_{{param}},
{%- endfor %}
};
{%- for param, i in in_params %}
{%- if i.attributes.type == "float" %}
FMOD_DSP_INIT_PARAMDESC_FLOAT(param_{{param}}, "{{i.display}}", "", "{{i.display}}",
{{i.attributes.min}}f, {{i.attributes.max}}f, {{i.attributes.default}}f);
{%- elif i.attributes.type == "int" %}
FMOD_DSP_INIT_PARAMDESC_INT(param_{{param}}, "{{i.display}}", "", "{{i.display}}",
{{i.attributes.min}}, {{i.attributes.max}}, {{i.attributes.default}}, false, nullptr);
{%- elif i.attributes.type == "bool" %}
FMOD_DSP_INIT_PARAMDESC_BOOL(param_{{param}}, "{{i.display}}", "", "{{i.display}}",
{{i.attributes.default}}, nullptr);
{%- endif %}
{%- endfor %}
static FMOD_DSP_DESCRIPTION desc { FMOD_PLUGIN_SDK_VERSION, "{{name}}", 1};
desc.numinputbuffers = {{"0" if is_source_plugin else "1"}};
desc.numoutputbuffers = 1;
desc.create = PluginCreate;
desc.release = PluginRelease;
desc.reset = PluginReset;
desc.process = PluginProcess;
desc.numparameters = {{in_params|length}};
desc.paramdesc = param_desc;
desc.setparameterfloat = PluginSetFloat;
desc.getparameterfloat = PluginGetFloat;
desc.setparameterint = PluginSetInt;
desc.getparameterint = PluginGetInt;
desc.setparameterbool = PluginSetBool;
desc.getparameterbool = PluginGetBool;
return &desc;
}
}
//------------------------------------------------------------------------------
hvcc-0.16.0/hvcc/generators/c2fmod/templates/src/{{name}}.plugin.js 0000644 0000000 0000000 00000000420 00000000000 022172 0 ustar 00 studio.plugins.registerPluginDescription("{{name}}", {
companyName: "Wasted Audio",
productName: "{{name|capitalize}}",
parameters: {
{%- for param, i in in_params %}
"{{param}}": {
displayName: "{{param|capitalize}}"
},
{%- endfor %}
},
});
hvcc-0.16.0/hvcc/generators/c2js/README.md 0000644 0000000 0000000 00000000255 00000000000 014626 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 hvcc-0.16.0/hvcc/generators/c2js/__init__.py 0000644 0000000 0000000 00000000000 00000000000 015444 0 ustar 00 hvcc-0.16.0/hvcc/generators/c2js/c2js.py 0000644 0000000 0000000 00000025147 00000000000 014571 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2021-2026 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 pathlib import Path
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: Path,
out_dir: Path,
patch_name: str,
output_name: str,
post_js_path: Path,
should_modularize: int,
environment: str,
pre_js_path: Path = Path(),
binaryen_async: int = 1
) -> Path:
"""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",
"-DHV_BARE_METAL",
"-ffast-math",
"-DNDEBUG",
"-Wall"
]
c_src_paths = [Path(c_src_dir, c) for c in os.listdir(c_src_dir) if c.endswith((".c"))]
cpp_src_paths = [Path(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 = Path(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", "EXPORTED_RUNTIME_METHODS=HEAPF32",
"-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 pre_js_path != 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: Path,
out_dir: Path,
externs: ExternInfo,
patch_name: str,
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 = Path(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 out_dir.exists():
os.makedirs(out_dir)
out_dir = out_dir.absolute()
try:
# initialise the jinja template environment
env = jinja2.Environment()
env.loader = jinja2.FileSystemLoader(Path(
Path(__file__).parent,
"template"))
# generate heavy js wrapper from template
# Note: this file will be incorporated into the emscripten output
# and removed afterwards
post_js_path = Path(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 = js_path.name
# generate index.html from template
with open(Path(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 = Path(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 = Path(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
)
hvcc-0.16.0/hvcc/generators/c2js/template/hv_worklet.js 0000644 0000000 0000000 00000030244 00000000000 017705 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 outputBuffer 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 lengthOutSamples = this.blockSize * this.getNumOutputChannels();
var lengthInSamples = this.blockSize * this.getNumInputChannels();
this.outputBuffer = new Float32Array(
Module.HEAPF32.buffer,
Module._malloc(lengthOutSamples * Float32Array.BYTES_PER_ELEMENT),
lengthOutSamples);
this.inputBuffer = 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) {
// Currently only supports one output connection and one input connection (inputs[0] and outputs[0])
try{
var inputChannelCount = this.getNumInputChannels();
if (inputs.length > 0 && inputs[0].length && inputChannelCount > 0) {
for (let c = 0; c < inputChannelCount; c++) {
if (!inputs[0][c]) {
continue;
}
this.inputBuffer.set(inputs[0][c], c * this.blockSize);
}
} else {
this.inputBuffer.set(0); //clear buffer when no inputs are connected
}
_hv_processInline(this.heavyContext, this.inputBuffer.byteOffset, this.outputBuffer.byteOffset, this.blockSize);
var output = outputs[0];
var outputChannelCount = this.getNumOutputChannels();
for (var i = 0; i < outputChannelCount; ++i) {
output[i].set(this.outputBuffer.subarray(i * this.blockSize, (i + 1) * this.blockSize))
}
} 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];
hvcc-0.16.0/hvcc/generators/c2js/template/hv_worklet_start.js 0000644 0000000 0000000 00000000504 00000000000 021116 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);
}
hvcc-0.16.0/hvcc/generators/c2js/template/hv_wrapper.js 0000644 0000000 0000000 00000037233 00000000000 017703 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 lengthOutSamples = this.blockSize * this.getNumOutputChannels();
var lengthInSamples = this.blockSize * this.getNumInputChannels();
this.outputBuffer = new Float32Array(
Module.HEAPF32.buffer,
Module._malloc(lengthOutSamples * Float32Array.BYTES_PER_ELEMENT),
lengthOutSamples);
this.inputBuffer = 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) {
// Currently only supports one output connection and one input connection on the worklet/scriptprocessor. (unlimited channels though)
// Note(ZXMushroom63): calling getNumXXXChannels() every iteration of the for loop is slightly less efficient than calling once and storing the result
var inputChannelCount = this.getNumInputChannels();
if (inputChannelCount > 0) {
for (let i = 0; i < inputChannelCount; i++) {
if ((event.inputBuffer.numberOfChannels - 2) < i) {
continue;
}
this.inputBuffer.set(event.inputBuffer.getChannelData(i), i * this.blockSize);
}
} else {
this.inputBuffer.set(0); //clear buffer when no inputs are connected
}
_hv_processInline(this.heavyContext, this.inputBuffer.byteOffset, this.outputBuffer.byteOffset, this.blockSize);
var outputChannelCount = this.getNumOutputChannels();
for (var i = 0; i < outputChannelCount; ++i) {
var output = event.outputBuffer.getChannelData(i);
output.set(this.outputBuffer.subarray(i * this.blockSize, (i + 1) * this.blockSize));
}
}
{{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];
hvcc-0.16.0/hvcc/generators/c2js/template/index.html 0000644 0000000 0000000 00000024753 00000000000 017170 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 %}
hvcc-0.16.0/hvcc/generators/c2owl/__init__.py 0000644 0000000 0000000 00000000000 00000000000 015631 0 ustar 00 hvcc-0.16.0/hvcc/generators/c2owl/c2owl.py 0000644 0000000 0000000 00000012777 00000000000 015150 0 ustar 00 import shutil
import time
import jinja2
import json
from typing import List, Optional
from pathlib import Path
import hvcc.core.hv2ir.HeavyLangObject as HeavyLangObject
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
from hvcc.types.IR import IRGraph
heavy_hash = HeavyLangObject.HeavyLangObject.get_hash
OWL_BUTTONS = ['Push', 'B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7', 'B8', 'B9', 'B10', 'B11', 'Red', 'Green']
class c2owl(Generator):
""" Generates a OWL wrapper for a given patch.
"""
@classmethod
def make_jdata(cls, patch_ir: Path) -> List:
jdata = list()
with open(patch_ir, mode="r") as f:
ir = IRGraph(**json.load(f))
for name, recv in ir.control.receivers.items():
# skip __hv_init and similar
if name.startswith("__"):
continue
# If a name has been specified
if recv.attributes.get('raw'):
key = recv.attributes['raw']
jdata.append((key, name, 'RECV', f"0x{heavy_hash(name):X}",
recv.attributes['min'],
recv.attributes['max'],
recv.attributes['default'],
key in OWL_BUTTONS))
elif name.startswith('Channel-'):
key = name.split('Channel-', 1)[1]
jdata.append((key, name, 'RECV', f"0x{heavy_hash(name):X}",
0, 1, None, key in OWL_BUTTONS))
for _, obj in ir.objects.items():
try:
if obj.type == '__send':
name = obj.args['name']
if obj.args['attributes'].get('raw'):
key = obj.args['attributes']['raw']
jdata.append((key, f'{name}>', 'SEND', f"0x{heavy_hash(name):X}",
obj.args['attributes']['min'],
obj.args['attributes']['max'],
obj.args['attributes']['default'],
key in OWL_BUTTONS))
elif name.startswith('Channel-'):
key = name.split('Channel-', 1)[1]
jdata.append((key, f'{name}>', 'SEND', f"0x{heavy_hash(name):X}",
0, 1, None, key in OWL_BUTTONS))
except Exception:
pass
return jdata
@classmethod
def compile(
cls,
c_src_dir: Path,
out_dir: Path,
externs: ExternInfo,
patch_name: str,
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 = Path(out_dir, "Source")
patch_name = patch_name or "heavy"
copyright_c = copyright_manager.get_copyright_for_c(copyright)
try:
# ensure that the output directory does not exist
out_dir = out_dir.absolute()
if out_dir.exists():
shutil.rmtree(out_dir)
# copy over generated C source files
shutil.copytree(c_src_dir, out_dir)
# copy over deps
shutil.copytree(Path(Path(__file__).parent, "deps"), out_dir, dirs_exist_ok=True)
# initialize the jinja template environment
env = jinja2.Environment()
env.loader = jinja2.FileSystemLoader(Path(Path(__file__).parent), "templates")
# construct jdata from ir
ir_dir = Path(c_src_dir, "../ir")
patch_ir = Path(ir_dir, f"{patch_name}.heavy.ir.json")
jdata = cls.make_jdata(patch_ir)
# generate OWL wrapper from template
owl_hpp_path = Path(out_dir, f"HeavyOWL_{patch_name}.hpp")
with open(owl_hpp_path, "w") as f:
f.write(env.get_template("HeavyOwl.hpp").render(
jdata=jdata,
name=patch_name,
copyright=copyright_c))
owl_h_path = Path(out_dir, "HeavyOwlConstants.h")
with open(owl_h_path, "w") as f:
f.write(env.get_template("HeavyOwlConstants.h").render(
jdata=jdata,
copyright=copyright_c))
# ======================================================================================
return CompilerResp(
stage="c2owl",
in_dir=c_src_dir,
out_dir=out_dir,
out_file=owl_h_path,
compile_time=time.time() - tick
)
except Exception as e:
return CompilerResp(
stage="c2owl",
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
)
hvcc-0.16.0/hvcc/generators/c2owl/deps/HvMessage.c 0000644 0000000 0000000 00000016215 00000000000 016520 0 ustar 00 /**
* Copyright (c) 2014,2015,2016 Enzien Audio Ltd.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "HvMessage.h"
#include "message.h"
#include
HvMessage *msg_init(HvMessage *m, hv_size_t numElements, hv_uint32_t timestamp) {
m->timestamp = timestamp;
m->numElements = (hv_uint16_t) numElements;
m->numBytes = (hv_uint16_t) msg_getCoreSize(numElements);
return m;
}
HvMessage *msg_initWithFloat(HvMessage *m, hv_uint32_t timestamp, float f) {
m->timestamp = timestamp;
m->numElements = 1;
m->numBytes = sizeof(HvMessage);
msg_setFloat(m, 0, f);
return m;
}
HvMessage *msg_initWithBang(HvMessage *m, hv_uint32_t timestamp) {
m->timestamp = timestamp;
m->numElements = 1;
m->numBytes = sizeof(HvMessage);
msg_setBang(m, 0);
return m;
}
HvMessage *msg_initWithSymbol(HvMessage *m, hv_uint32_t timestamp, const char *s) {
m->timestamp = timestamp;
m->numElements = 1;
m->numBytes = sizeof(HvMessage) + (hv_uint16_t) hv_strlen(s);
msg_setSymbol(m, 0, s);
return m;
}
HvMessage *msg_initWithHash(HvMessage *m, hv_uint32_t timestamp, hv_uint32_t h) {
m->timestamp = timestamp;
m->numElements = 1;
m->numBytes = sizeof(HvMessage);
msg_setHash(m, 0, h);
return m;
}
void msg_copyToBuffer(const HvMessage *m, char *buffer, hv_size_t len) {
HvMessage *r = (HvMessage *) buffer;
hv_size_t len_r = msg_getCoreSize(msg_getNumElements(m));
// assert that the message is not already larger than the length of the buffer
hv_assert(len_r <= len);
// copy the basic message to the buffer
hv_memcpy(r, m, len_r);
char *p = buffer + len_r; // points to the end of the base message
for (int i = 0; i < msg_getNumElements(m); ++i) {
if (msg_isSymbol(m,i)) {
const hv_size_t symLen = (hv_size_t) hv_strlen(msg_getSymbol(m,i)) + 1; // include the trailing null char
hv_assert(len_r + symLen <= len); // stay safe!
hv_strncpy(p, msg_getSymbol(m,i), symLen);
msg_setSymbol(r, i, p);
p += symLen;
len_r += symLen;
}
}
r->numBytes = (hv_uint16_t) len_r; // update the message size in memory
}
// the message is serialised such that all symbol elements are placed in order at the end of the buffer
HvMessage *msg_copy(const HvMessage *m) {
const hv_uint32_t heapSize = msg_getSize(m);
char *r = (char *) hv_malloc(heapSize);
hv_assert(r != NULL);
msg_copyToBuffer(m, r, heapSize);
return (HvMessage *) r;
}
void msg_free(HvMessage *m) {
hv_free(m); // because heap messages are serialised in memory, a simple call to free releases the message
}
bool msg_hasFormat(const HvMessage *m, const char *fmt) {
hv_assert(fmt != NULL);
const int n = msg_getNumElements(m);
for (int i = 0; i < n; ++i) {
switch (fmt[i]) {
case 'b': if (!msg_isBang(m, i)) return false; break;
case 'f': if (!msg_isFloat(m, i)) return false; break;
case 'h': if (!msg_isHash(m, i)) return false; break;
case 's': if (!msg_isSymbol(m, i)) return false; break;
default: return false;
}
}
return (fmt[n] == '\0');
}
bool msg_compareSymbol(const HvMessage *m, int i, const char *s) {
switch (msg_getType(m,i)) {
case HV_MSG_SYMBOL: return !hv_strcmp(msg_getSymbol(m, i), s);
case HV_MSG_HASH: return (msg_getHash(m,i) == hv_string_to_hash(s));
default: return false;
}
}
bool msg_equalsElement(const HvMessage *m, int i_m, const HvMessage *n, int i_n) {
if (i_m < msg_getNumElements(m) && i_n < msg_getNumElements(n)) {
if (msg_getType(m, i_m) == msg_getType(n, i_n)) {
switch (msg_getType(m, i_m)) {
case HV_MSG_BANG: return true;
case HV_MSG_FLOAT: return (msg_getFloat(m, i_m) == msg_getFloat(n, i_n));
case HV_MSG_SYMBOL: return msg_compareSymbol(m, i_m, msg_getSymbol(n, i_n));
case HV_MSG_HASH: return msg_getHash(m,i_m) == msg_getHash(n,i_n);
default: break;
}
}
}
return false;
}
void msg_setElementToFrom(HvMessage *n, int i_n, const HvMessage *const m, int i_m) {
switch (msg_getType(m, i_m)) {
case HV_MSG_BANG: msg_setBang(n, i_n); break;
case HV_MSG_FLOAT: msg_setFloat(n, i_n, msg_getFloat(m, i_m)); break;
case HV_MSG_SYMBOL: msg_setSymbol(n, i_n, msg_getSymbol(m, i_m)); break;
case HV_MSG_HASH: msg_setHash(n, i_n, msg_getHash(m, i_m));
default: break;
}
}
hv_uint32_t msg_getHash(const HvMessage *const m, int i) {
hv_assert(i < msg_getNumElements(m)); // invalid index
switch (msg_getType(m,i)) {
case HV_MSG_BANG: return 0xFFFFFFFF;
case HV_MSG_FLOAT: {
float f = msg_getFloat(m,i);
return *((hv_uint32_t *) &f);
}
case HV_MSG_SYMBOL: return hv_string_to_hash(msg_getSymbol(m,i));
case HV_MSG_HASH: return (&(m->elem)+i)->data.h;
default: return 0;
}
}
char *msg_toString(const HvMessage *m) {
hv_assert(msg_getNumElements(m) > 0);
int *len = (int *) hv_alloca(msg_getNumElements(m)*sizeof(int));
int size = 0; // the total length of our final buffer
// loop through every element in our list of atoms
// first loop figures out how long our buffer should be
for (int i = 0; i < msg_getNumElements(m); i++) {
// length of our string is each atom plus a space, or \0 on the end
switch (msg_getType(m, i)) {
case HV_MSG_BANG: len[i] = 5; break;
case HV_MSG_FLOAT: len[i] = strnlen(msg_ftoa(msg_getFloat(m, i), 10), 16)+1; break;
case HV_MSG_SYMBOL: len[i] = strnlen(msg_getSymbol(m, i), 16)+1; break;
case HV_MSG_HASH: len[i] = strnlen(msg_itoa(msg_getHash(m, i), 16), 8)+3; break;
default: break;
}
size += len[i];
}
hv_assert(size > 0);
// now we do the piecewise concatenation into our final string
// the final buffer we will pass back after concatenating all strings - user should free it
char *finalString = (char *) hv_malloc(size*sizeof(char));
char* dst = finalString;
for (int i = 0; i < msg_getNumElements(m); i++) {
// put a string representation of each atom into the final string
char* ptr;
switch (msg_getType(m, i)) {
case HV_MSG_BANG:
dst = stpcpy(dst, "bang");
break;
case HV_MSG_FLOAT:
ptr = msg_ftoa(msg_getFloat(m, i), 10);
dst = stpcpy(dst, ptr);
break;
case HV_MSG_SYMBOL:
ptr = (char*)msg_getSymbol(m, i);
dst = stpcpy(dst, ptr);
break;
case HV_MSG_HASH:
ptr = msg_itoa(msg_getHash(m, i), 16);
dst = stpcpy(dst, "0x");
dst = stpcpy(dst, ptr);
break;
default:
break;
}
dst = stpcpy(dst, " ");
}
hv_assert(dst - finalString == size);
finalString[size-1] = '\0'; // ensure that the string is null terminated
return finalString;
}
hvcc-0.16.0/hvcc/generators/c2owl/deps/HvUtils.h 0000644 0000000 0000000 00000026323 00000000000 016242 0 ustar 00 /**
* Copyright (c) 2014,2015,2016 Enzien Audio Ltd.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef _HEAVY_UTILS_H_
#define _HEAVY_UTILS_H_
// platform definitions
#if _WIN32 || _WIN64
#define HV_WIN 1
#ifdef _MSC_VER
#define HV_MSVC 1
#endif
#elif __APPLE__
#define HV_APPLE 1
#elif __ANDROID__
#define HV_ANDROID 1
#elif __unix__ || __unix
#define HV_UNIX 1
#else
#ifndef HV_BARE_METAL
#warning Could not detect platform. Assuming Unix-like.
#endif
#endif
#ifdef EMSCRIPTEN
#define HV_EMSCRIPTEN 1
#endif
// basic includes
#include
#ifdef ARM_CORTEX
#include
#else
#include
#include
#endif
// type definitions
#include
#include
#define hv_uint8_t uint8_t
#define hv_int16_t int16_t
#define hv_uint16_t uint16_t
#define hv_int32_t int32_t
#define hv_uint32_t uint32_t
#define hv_uint64_t uint64_t
#define hv_size_t size_t
#define hv_uintptr_t uintptr_t
// SIMD-specific includes
#if !(HV_SIMD_NONE || HV_SIMD_NEON || HV_SIMD_SSE || HV_SIMD_AVX)
#define HV_SIMD_NEON __ARM_NEON__
#define HV_SIMD_SSE (__SSE__ && __SSE2__ && __SSE3__ && __SSSE3__ && __SSE4_1__)
#define HV_SIMD_AVX (__AVX__ && HV_SIMD_SSE)
#endif
#ifndef HV_SIMD_FMA
#define HV_SIMD_FMA __FMA__
#endif
#if HV_SIMD_AVX || HV_SIMD_SSE
#include
#elif HV_SIMD_NEON
#include
#endif
#if HV_SIMD_NEON // NEON
#define HV_N_SIMD 4
#define hv_bufferf_t float32x4_t
#define hv_bufferi_t int32x4_t
#define hv_bInf_t float32x4_t
#define hv_bOutf_t float32x4_t*
#define hv_bIni_t int32x4_t
#define hv_bOuti_t int32x4_t*
#define VIf(_x) (_x)
#define VOf(_x) (&_x)
#define VIi(_x) (_x)
#define VOi(_x) (&_x)
#elif HV_SIMD_AVX // AVX
#define HV_N_SIMD 8
#define hv_bufferf_t __m256
#define hv_bufferi_t __m256i
#define hv_bInf_t __m256
#define hv_bOutf_t __m256*
#define hv_bIni_t __m256i
#define hv_bOuti_t __m256i*
#define VIf(_x) (_x)
#define VOf(_x) (&_x)
#define VIi(_x) (_x)
#define VOi(_x) (&_x)
#elif HV_SIMD_SSE // SSE
#define HV_N_SIMD 4
#define hv_bufferf_t __m128
#define hv_bufferi_t __m128i
#define hv_bInf_t __m128
#define hv_bOutf_t __m128*
#define hv_bIni_t __m128i
#define hv_bOuti_t __m128i*
#define VIf(_x) (_x)
#define VOf(_x) (&_x)
#define VIi(_x) (_x)
#define VOi(_x) (&_x)
#else // DEFAULT
#define HV_N_SIMD 1
#undef HV_SIMD_NONE
#define HV_SIMD_NONE 1
#define hv_bufferf_t float
#define hv_bufferi_t int
#define hv_bInf_t float
#define hv_bOutf_t float*
#define hv_bIni_t int
#define hv_bOuti_t int*
#define VIf(_x) (_x)
#define VOf(_x) (&_x)
#define VIi(_x) (_x)
#define VOi(_x) (&_x)
#endif
#define HV_N_SIMD_MASK (HV_N_SIMD-1)
// Strings
#include
#define hv_strlen(a) strlen(a)
#define hv_strncpy(a, b, c) strncpy(a, b, c)
#define hv_strcmp(a, b) strcmp(a, b)
#define hv_snprintf(a, b, c, ...) snprintf(a, b, c, __VA_ARGS__)
// Memory management
#ifndef ARM_CORTEX
#define hv_realloc(a, b) realloc(a, b)
#endif // ARM_CORTEX
#define hv_memcpy(a, b, c) memcpy(a, b, c)
#define hv_memclear(a, b) memset(a, 0, b)
#if HV_MSVC
#include
#define hv_alloca(_n) _alloca(_n)
#if HV_SIMD_AVX
#define hv_malloc(_n) _aligned_malloc(_n, 32)
#define hv_realloc(a, b) _aligned_realloc(a, b, 32)
#define hv_free(x) _aligned_free(x)
#elif HV_SIMD_SSE || HV_SIMD_NEON
#define hv_malloc(_n) _aligned_malloc(_n, 16)
#define hv_realloc(a, b) _aligned_realloc(a, b, 16)
#define hv_free(x) _aligned_free(x)
#else // HV_SIMD_NONE
#define hv_malloc(_n) malloc(_n)
#define hv_free(_n) free(_n)
#endif
#elif HV_APPLE
#define hv_alloca(_n) alloca(_n)
#define hv_realloc(a, b) realloc(a, b)
#if HV_SIMD_AVX
#include
#define hv_malloc(_n) _mm_malloc(_n, 32)
#define hv_free(x) _mm_free(x)
#elif HV_SIMD_SSE
#include
#define hv_malloc(_n) _mm_malloc(_n, 16)
#define hv_free(x) _mm_free(x)
#elif HV_SIMD_NEON
// malloc on ios always has 16-byte alignment
#define hv_malloc(_n) malloc(_n)
#define hv_free(x) free(x)
#else // HV_SIMD_NONE
#define hv_malloc(_n) malloc(_n)
#define hv_free(x) free(x)
#endif
#elif defined ARM_CORTEX
#include
#define hv_alloca(_n) alloca(_n)
#define hv_malloc(_n) pvPortMalloc(_n)
#define hv_free(_n) vPortFree(_n)
#define hv_realloc(a, b) pvPortRealloc(a, b)
#else
#include
#define hv_alloca(_n) alloca(_n)
#if HV_SIMD_AVX
#define hv_malloc(_n) aligned_alloc(32, _n)
#define hv_free(x) free(x)
#elif HV_SIMD_SSE || HV_SIMD_NEON
#define hv_malloc(_n) aligned_alloc(16, _n)
#define hv_free(x) free(x)
#else // HV_SIMD_NONE
#define hv_malloc(_n) malloc(_n)
#define hv_free(_n) free(_n)
#endif
#endif
// Assert
#ifdef ARM_CORTEX
#include "message.h"
#define hv_assert(e) ASSERT((e), "Heavy assertion failed")
#else
#include
#define hv_assert(e) assert(e)
#endif
// Export and Inline
#if HV_MSVC
#define HV_EXPORT __declspec(dllexport)
#define inline __inline
#define HV_FORCE_INLINE __forceinline
#else
#define HV_EXPORT
#define HV_FORCE_INLINE inline __attribute__((always_inline))
#endif
#ifdef __cplusplus
extern "C" {
#endif
// Returns a 32-bit hash of any string. Returns 0 if string is NULL.
hv_uint32_t hv_string_to_hash(const char *str);
#ifdef __cplusplus
}
#endif
// Math
#ifndef ARM_CORTEX
#include
#endif
static inline hv_size_t __hv_utils_max_ui(hv_size_t x, hv_size_t y) { return (x > y) ? x : y; }
static inline hv_size_t __hv_utils_min_ui(hv_size_t x, hv_size_t y) { return (x < y) ? x : y; }
static inline hv_int32_t __hv_utils_max_i(hv_int32_t x, hv_int32_t y) { return (x > y) ? x : y; }
static inline hv_int32_t __hv_utils_min_i(hv_int32_t x, hv_int32_t y) { return (x < y) ? x : y; }
#define hv_max_ui(a, b) __hv_utils_max_ui(a, b)
#define hv_min_ui(a, b) __hv_utils_min_ui(a, b)
#define hv_max_i(a, b) __hv_utils_max_i(a, b)
#define hv_min_i(a, b) __hv_utils_min_i(a, b)
#define hv_max_f(a, b) fmaxf(a, b)
#define hv_min_f(a, b) fminf(a, b)
#define hv_max_d(a, b) fmax(a, b)
#define hv_min_d(a, b) fmin(a, b)
#ifdef ARM_CORTEX
#define hv_sin_f(a) arm_sin_f32(a)
#define hv_cos_f(a) arm_cos_f32(a)
#define hv_sqrt_f(a) arm_sqrtf(a)
#define hv_pow_f(a, b) fast_powf(a, b)
#define hv_exp_f(a) fast_expf(a)
#define hv_log_f(a) fast_logf(a)
#else
#define hv_sin_f(a) sinf(a)
#define hv_cos_f(a) cosf(a)
#define hv_sqrt_f(a) sqrtf(a)
#define hv_pow_f(a, b) powf(a, b)
#define hv_exp_f(a) expf(a)
#define hv_log_f(a) logf(a)
#endif
#define hv_sinh_f(a) sinhf(a)
#define hv_cosh_f(a) coshf(a)
#define hv_tan_f(a) tanf(a)
#define hv_tanh_f(a) tanhf(a)
#define hv_asin_f(a) asinf(a)
#define hv_asinh_f(a) asinhf(a)
#define hv_acos_f(a) acosf(a)
#define hv_acosh_f(a) acoshf(a)
#define hv_atan_f(a) atanf(a)
#define hv_atanh_f(a) atanhf(a)
#define hv_atan2_f(a, b) atan2f(a, b)
#define hv_abs_f(a) fabsf(a)
#if HV_ANDROID
// NOTE(mhroth): for whatever silly reason, log2f is not defined!
#define hv_log2_f(a) (1.44269504088896f*logf(a))
#else
#define hv_log2_f(a) log2f(a)
#endif // HV_ANDROID
#define hv_log10_f(a) log10f(a)
#define hv_ceil_f(a) ceilf(a)
#define hv_floor_f(a) floorf(a)
#define hv_round_f(a) roundf(a)
#if HV_EMSCRIPTEN || defined ARM_CORTEX
#define hv_fma_f(a, b, c) ((a*b)+c) // emscripten does not support fmaf (yet?). Inefficient on ARM Cortex M
#else
#define hv_fma_f(a, b, c) fmaf(a, b, c)
#endif
#if HV_MSVC
// finds ceil(log2(x))
#include
static inline hv_uint32_t __hv_utils_min_max_log2(hv_uint32_t x) {
unsigned long z = 0;
_BitScanReverse(&z, x);
return (hv_uint32_t) (z+1);
}
#else
static inline hv_uint32_t __hv_utils_min_max_log2(hv_uint32_t x) {
return (hv_uint32_t) (32 - __builtin_clz(x-1));
}
#endif
#define hv_min_max_log2(a) __hv_utils_min_max_log2(a)
#define hv_if_f(a, b, c) ((a) ? (b) : (c))
#define hv_modf_f(a) fmodf(a, 1.0f)
#define hv_cbrt_f(a) cbrtf(a)
#define hv_copysign_f(a, b) copysignf(a, b)
#define hv_remainder_f(a, b) remainderf(a, b)
#define hv_erf_f(a) erff(a)
#define hv_erfc_f(a) erfcf(a)
#define hv_expm1_f(a) expm1f(a)
#define hv_finite_f(a) isfinite(a)
#define hv_fmod_f(a, b) fmodf(a, b)
#define hv_ldexp_f(a, b) ldexpf(a, b)
#define hv_isinf_f(a) isinf(a)
#define hv_isnan_f(a) isnan(a)
#define hv_ln_f(a) logf(a)
#define hv_log10_f(a) log10f(a)
#define hv_log1p_f(a) log1pf(a)
#define hv_rint_f(a) rintf(a)
#define hv_shl_i(a, b) ((a) << (b))
#define hv_shr_i(a, b) ((a) >> (b))
#define hv_bit_not_i(a) ~a
#define hv_not_f(a) !a
// Atomics
#if HV_WIN
#include
#define hv_atomic_bool volatile LONG
#define HV_SPINLOCK_ACQUIRE(_x) while (InterlockedCompareExchange(&_x, true, false)) { }
#define HV_SPINLOCK_TRY(_x) return !InterlockedCompareExchange(&_x, true, false)
#define HV_SPINLOCK_RELEASE(_x) (_x = false)
#elif HV_ANDROID
// Android support for atomics isn't that great, we'll do it manually
// https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html
#define hv_atomic_bool hv_uint8_t
#define HV_SPINLOCK_ACQUIRE(_x) while (__sync_lock_test_and_set(&_x, 1))
#define HV_SPINLOCK_TRY(_x) return !__sync_lock_test_and_set(&_x, 1)
#define HV_SPINLOCK_RELEASE(_x) __sync_lock_release(&_x)
#elif defined ARM_CORTEX || HV_EMSCRIPTEN
/* no spinlock if we don't have pre-emptive scheduling */
#define hv_atomic_bool volatile bool
#define HV_SPINLOCK_ACQUIRE(_x) { extern volatile bool _msgLock; _msgLock = true; }
#define HV_SPINLOCK_TRY(_x) { extern volatile bool _msgLock; return !_msgLock; }
#define HV_SPINLOCK_RELEASE(_x) { extern volatile bool _msgLock; _msgLock = false; }
#elif __cplusplus
#include
#define hv_atomic_bool std::atomic_flag
#define HV_SPINLOCK_ACQUIRE(_x) while (_x.test_and_set(std::memory_order_acquire))
#define HV_SPINLOCK_TRY(_x) return !_x.test_and_set(std::memory_order_acquire)
#define HV_SPINLOCK_RELEASE(_x) _x.clear(std::memory_order_release)
#elif defined(__has_include)
#if __has_include()
#include
#define hv_atomic_bool atomic_flag
#define HV_SPINLOCK_ACQUIRE(_x) while (atomic_flag_test_and_set_explicit(&_x, memory_order_acquire))
#define HV_SPINLOCK_TRY(_x) return !atomic_flag_test_and_set_explicit(&_x, memory_order_acquire)
#define HV_SPINLOCK_RELEASE(_x) atomic_flag_clear_explicit(memory_order_release)
#endif
#endif
#ifndef hv_atomic_bool
#define hv_atomic_bool volatile bool
#define HV_SPINLOCK_ACQUIRE(_x) \
while (_x) {} \
_x = true;
#define HV_SPINLOCK_TRY(_x) \
if (!_x) { \
_x = true; \
return true; \
} else return false;
#define HV_SPINLOCK_RELEASE(_x) (_x = false)
#endif
#endif // _HEAVY_UTILS_H_
hvcc-0.16.0/hvcc/generators/c2owl/templates/HeavyOwl.hpp 0000644 0000000 0000000 00000020563 00000000000 020005 0 ustar 00 {{copyright}}
#ifndef __HeavyPatch_hpp__
#define __HeavyPatch_hpp__
#include "Patch.h"
#include "basicmaths.h"
#include "HvHeavy.h"
#include "Heavy_{{name}}.hpp"
#include "HeavyOwlConstants.h"
#define BUTTON_Push PUSHBUTTON
#define BUTTON_Green GREEN_BUTTON
#define BUTTON_Red RED_BUTTON
#define BUTTON_B1 BUTTON_A
#define BUTTON_B2 BUTTON_B
#define BUTTON_B3 BUTTON_C
#define BUTTON_B4 BUTTON_D
#define BUTTON_B5 BUTTON_E
#define BUTTON_B6 BUTTON_F
#define BUTTON_B7 BUTTON_G
#define BUTTON_B8 BUTTON_H
#define BUTTON_B9 BUTTON_I
#define BUTTON_B10 BUTTON_J
#define BUTTON_B11 BUTTON_K
#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 HEAVY_MESSAGE_POOL_SIZE 4 // in kB (default 10kB)
#define HEAVY_MESSAGE_IN_QUEUE_SIZE 1 // in kB (default 2kB)
#define HEAVY_MESSAGE_OUT_QUEUE_SIZE 0 // in kB (default 0kB)
extern "C" {
volatile bool _msgLock = false;
static bool isButtonPressed(PatchButtonId bid){
return getProgramVector()->buttons & (1<buttons |= 1<buttons &= ~(1<setUserData(this);
context->setPrintHook(&owlPrintHook);
context->setSendHook(&owlSendHook);
{% for param, name, typ, namehash, minvalue, maxvalue, defvalue, button in jdata if button == False %}
// {{name}}
registerParameter(PARAMETER_{{param}}, HV_NAME_CHANNEL_{{param}});
setParameterValue(PARAMETER_{{param}}, HV_DEFAULT_CHANNEL_{{param}});
{% endfor %}
}
~HeavyPatch() {
delete context;
}
uint16_t getButtonValue(PatchButtonId bid, const HvMessage *m){
if(hv_msg_getNumElements(m) > 0 && hv_msg_isFloat(m, 0))
return hv_msg_getFloat(m, 0) > 0.5 ? 4095 : 0;
else
return isButtonPressed(bid) ? 0 : 4095; // toggle
}
void sendCallback(uint32_t sendHash, const HvMessage *m){
switch(sendHash){
case HV_HASH_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"
// debugMessage("noteout", note, velocity, ch);
sendMidi(MidiMessage::note(ch, note, velocity));
}
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;
// debugMessage("ctlout", value, cc, ch);
sendMidi(MidiMessage::cc(ch, cc, value));
}
break;
case HV_HASH_BENDOUT:
{
uint16_t value = hv_msg_getFloat(m, 0);
uint8_t ch = hv_msg_getFloat(m, 1);
ch %= 16;
// debugMessage("bendout", value, ch);
sendMidi(MidiMessage::pb(ch, value));
}
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;
sendMidi(MidiMessage::at(ch, note, value));
}
break;
case HV_HASH_TOUCHOUT:
{
uint8_t value = hv_msg_getFloat(m, 0);
uint8_t ch = hv_msg_getFloat(m, 1);
ch %= 16;
sendMidi(MidiMessage::cp(ch, value));
}
break;
case HV_HASH_PGMOUT:
{
uint8_t value = hv_msg_getFloat(m, 0);
uint8_t ch = hv_msg_getFloat(m, 1);
ch %= 16;
sendMidi(MidiMessage::pc(ch, value));
}
break;
{% for param, name, typ, namehash, minvalue, maxvalue, defvalue, button in jdata if typ == 'SEND'%}
{% if button == True %}
// Button {{name}}
case HV_HASH_{{typ}}_CHANNEL_{{param}}:
setButton(BUTTON_{{param}}, (hv_msg_getFloat(m, 0)-HV_MIN_CHANNEL_{{param}})/
(HV_MAX_CHANNEL_{{param}}-HV_MIN_CHANNEL_{{param}}) > 0.5);
{% else %}
// Parameter {{name}}
case HV_HASH_{{typ}}_CHANNEL_{{param}}:
setParameterValue(PARAMETER_{{param}}, (hv_msg_getFloat(m, 0)-HV_MIN_CHANNEL_{{param}})/
(HV_MAX_CHANNEL_{{param}}-HV_MIN_CHANNEL_{{param}}));
{% endif %}
break;
{% endfor %}
default:
break;
}
}
void processMidi(MidiMessage msg){
// sendMessageToReceiverV parses format and loops over args, see HeavyContext.cpp
switch(msg.getStatus()){
case CONTROL_CHANGE:
context->sendMessageToReceiverV(HV_HASH_CTLIN, 0, "fff",
(float)msg.getControllerValue(), // value
(float)msg.getControllerNumber(), // controller number
(float)msg.getChannel());
break;
case NOTE_ON:
context->sendMessageToReceiverV(HV_HASH_NOTEIN, 0, "fff",
(float)msg.getNote(), // pitch
(float)msg.getVelocity(), // velocity
(float)msg.getChannel());
break;
case NOTE_OFF:
context->sendMessageToReceiverV(HV_HASH_NOTEIN, 0, "fff",
(float)msg.getNote(), // pitch
0.0f, // velocity
(float)msg.getChannel());
break;
case POLY_KEY_PRESSURE:
context->sendMessageToReceiverV(HV_HASH_POLYTOUCHIN, 0, "fff",
(float)msg.getPolyKeyPressure(),
(float)msg.getNote(),
(float)msg.getChannel());
break;
case CHANNEL_PRESSURE:
context->sendMessageToReceiverV(HV_HASH_TOUCHIN, 0, "ff",
(float)msg.getChannelPressure(),
(float)msg.getChannel());
break;
case PITCH_BEND_CHANGE:
context->sendMessageToReceiverV(HV_HASH_BENDIN, 0, "ff",
(float)msg.getPitchBend(),
(float)msg.getChannel());
break;
case PROGRAM_CHANGE:
context->sendMessageToReceiverV(HV_HASH_PGMIN, 0, "ff",
(float)msg.getProgramChange(),
(float)msg.getChannel());
break;
default:
break;
}
}
void buttonChanged(PatchButtonId bid, uint16_t value, uint16_t samples){
if(_msgLock)
return;
switch(bid){
{% for param, name, typ, namehash, minvalue, maxvalue, defvalue, button in jdata if typ == 'RECV' and button == True %}
// {{name}}
case BUTTON_{{param}}:
context->sendFloatToReceiver(HV_HASH_{{typ}}_CHANNEL_{{param}}, isButtonPressed(BUTTON_{{param}})*
(HV_MAX_CHANNEL_{{param}}-HV_MIN_CHANNEL_{{param}})+HV_MIN_CHANNEL_{{param}});
break;
{% endfor %}
default:
break;
}
}
void processAudio(AudioBuffer &buffer) {
_msgLock = true;
{% for param, name, typ, namehash, minvalue, maxvalue, defvalue, button in jdata if typ == 'RECV' and button == False %}
// {{name}}
context->sendFloatToReceiver(HV_HASH_{{typ}}_CHANNEL_{{param}}, getParameterValue(PARAMETER_{{param}})*
(HV_MAX_CHANNEL_{{param}}-HV_MIN_CHANNEL_{{param}})+HV_MIN_CHANNEL_{{param}});
{% endfor %}
_msgLock = false;
float* outputs[] = {buffer.getSamples(LEFT_CHANNEL), buffer.getSamples(RIGHT_CHANNEL)};
context->process(outputs, outputs, getBlockSize());
}
private:
HeavyContext* context;
};
static void owlSendHook(HeavyContextInterface* ctxt, const char *receiverName, uint32_t sendHash, const HvMessage *m){
HeavyPatch* patch = (HeavyPatch*)ctxt->getUserData();
patch->sendCallback(sendHash, m);
}
#endif // __HeavyPatch_hpp__
hvcc-0.16.0/hvcc/generators/c2owl/templates/HeavyOwlConstants.h 0000644 0000000 0000000 00000000777 00000000000 021347 0 ustar 00 {{copyright}}
{% for param, name, typ, namehash, minvalue, maxvalue, defvalue, button in jdata %}
// {{param}} {{name}} {{typ}} {{namehash}} {{minvalue}} {{maxvalue}} {{defvalue}} {{(defvalue-minvalue)/(maxvalue-minvalue)}}
#define HV_NAME_CHANNEL_{{param}} "{{name}}"
#define HV_HASH_{{typ}}_CHANNEL_{{param}} {{namehash}}
#define HV_MIN_CHANNEL_{{param}} {{minvalue}}
#define HV_MAX_CHANNEL_{{param}} {{maxvalue}}
#define HV_DEFAULT_CHANNEL_{{param}} {{(defvalue-minvalue)/(maxvalue-minvalue)}}
{% endfor %}
hvcc-0.16.0/hvcc/generators/c2pdext/__init__.py 0000644 0000000 0000000 00000000000 00000000000 016154 0 ustar 00 hvcc-0.16.0/hvcc/generators/c2pdext/c2pdext.py 0000644 0000000 0000000 00000010217 00000000000 016001 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2021-2026 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 shutil
import time
import jinja2
from typing import Optional
from pathlib import Path
from ..copyright import copyright_manager
from ..filters import filter_max
from hvcc.interpreters.pd2hv.NotificationEnum import NotificationEnum
from hvcc.types.compiler import Generator, CompilerResp, CompilerNotif, CompilerMsg, ExternInfo
from hvcc.types.meta import Meta
class c2pdext(Generator):
"""Generates a Pure Data external wrapper for a given patch.
"""
@classmethod
def compile(
cls,
c_src_dir: Path,
out_dir: Path,
externs: ExternInfo,
patch_name: str,
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 = Path(out_dir, "pdext")
receiver_list = externs.parameters.inParam
copyright = copyright_manager.get_copyright_for_c(copyright)
patch_name = patch_name or "heavy"
ext_name = f"{patch_name}~"
struct_name = f"{patch_name}_tilde"
# ensure that the output directory does not exist
out_dir = out_dir.absolute()
if out_dir.exists():
shutil.rmtree(out_dir)
# copy over generated C source files
shutil.copytree(c_src_dir, out_dir)
# copy over static files
shutil.copy(
Path(Path(__file__).parent, "static", "m_pd.h"),
f"{out_dir}/")
shutil.copy(
Path(Path(__file__).parent, "static", "Makefile.pdlibbuilder"),
f"{out_dir}/../")
try:
# initialise the jinja template environment
env = jinja2.Environment()
env.filters["max"] = filter_max
env.loader = jinja2.FileSystemLoader(
Path(Path(__file__).parent, "templates"))
# generate Pd external wrapper from template
pdext_path = Path(out_dir, f"{ext_name}.c")
with open(pdext_path, "w") as f:
f.write(env.get_template("pd_external.c").render(
name=patch_name,
struct_name=struct_name,
display_name=ext_name,
num_input_channels=num_input_channels,
num_output_channels=num_output_channels,
receivers=receiver_list,
copyright=copyright))
# generate Makefile from template
pdext_path = Path(out_dir, "../Makefile")
with open(pdext_path, "w") as f:
f.write(env.get_template("Makefile").render(
name=patch_name))
return CompilerResp(
stage="c2pdext",
in_dir=c_src_dir,
out_dir=out_dir,
out_file=pdext_path,
compile_time=time.time() - tick
)
except Exception as e:
return CompilerResp(
stage="c2pdext",
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
)
hvcc-0.16.0/hvcc/generators/c2pdext/static/Makefile.pdlibbuilder 0000644 0000000 0000000 00000131451 00000000000 021451 0 ustar 00 # Makefile.pdlibbuilder dated 2019-12-21
version = 0.7.0
# Helper makefile for Pure Data external libraries.
# Written by Katja Vetter March-June 2015 for the public domain. No warranties.
# Inspired by Hans Christoph Steiner's Makefile Template and Stephan Beal's
# ShakeNMake.
#
# Grab the newest version of Makefile.pdlibbuilder from
# https://github.com/pure-data/pd-lib-builder/
#
# GNU make version >= 3.81 required.
#
#
#=== characteristics ===========================================================
#
#
# - defines build settings based on autodetected OS and architecture
# - defines rules to build Pd class- or lib executables from C or C++ sources
# - defines rules for libdir installation
# - defines convenience targets for developer and user
# - evaluates implicit dependencies for non-clean builds
#
#
#=== basic usage ===============================================================
#
#
# In your Makefile, define your Pd lib name and class files, and include
# Makefile.pdlibbuilder at the end of the Makefile. Like so:
#
# ________________________________________________________________________
#
# # Makefile for mylib
#
# lib.name = mylib
#
# class.sources = myclass1.c myclass2.c
#
# datafiles = myclass1-help.pd myclass2-help.pd README.txt LICENSE.txt
#
# include Makefile.pdlibbuilder
# ________________________________________________________________________
#
#
# For files in class.sources it is assumed that class basename == source file
# basename. The default target builds all classes as individual executables
# with Pd's default extension for the platform. For anything more than the
# most basic usage, continue reading.
#
#
#=== list of Makefile.pdlibbuilder API variables ===============================
#
#
# Variables available for definition in your library Makefile:
#
# - lib.name
# - lib.setup.sources
# - class.sources
# - common.sources
# - shared.sources
# - .class.sources
# - .class.ldflags
# - .class.ldlibs
# - cflags
# - ldflags
# - ldlibs
# - datafiles
# - datadirs
# - makefiles
# - makefiledirs
# - externalsdir
#
# Optional multiline defines evaluated per operating system:
#
# - forLinux
# - forDarwin
# - forWindows
#
# Variables available for your makefile or make command line:
#
# - make-lib-executable
# - suppress-wunused
#
# Path variables for make command line or environment:
#
# - PDDIR
# - PDINCLUDEDIR
# - PDBINDIR
# - PDLIBDIR
#
# Standard make variables for make command line or environment:
#
# - CPPFLAGS
# - CFLAGS
# - LDFLAGS
# - CC
# - CXX
# - INSTALL
# - STRIP
# - DESTDIR
#
# Optional user variables for make command line or environment:
#
# - PLATFORM
# - extension
# - floatsize
#
# Deprecated path variables:
#
# - pdincludepath
# - pdbinpath
# - objectsdir
#
#
#=== descriptions of Makefile.pdlibbuilder API variables =======================
#
#
# lib.name:
# Name of the library directory as it will be installed / distributed. Also the
# name of the lib executable in the case where all classes are linked into
# a single binary.
#
# lib.setup.sources:
# Source file(s) (C or C++) which must be compiled only when linking all classes
# into a single lib binary.
#
# class.sources:
# All sources files (C or C++) for which the condition holds that
# class name == source file basename.
#
# .class.sources:
# Source file(s) (C or C++) specific to class . Use this for
# multiple-source classes or when class name != source file basename.
#
# common.sources:
# Source file(s) which must be statically linked to each class in the library.
#
# shared.sources:
# Source file(s) (C or C++) to build a shared dynamic link lib, to be linked
# with all class executables.
#
# cflags, ldflags, ldlibs:
# Define cflags (preprocessor&compiler), ldflags (linker) and ldlibs (dynamic
# link libs) for the whole library. These flags are added to platform-specific
# flags defined by Makefile.pdlibbuilder.
#
# .class.ldflags and .class.ldlibs:
# Define ldflags resp. ldlibs specific to class . These flags are
# added to platform-specific flags defined by Makefile.pdlibbuilder, and flags
# defined in your Makefile for the whole library. Note: cflags can not be
# defined per class in the current implementation.
#
# datafiles and datadirs:
# All extra files you want to include in binary distributions of the
# library: abstractions and help patches, example patches, meta patch, readme
# and license texts, manuals, sound files, etcetera. Use 'datafiles' for all
# files that should go into your lib rootdir and 'datadirs' for complete
# directories you want to copy from source to distribution.
#
# forLinux, forDarwin, forWindows:
# Shorthand for 'variable definitions for Linux only' etc. Use like:
# define forLinux
# cflags += -DLINUX
# class.sources += linuxthing.c
# endef
#
# makefiles and makefiledirs:
# Extra makefiles or directories with makefiles that should be made in sub-make
# processes.
#
# make-lib-executable:
# When this variable is defined 'yes' in your makefile or as command argument,
# Makefile.pdlibbuilder will try to build all classes into a single library
# executable (but it will force exit if lib.setup.sources is undefined).
# If your makefile defines 'make-lib-executable=yes' as the library default,
# this can still be overridden with 'make-lib-executable=no' as command argument
# to build individual class executables (the Makefile.pdlibbuilder default.)
#
# suppress-wunused:
# When this variable is defined ('yes' or any other value), -Wunused-variable,
# -Wunused-parameter, -Wunused-value and -Wunused-function are suppressed,
# but the other warnings from -Wall are retained.
#
# PDDIR:
# Root directory of 'portable' pd package. When defined, PDINCLUDEDIR and
# PDBINDIR will be evaluated as $(PDDIR)/src and $(PDDIR)/bin.
#
# PDINCLUDEDIR:
# Directory where Pd API m_pd.h should be found, and other Pd header files.
# Overrides the default search path.
#
# PDBINDIR:
# Directory where pd.dll should be found for linking (Windows only). Overrides
# the default search path.
#
# PDLIBDIR:
# Root directory for installation of Pd library directories. Overrides the
# default install location.
#
# DESTDIR:
# Prepended path component for staged install.
#
# PLATFORM:
# Target platform for cross compilation in the form of GNU triplet:
# cpu-vendor-os. Example: x86_64-w64-mingw32. This specifies the tool chain that
# pdlibbuilder will use, if installed and locatable. System and architecture
# will then be autodefined accordingly. In most cases no other variables need to
# be overridden.
#
# extension:
# Extension for the external to use. Example: m_amd64
# A sane default is picked, but it is useful if you want to provide
# co-installable externals for multiple platforms (for the same operating
# systems)
#
# floatsize:
# the size of the t_float in bits. Example: 32
# t_float are usually single precision (32bit), which is the default.
# For double precision use floatsize=64
# When building double precision externals, you will want to set the extension
# as well, e.g. extension=windows-amd64-64.dll (--.)
#
# CPPFLAGS:
# Preprocessor flags which are not strictly required for building.
#
# CFLAGS:
# Compiler flags which are not strictly required for building. Compiler flags
# defined by Makefile.pdlibbuilder for warning, optimization and architecture
# specification are overriden by CFLAGS.
#
# LDFLAGS:
# Linker flags which are not strictly required for building. Linker flags
# defined by Makefile.pdlibbuilder for architecture specification are overriden
# by LDFLAGS.
#
# CC and CXX:
# C and C++ compiler programs as defined in your build environment.
#
# INSTALL
# Definition of install program.
#
# STRIP
# Name of strip program. Default 'strip' can be overridden in cross compilation
# environments.
#
# objectsdir:
# Root directory for installation of Pd library directories, like PDLIBDIR but
# not overridable by environment. Supported for compatibility with pd-extended
# central makefile, but deprecated otherwise.
#
# pdincludepath, pdbinpath:
# As PDINCLUDEDIR and PDBINDIR but not overridable by environment. Deprecated
# as user variables.
#
#
#=== paths =====================================================================
#
#
# Source files in directories other than current working directory must be
# prefixed with their relative path. Do not rely on VPATH or vpath.
# Object (.o) files are built in the directory of their source files.
# Executables are built in current working directory.
#
# Default search path for m_pd.h and other API header files is platform
# dependent, and overridable by PDINCLUDEDIR:
#
# Linux: /usr/include/pd
#
# OSX: /Applications/Pd*.app/Contents/Resources/src
#
# Windows: %PROGRAMFILES%/Pd/src
# %PROGRAMFILES(X86)%/Pd/src (32 bit builds on 64 bit Windows)
#
# Default search path for binary pd.dll (Windows), overridable by PDBINDIR
#
# %PROGRAMFILES%/Pd/bin
# %PROGRAMFILES(X86)%/Pd/bin (32 bit builds on 64 bit Windows)
#
# Default location to install pd libraries is platform dependent, and
# overridable by PDLIBDIR:
#
# Linux: /usr/local/lib/pd-externals
# OSX: ~/Library/Pd
# Windows: %APPDATA%/Pd
#
# https://puredata.info/docs/faq/how-do-i-install-externals-and-help-files
# The rationale for not installing to ~/pd-externals by default on Linux
# is that some people share the home dir between 32 and 64 bit installations.
#
#
#=== targets ===================================================================
#
#
# all: build $(executables) plus optional post target
# post: target to build after $(executables)
# alldebug: build all with -g option turned on for debug symbols
# : force clean build of an individual class
# .pre: make preprocessor output file in current working directory
# .lst: make asm/source output file in current working directory
#
# install: install executables and data files
# clean: remove build products from source tree
#
# help: print help text
# vars: print makefile variables
# allvars: print all variables
# depend: print generated prerequisites
# dumpmachine: print compiler output of option '-dumpmachine'
# coffee: dummy target
#
# Variable $(executables) expands to class executables plus optional shared lib,
# or alternatively to single lib executable when make-lib-executable=true.
# Targets pre and post can be defined by library makefile. Make sure to include
# Makefile.pdlibbuilder first so default target all will not be redefined.
#
#
#=== Pd-extended libdir concept ================================================
#
#
# For libdir layout as conceived by Hans-Christoph Steiner, see:
#
# https://puredata.info/docs/developer/Libdir
#
# Files README.txt, LICENSE.txt and -meta.pd are part of the libdir
# convention. Help patches for each class and abstraction are supposed to be
# available. Makefile.pdlibbuilder does not force the presence of these files
# however. It does not automatically include such files in libdir installations.
# Data files you want to include in distributions must be defined explicitly in
# your Makefile.
#
#
#=== Makefile.pdlibbuilder syntax conventions ==================================
#
#
# Makefile.pdlibbuilder variable names are lower case. Default make variables,
# environment variables, and standard user variables (CC, CXX, CFLAGS, DESTDIR)
# are upper case. Use target 'allvars' to print all variables and their values.
#
# 'Fields' in data variables are separated by dots, like in 'foo.class.sources'.
# Words in variables expressing a function or command are separated by dashes,
# like in 'make-lib-executable'.
#
#
#=== useful make options =======================================================
#
#
# Use 'make -d ' to print debug details of the make process.
# Use 'make -p ' to print make's database.
#
#
#=== TODO ======================================================================
#
#
# - decide whether to use -static-libgcc or shared dll in MinGW
# - cygwin support
# - android support
# - figure out how to handle '$' in filenames
# - add makefile template targets dpkg-source dist libdir distclean tags?
#
#
#=== end of documentation sections =============================================
#
#
################################################################################
################################################################################
################################################################################
# GNU make version 3.81 (2006) or higher is required because of the following:
# - function 'info'
# - variable '.DEFAULT_GOAL'
# force exit when make version is < 3.81
ifneq ($(firstword $(sort 3.81 $(MAKE_VERSION))), 3.81)
$(error GNU make version 3.81 or higher is required)
endif
# Relative path to externals root dir in multi-lib source tree like
# pd-extended SVN. Default is parent of current working directory. May be
# defined differently in including makefile.
externalsdir ?= ..
# variable you can use to check if Makefile.pdlibbuilder is already included
Makefile.pdlibbuilder = true
################################################################################
### target platform detection ##################################################
################################################################################
#=== target platform ===========================================================
# PLATFORM: optional user variable to define target platform for cross
# compilation. Redefine build tools accordingly. PLATFORM should match
# the exact target prefix of tools present in $PATH, like x86_64-w64-mingw32,
# x86_64-apple-darwin12 etc. Tool definitions are exported to ensure submakes
# will get the same.
ifneq ($(PLATFORM),)
ifneq ($(findstring darwin, $(PLATFORM)),)
export CC = $(PLATFORM)-cc
export CXX = $(PLATFORM)-c++
export CPP = $(PLATFORM)-cc
else
export CC = $(PLATFORM)-gcc
export CXX = $(PLATFORM)-g++
export CPP = $(PLATFORM)-cpp
endif
STRIP = $(PLATFORM)-strip
endif
# Let (native or cross-) compiler report target triplet and isolate individual
# words therein to facilitate later processing.
target.triplet := $(subst -, ,$(shell $(CC) -dumpmachine))
#=== operating system ==========================================================
# The following systems are defined: Linux, Darwin, Windows. GNU and
# GNU/kFreeBSD are treated as Linux to get the same options.
ifneq ($(filter linux gnu% kfreebsd, $(target.triplet)),)
system = Linux
endif
ifneq ($(filter darwin%, $(target.triplet)),)
system = Darwin
endif
ifneq ($(filter mingw% cygwin%, $(target.triplet)),)
system = Windows
endif
# evaluate possible system-specific multiline defines from library makefile
$(eval $(for$(system)))
# TODO: Cygwin, Android
#=== architecture ==============================================================
# The following CPU names can be processed by pdlibbuilder:
# i*86 Intel 32 bit
# x86_64 Intel 64 bit
# arm ARM 32 bit
# aarch64 ARM 64 bit
target.arch := $(firstword $(target.triplet))
################################################################################
### variables per platform #####################################################
################################################################################
#=== flags per floatsize == ====================================================
floatsize = 32
ifneq ($(filter-out 32,$(floatsize)),)
floatsize.flags = -DPD_FLOATSIZE=$(floatsize)
else
floatsize.flags =
endif
#=== flags per architecture ====================================================
# Set architecture-dependent cflags, mainly for Linux. For Mac and Windows,
# arch.c.flags are overriden below. To see gcc's default architecture flags:
# $ gcc -Q --help=target
# ARMv6: Raspberry Pi 1st gen, not detectable from target.arch
ifeq ($(shell uname -m), armv6l)
arch.c.flags = -march=armv6 -mfpu=vfp -mfloat-abi=hard
# ARMv7: Beagle, Udoo, RPi2 etc.
else ifeq ($(target.arch), arm)
arch.c.flags = -march=armv7-a -mfpu=vfpv3 -mfloat-abi=hard
# ARMv8 64 bit, not tested yet
else ifeq ($(target.arch), aarch64)
arch.c.flags = -mcpu=cortex-a53
# Intel 32 bit, build with SSE and SSE2 instructions
else ifneq ($(filter i%86, $(target.arch)),)
arch.c.flags = -march=pentium4 -mfpmath=sse -msse -msse2
# Intel/AMD 64 bit, build with SSE, SSE2 and SSE3 instructions
else ifeq ($(target.arch), x86_64)
arch.c.flags = -march=core2 -mfpmath=sse -msse -msse2 -msse3
# if none of the above architectures detected
else
arch.c.flags =
endif
#=== flags and paths for Linux =================================================
ifeq ($(system), Linux)
prefix = /usr/local
libdir := $(prefix)/lib
pkglibdir = $(libdir)/pd-externals
pdincludepath := $(wildcard /usr/include/pd)
extension = pd_linux
cpp.flags := -DUNIX
c.flags := -fPIC
c.ldflags := -rdynamic -shared -fPIC -Wl,-rpath,"\$$ORIGIN",--enable-new-dtags
c.ldlibs := -lc -lm
cxx.flags := -fPIC -fcheck-new
cxx.ldflags := -rdynamic -shared -fPIC -Wl,-rpath,"\$$ORIGIN",--enable-new-dtags
cxx.ldlibs := -lc -lm -lstdc++
shared.extension = so
shared.ldflags = -rdynamic -fPIC -shared -Wl,-soname,$(shared.lib)
endif
#=== flags and paths for Darwin ================================================
# LLVM-clang doesn't support -fcheck-new, therefore this flag is only used when
# compiling with g++.
ifeq ($(system), Darwin)
pkglibdir = $(HOME)/Library/Pd
pdincludepath := $(firstword $(wildcard \
/Applications/Pd*.app/Contents/Resources/src))
extension = pd_darwin
cpp.flags := -DUNIX -DMACOSX -I /sw/include
c.flags :=
c.ldflags := -undefined suppress -flat_namespace -bundle
c.ldlibs := -lc
cxx.ldflags := -undefined suppress -flat_namespace -bundle
cxx.ldlibs := -lc
shared.extension = dylib
shared.ldflags = -dynamiclib -undefined dynamic_lookup \
-install_name @loader_path/$(shared.lib) \
-compatibility_version 1 -current_version 1.0
ifneq ($(filter %g++, $(CXX)),)
cxx.flags := -fcheck-new
endif
ifeq ($(extension), d_fat)
arch := i386 x86_64
else
arch := $(target.arch)
endif
ifneq ($(filter -mmacosx-version-min=%, $(cflags)),)
version.flag := $(filter -mmacosx-version-min=%, $(cflags))
else
version.flag = -mmacosx-version-min=10.6
endif
arch.c.flags := $(addprefix -arch , $(arch)) $(version.flag)
arch.ld.flags := $(arch.c.flags)
endif
#=== flags and paths for Windows ===============================================
# Standard paths on Windows contain spaces, and GNU make functions treat such
# paths as lists, with unintended effects. Therefore we must use shell function
# ls instead of make's wildcard when probing for a path, and use double quotes
# when specifying a path in a command argument.
# Default paths in Mingw / Mingw-w64 environments. 'PROGRAMFILES' is standard
# location for builds with native architecture, 'ProgramFiles(x86)' for i686
# builds on x86_64 Windows (detection method by Lucas Cordiviola). Curly braces
# required because of parentheses in variable name.
ifeq ($(system), Windows)
pkglibdir := $(APPDATA)/Pd
ifeq ($(target.arch), i686)
programfiles := ${ProgramFiles(x86)}
else
programfiles := $(PROGRAMFILES)
endif
pdbinpath := $(programfiles)/Pd/bin
pdincludepath := $(programfiles)/Pd/src
endif
# Store default path to pd.dll in PDBINDIR if the latter is not user-defined.
# For include path this is done in the platform-independent paths section below,
# but for PDBINDIR it is done here so ld flags can be evaluated as immediate
# variables.
ifeq ($(system), Windows)
ifdef PDDIR
PDBINDIR := $(PDDIR)/bin
endif
PDBINDIR ?= $(pdbinpath)
endif
# TODO: decide whether -mms-bitfields should be specified.
ifeq ($(system), Windows)
cpp.flags := -DMSW -DNT
ifeq ($(target.arch), i686)
arch.c.flags := -march=pentium4 -msse -msse2 -mfpmath=sse
else ifeq ($(target.arch), x86_64)
cpp.flags := -DMSW -DNT -DPD_LONGINTTYPE=__int64
arch.c.flags := -march=core2 -msse -msse2 -msse3 -mfpmath=sse
else
arch.c.flags =
endif
extension = dll
c.flags :=
c.ldflags := -static-libgcc -shared \
-Wl,--enable-auto-import "$(PDBINDIR)/pd$(filter-out 32,$(floatsize)).dll"
c.ldlibs :=
cxx.flags := -fcheck-new
cxx.ldflags := -static-libgcc -static-libstdc++ -shared \
-Wl,--enable-auto-import "$(PDBINDIR)/pd$(filter-out 32,$(floatsize)).dll"
cxx.ldlibs :=
shared.extension = dll
shared.ldflags := -static-libgcc -shared "$(PDBINDIR)/pd$(filter-out 32,$(floatsize)).dll"
stripflags = --strip-all
endif
#=== paths =====================================================================
# Platform-dependent default paths are specified above, but overridable.
# Path variables in upper case can be defined as make command argument or in the
# environment. Variable 'objectsdir' is supported for compatibility with
# the build system that pd-l2ork has inherited from pd-extended.
PDINCLUDEDIR ?= $(pdincludepath)
PDLIBDIR ?= $(firstword $(objectsdir) $(pkglibdir))
ifdef PDDIR
PDINCLUDEDIR := $(wildcard $(PDDIR)/src)
endif
# base path where all components of the lib will be installed by default
installpath := $(DESTDIR)$(PDLIBDIR)/$(lib.name)
# check if include path contains spaces (as is often the case on Windows)
# if so, store the path so we can later do checks with it
pdincludepathwithspaces := $(if $(word 2, $(PDINCLUDEDIR)), $(PDINCLUDEDIR))
#=== accumulated build flags ===================================================
# From GNU make docs: 'Users expect to be able to specify CFLAGS freely
# themselves.' So we use CFLAGS to define options which are not strictly
# required for compilation: optimizations, architecture specifications, and
# warnings. CFLAGS can be safely overriden using a make command argument.
# Variables cflags, ldflags and ldlibs may be defined in including makefile.
optimization.flags = -O3 -ffast-math -funroll-loops -fomit-frame-pointer
warn.flags = -Wall -Wextra -Wshadow -Winline -Wstrict-aliasing
# suppress -Wunused-variable & Co if you don't want to clutter a build log
ifdef suppress-wunused
warn.flags += $(addprefix -Wno-unused-, function parameter value variable)
endif
CFLAGS = $(warn.flags) $(optimization.flags) $(arch.c.flags)
# preprocessor flags
cpp.flags := -DPD -I "$(PDINCLUDEDIR)" $(floatsize.flags) $(cpp.flags) $(CPPFLAGS)
# flags for dependency checking (cflags from makefile may define -I options)
depcheck.flags := $(cpp.flags) $(cflags)
# architecture specifications for linker are overridable by LDFLAGS
LDFLAGS := $(arch.ld.flags)
# now add the same ld flags to shared dynamic lib
shared.ldflags += $(LDFLAGS)
# accumulated flags for C compiler / linker
c.flags := $(cpp.flags) $(c.flags) $(cflags) $(CFLAGS)
c.ldflags := $(c.ldflags) $(ldflags) $(LDFLAGS)
c.ldlibs := $(c.ldlibs) $(ldlibs)
# accumulated flags for C++ compiler / linker
cxx.flags := $(cpp.flags) $(cxx.flags) $(cflags) $(CFLAGS)
cxx.ldflags := $(cxx.ldflags) $(ldflags) $(LDFLAGS)
cxx.ldlibs := $(cxx.ldlibs) $(ldlibs)
################################################################################
### variables: library name and version ########################################
################################################################################
# strip possibles spaces from lib.name, they mess up calculated file names
lib.name := $(strip $(lib.name))
# if meta file exists, check library version
metafile := $(wildcard $(lib.name)-meta.pd)
ifdef metafile
lib.version := $(shell sed -n \
's|^\#X text [0-9][0-9]* [0-9][0-9]* VERSION \(.*\);|\1|p' \
$(metafile))
endif
################################################################################
### variables: files ###########################################################
################################################################################
object.extension = $(extension).o
#=== sources ===================================================================
# (re)define .class.sources using file names in class.sources
define add-class-source
$(notdir $(basename $v)).class.sources += $v
endef
$(foreach v, $(class.sources), $(eval $(add-class-source)))
# derive class names from .class.sources variables
sourcevariables := $(filter %.class.sources, $(.VARIABLES))
classes := $(basename $(basename $(sourcevariables)))
# accumulate all source files specified in makefile
classes.sources := $(sort $(foreach v, $(sourcevariables), $($v)))
all.sources := $(classes.sources) $(lib.setup.sources) \
$(shared.sources) $(common.sources)
#=== object files ==============================================================
# construct object filenames from all C and C++ source file names
classes.objects := $(addsuffix .$(object.extension), $(basename $(classes.sources)))
common.objects := $(addsuffix .$(object.extension), $(basename $(common.sources)))
shared.objects := $(addsuffix .$(object.extension), $(basename $(shared.sources)))
lib.setup.objects := $(addsuffix .$(object.extension), $(basename $(lib.setup.sources)))
all.objects = $(classes.objects) $(common.objects) $(shared.objects) \
$(lib.setup.objects)
#=== executables ===============================================================
# construct class executable names from class names
classes.executables := $(addsuffix .$(extension), $(classes))
# Construct shared lib executable name if shared sources are defined.
# If extension does not end with shared extension, use both to facilitate co-
# installation for different platforms, like .m_i386.dll and .linux-amd64-32.so
ifdef shared.sources
ifneq ($(filter %.$(shared.extension), .$(extension)), )
# $(extension) already ends with $(shared.extension), no need to duplicate it
shared.lib = lib$(lib.name).$(extension)
else
shared.lib = lib$(lib.name).$(extension).$(shared.extension)
endif
else
shared.lib :=
endif
################################################################################
### variables: tools ###########################################################
################################################################################
# aliases so we can later define 'compile-$1' and set 'c' or 'cxx' as argument
compile-c := $(CC)
compile-cxx := $(CXX)
################################################################################
### checks #####################################################################
################################################################################
# At this point most variables are defined. Now do some checks and info's
# before rules begin.
# print Makefile.pdlibbuilder version before possible termination
$(info ++++ info: using Makefile.pdlibbuilder version $(version))
# Terminate if target triplet remained empty, to avoid all sorts of confusing
# scenarios and spurious bugs.
ifeq ($(target.triplet),)
$(error Command "$(CC) -dumpmachine" did not return a target triplet, \
needed for a build. \
Is compiler "$(CC)" installed in your PATH? ($(PATH)). \
Does compiler "$(CC)" support option "-dumpmachine"?)
endif
# 'forward declaration' of default target, needed to do checks
all:
# To avoid unpredictable results, make sure the default target is not redefined
# by including makefile.
ifneq ($(.DEFAULT_GOAL), all)
$(error Default target must be 'all'.)
endif
# find out which target(s) will be made
ifdef MAKECMDGOALS
goals := $(MAKECMDGOALS)
else
goals := all
endif
# store path to Pd API m_pd.h if it is found
ifdef PDINCLUDEDIR
mpdh := $(shell ls "$(PDINCLUDEDIR)/m_pd.h")
endif
# store path to pd.dll; if not found, ls will give a useful error
ifeq ($(system), Windows)
pddll := $(shell ls "$(PDBINDIR)/pd$(filter-out 32,$(floatsize)).dll")
endif
# when making target all, check if m_pd.h is found and print info about it
ifeq ($(goals), all)
$(if $(mpdh), \
$(info ++++ info: using Pd API $(mpdh)), \
$(warning Where is Pd API m_pd.h? Do 'make help' for info.))
endif
# print target info
$(info ++++ info: making target $(goals) $(if $(lib.name),in lib $(lib.name)))
# when installing, print installpath info
$(if $(filter install install-lib, $(goals)), $(info ++++ info: \
installpath is '$(installpath)'))
#=== define executables ========================================================
# By default we build class executables, and optionally a shared dynamic link
# lib. When make-lib-executable=yes we build all classes into a single lib
# executable, on the condition that variable lib.setup.sources is defined.
ifeq ($(make-lib-executable),yes)
$(if $(lib.setup.sources), ,\
$(error Can not build library blob because lib.setup.sources is undefined))
executables := $(lib.name).$(extension)
else
executables := $(classes.executables) $(shared.lib)
endif
################################################################################
### rules: special targets #####################################################
################################################################################
# Disable built-in rules. If some target can't be built with the specified
# rules, it should not be built at all.
MAKEFLAGS += --no-builtin-rules
.PRECIOUS:
.SUFFIXES:
.PHONY: all post build-lib \
$(classes) $(makefiledirs) $(makefiles) \
install install-executables install-datafiles install-datadirs \
force clean vars allvars depend help
################################################################################
### rules: build targets #######################################################
################################################################################
# Target all forces the build of targets [$(executables) post] in
# deterministic order. Target $(executables) builds class executables plus
# optional shared lib or alternatively a single lib executable when
# make-lib-executable=true. Target post is optionally defined by
# library makefile.
all: post
post: $(executables)
all:
$(info ++++info: target all in lib $(lib.name) completed)
# build all with -g option turned on for debug symbols
alldebug: c.flags += -g
alldebug: cxx.flags += -g
alldebug: all
#=== class executable ==========================================================
# recipe for linking objects in class executable
# argument $1 = compiler type (c or cxx)
# argument $2 = class basename
define link-class
$(compile-$1) \
$($1.ldflags) $($2.class.ldflags) \
-o $2.$(extension) \
$(addsuffix .$(object.extension), $(basename $($2.class.sources))) \
$(addsuffix .$(object.extension), $(basename $(common.sources))) \
$($1.ldlibs) $($2.class.ldlibs) $(shared.lib)
endef
# general rule for linking object files in class executable
%.$(extension): $(shared.lib)
$(info ++++ info: linking objects in $@ for lib $(lib.name))
$(if $(filter %.cc %.cpp, $($*.class.sources)), \
$(call link-class,cxx,$*), \
$(call link-class,c,$*))
#=== library blob ==============================================================
# build all classes into single executable
build-lib: $(lib.name).$(extension)
$(info ++++ info: library blob $(lib.name).$(extension) completed)
# recipe for linking objects in lib executable
# argument $1 = compiler type (c or cxx)
define link-lib
$(compile-$1) \
$($1.ldflags) $(lib.ldflags) \
-o $(lib.name).$(extension) $(all.objects) \
$($1.ldlibs) $(lib.ldlibs)
endef
# rule for linking objects in lib executable
# declared conditionally to avoid name clashes
ifeq ($(make-lib-executable),yes)
$(lib.name).$(extension): $(all.objects)
$(if $(filter %.cc %.cpp, $(all.sources)), \
$(call link-lib,cxx), \
$(call link-lib,c))
endif
#=== shared dynamic lib ========================================================
# recipe for linking objects in shared executable
# argument $1 = compiler type (c or cxx)
define link-shared
$(compile-$1) \
$(shared.ldflags) \
-o $(shared.lib) $(shared.objects) \
$($1.ldlibs) $(shared.ldlibs)
endef
# rule for linking objects in shared executable
# build recipe is in macro 'link-shared'
$(shared.lib): $(shared.objects)
$(info ++++ info: linking objects in shared lib $@)
$(if $(filter %.cc %.cpp, $(shared.sources)), \
$(call link-shared,cxx), \
$(call link-shared,c))
#=== object files ==============================================================
# recipe to make .o file from source
# argument $1 is compiler type (c or cxx)
define make-object-file
$(info ++++ info: making $@ in lib $(lib.name))
$(compile-$1) \
$($1.flags) \
-o $@ -c $<
endef
# Three rules to create .o files. These are double colon 'terminal' rules,
# meaning they are the last in a rules chain.
%.$(object.extension):: %.c
$(call make-object-file,c)
%.$(object.extension):: %.cc
$(call make-object-file,cxx)
%.$(object.extension):: %.cpp
$(call make-object-file,cxx)
#=== explicit prerequisites for class executables ==============================
# For class executables, prerequisite rules are declared in run time. Target
# 'depend' prints these rules for debugging purposes.
# declare explicit prerequisites rule like 'class: class.extension'
# argument $v is class basename
define declare-class-target
$v: $v.$(extension)
endef
# declare explicit prerequisites rule like 'class.extension: object1.o object2.o'
# argument $v is class basename
define declare-class-executable-target
$v.$(extension): $(addsuffix .$(object.extension), $(basename $($v.class.sources))) \
$(addsuffix .$(object.extension), $(basename $(common.sources)))
endef
# evaluate explicit prerequisite rules for all classes
$(foreach v, $(classes), $(eval $(declare-class-target)))
$(foreach v, $(classes), $(eval $(declare-class-executable-target)))
#=== implicit prerequisites for class executables ==============================
# Evaluating implicit prerequisites (header files) with help from the
# preprocessor is 'expensive' so this is done conditionally and selectively.
# Note that it is also possible to trigger a build via install targets, in
# which case implicit prerequisites are not checked.
# When the Pd include path contains spaces it will mess up the implicit
# prerequisites rules.
disable-dependency-tracking := $(strip $(pdincludepathwithspaces))
ifndef disable-dependency-tracking
must-build-everything := $(filter all, $(goals))
must-build-class := $(filter $(classes), $(goals))
must-build-sources := $(foreach v, $(must-build-class), $($v.class.sources))
endif
# declare implicit prerequisites rule like 'object.o: header1.h header2.h ...'
# argument $1 is input source file(s)
# dir is explicitly added because option -MM strips it by default
define declare-object-target
$(dir $1)$(patsubst %.o:,%.$(object.extension):,$(filter %.o: %.h, $(shell $(CPP) $(depcheck.flags) -MM $1))) $(MAKEFILE_LIST)
endef
# evaluate implicit prerequisite rules when rebuilding everything
ifdef must-build-everything
$(if $(wildcard $(all.objects)), \
$(info ++++ info: evaluating implicit prerequisites in lib $(lib.name).....) \
$(foreach v, $(all.sources), $(eval $(call declare-object-target, $v))))
endif
# evaluate implicit prerequisite rules when selectively building classes
ifdef must-build-class
$(foreach v, $(must-build-sources), \
$(eval $(call declare-object-target, $v)))
$(foreach v, $(shared.sources), \
$(eval $(call declare-object-target, $v)))
endif
################################################################################
### rules: preprocessor and assembly files #####################################
################################################################################
# Preprocessor and assembly output files for bug tracing etc. They are not part
# of the build processes for executables. By default these files are created in
# the current working directory. Dependency tracking is not performed, the build
# is forced instead to make sure it's up to date.
force:
#=== preprocessor file =========================================================
# make preprocessor output file with extension .pre
# argument $1 = compiler type (c or cxx)
define make-preprocessor-file
$(info ++++ info: making preprocessor output file $(notdir $*.pre) \
in current working directory)
$(compile-$1) -E $< $(c.flags) $($1.flags) -o $(notdir $*.pre)
endef
%.pre:: %.c force
$(call make-preprocessor-file,c)
%.pre:: %.cc force
$(call make-preprocessor-file,cxx)
%.pre:: %.cpp force
$(call make-preprocessor-file,cxx)
#=== assembly file =============================================================
# make C / assembly interleaved output file with extension .lst
# argument $1 = compiler type (c or cxx)
define make-assembly-file
$(info ++++ info: making assembly output file $(notdir $*.lst) \
in current working directory)
$(compile-$1) \
-c -Wa,-a,-ad -fverbose-asm \
$($1.flags) \
$< > $(notdir $*.lst)
endef
%.lst:: %.c force
$(call make-assembly-file,c)
%.lst:: %.cc force
$(call make-assembly-file,cxx)
%.lst:: %.cpp force
$(call make-assembly-file,cxx)
################################################################################
### rules: installation targets ################################################
################################################################################
#=== strip =====================================================================
# Stripping of installed binaries will only be done when variable 'stripflags'
# is defined non-empty. No default definition is provided except for Windows
# where the unstripped binaries are large, especially in the case of Mingw-w64.
# Note: while stripping all symbols ('-s' or '--strip-all') is possible for
# Linux and Windows, in the case of OSX only non-global symbols can be stripped
# (option '-x' or '--discard-all').
# Make definition of strip command overridable so it can be defined in an
# environment for cross-compilation.
STRIP ?= strip
# Commands in 'strip-executables' will be executed conditionally in the rule for
# target 'install-executables'.
strip-executables = cd "$(installpath)" && \
$(foreach v, $(executables), $(STRIP) $(stripflags) '$v';)
#=== install ===================================================================
# Install targets depend on successful exit status of target all because nothing
# must be installed in case of a build error.
# -p = preserve time stamps
# -m = set permission mode (as in chmod)
# -d = create all components of specified directories
INSTALL = install
INSTALL_PROGRAM := $(INSTALL) -p -m 644
INSTALL_DATA := $(INSTALL) -p -m 644
INSTALL_DIR := $(INSTALL) -m 755 -d
# strip spaces from file names
executables := $(strip $(executables))
datafiles := $(strip $(datafiles))
datadirs := $(strip $(datadirs))
# Do not make any install sub-target with empty variable definition because the
# install program would exit with an error.
install: $(if $(executables), install-executables)
install: $(if $(datafiles), install-datafiles)
install: $(if $(datadirs), install-datadirs)
install-executables: all
$(INSTALL_DIR) -v "$(installpath)"
$(foreach v, $(executables), \
$(INSTALL_PROGRAM) '$v' "$(installpath)";)
$(info ++++ info: executables of lib $(lib.name) installed \
from $(CURDIR) to $(installpath))
$(if $(stripflags), $(strip-executables),)
install-datafiles: all
$(INSTALL_DIR) -v "$(installpath)"
$(foreach v, $(datafiles), \
$(INSTALL_DATA) '$(v)' "$(installpath)";)
$(info ++++ info: data files of lib $(lib.name) installed \
from $(CURDIR) to $(installpath))
install-datadirs: all
$(foreach v, $(datadirs), $(INSTALL_DIR) "$(installpath)/$v";)
$(foreach v, $(datadirs), \
$(INSTALL_DATA) $(wildcard $v/*) "$(installpath)/$v";)
$(info ++++ info: data directories of lib $(lib.name) installed \
from $(CURDIR) to $(installpath))
################################################################################
### rules: distribution targets ################################################
################################################################################
# TODO
# These targets are implemented in Makefile Template, but I have to figure out
# how to do it under the not-so-strict conditions of Makefile.pdlibbuilder.
# make source package
dist:
@echo "target dist not yet implemented"
# make Debian source package
dpkg-source:
@echo "target dpkg-source not yet implemented"
$(ORIGDIR):
$(DISTDIR):
################################################################################
### rules: clean targets #######################################################
################################################################################
# delete build products from build tree
clean:
rm -f $(all.objects)
rm -f $(classes.executables) $(lib.name).$(extension) $(shared.lib)
rm -f *.pre *.lst
# remove distribution directories and tarballs from build tree
distclean: clean
@echo "target distclean not yet implemented"
################################################################################
### rules: submake targets #####################################################
################################################################################
# Iterate over sub-makefiles or makefiles in other directories.
# When 'continue-make=yes' is set, sub-makes will report 'true' to the parent
# process regardless of their real exit status. This prevents the parent make
# from being aborted by a sub-make error. Useful when you want to quickly find
# out which sub-makes from a large set will succeed.
ifeq ($(continue-make),yes)
continue = || true
endif
# These targets will trigger sub-make processes for entries in 'makefiledirs'
# and 'makefiles'.
all alldebug install clean distclean dist dkpg-source: \
$(makefiledirs) $(makefiles)
# this expands to identical rules for each entry in 'makefiledirs'
$(makefiledirs):
$(MAKE) --directory=$@ $(MAKECMDGOALS) $(continue)
# this expands to identical rules for each entry in 'makefiles'
$(makefiles):
$(MAKE) --directory=$(dir $@) --makefile=$(notdir $@) $(MAKECMDGOALS) $(continue)
################################################################################
### rules: convenience targets #################################################
################################################################################
#=== show variables ============================================================
# Several 'function' macro's cause errors when expanded within a rule or without
# proper arguments. Variables which are set with the define directive are only
# shown by name for that reason.
functions = \
add-class-source \
declare-class-target \
declare-class-executable-target \
declare-object-target \
link-class \
link-lib \
link-shared \
make-object-file \
make-preprocessor-file \
make-assembly-file
# show variables from makefiles
vars:
$(info ++++ info: showing makefile variables:)
$(foreach v,\
$(sort $(filter-out $(functions) functions, $(.VARIABLES))),\
$(if $(filter file, $(origin $v)),\
$(info variable $v = $($v))))
$(foreach v, $(functions), $(info 'function' name: $v))
@echo
# show all variables
allvars:
$(info ++++ info: showing default, automatic and makefile variables:)
$(foreach v, \
$(sort $(filter-out $(functions) functions, $(.VARIABLES))), \
$(info variable ($(origin $v)) $v = $($v)))
$(foreach v, $(functions), $(info 'function' name: $v))
@echo
#=== show dependencies =========================================================
# show generated prerequisites rules
depend:
$(info ++++ info: generated prerequisite rules)
$(foreach v, $(classes), $(info $(declare-class-target)))
$(foreach v, $(classes), $(info $(declare-class-executable-target)))
$(foreach v, $(all.sources), $(info $(call declare-object-target, $v)))
@echo
#=== show help text ============================================================
# brief info about targets and paths
ifdef mpdh
mpdhinfo := $(mpdh)
else
mpdhinfo := m_pd.h was not found. Is Pd installed?
endif
help:
@echo
@echo " Main targets:"
@echo " all: build executables (default target)"
@echo " install: install all components of the library"
@echo " vars: print makefile variables for troubleshooting"
@echo " allvars: print all variables for troubleshooting"
@echo " help: print this help text"
@echo
@echo " Pd API m_pd.h:"
@echo " $(mpdhinfo)"
@echo " You may specify your preferred Pd include directory as argument"
@echo " to the make command, like 'PDINCLUDEDIR=path/to/pd/src'."
@echo
@echo " Path for installation of your libdir(s):"
@echo " $(PDLIBDIR)"
@echo " Alternatively you may specify your path for installation as argument"
@echo " to the make command, like 'PDLIBDIR=path/to/pd-externals'."
@echo
@echo " Default paths are listed in the doc sections in Makefile.pdlibbuilder."
@echo
#=== platform test =============================================================
# This target can be used to test if the compiler for specified PLATFORM is
# correctly defined and available.
dumpmachine:
@$(CC) -dumpmachine
#=== dummy target ==============================================================
coffee:
@echo "Makefile.pdlibbuilder: Can not make coffee. Sorry."
################################################################################
### end of rules sections ######################################################
################################################################################
# for syntax highlighting in vim and github
# vim: set filetype=make:
hvcc-0.16.0/hvcc/generators/c2pdext/static/m_pd.h 0000644 0000000 0000000 00000113012 00000000000 016432 0 ustar 00 /* Copyright (c) 1997-1999 Miller Puckette.
* For information on usage and redistribution, and for a DISCLAIMER OF ALL
* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
#ifndef __m_pd_h_
#if defined(_LANGUAGE_C_PLUS_PLUS) || defined(__cplusplus)
extern "C" {
#endif
#define PD_MAJOR_VERSION 0
#define PD_MINOR_VERSION 54
#define PD_BUGFIX_VERSION 0
#define PD_TEST_VERSION ""
extern int pd_compatibilitylevel; /* e.g., 43 for pd 0.43 compatibility */
/* old name for "MSW" flag -- we have to take it for the sake of many old
"nmakefiles" for externs, which will define NT and not MSW */
#if defined(NT) && !defined(MSW)
#define MSW
#endif
/* These pragmas are only used for MSVC, not MinGW or Cygwin */
#ifdef _MSC_VER
/* #pragma warning( disable : 4091 ) */
#pragma warning( disable : 4305 ) /* uncast const double to float */
#pragma warning( disable : 4244 ) /* uncast float/int conversion etc. */
#pragma warning( disable : 4101 ) /* unused automatic variables */
#endif /* _MSC_VER */
/* the external storage class is "extern" in UNIX; in MSW it's ugly. */
#ifndef EXTERN
#ifdef _WIN32
#ifdef PD_INTERNAL
#define EXTERN __declspec(dllexport) extern
#else
#define EXTERN __declspec(dllimport) extern
#endif /* PD_INTERNAL */
#else
#define EXTERN extern
#endif /* _WIN32 */
#endif /* EXTERN */
/* On most c compilers, you can just say "struct foo;" to declare a
structure whose elements are defined elsewhere. On very old MSVC versions,
when compiling C (but not C++) code, you have to say "extern struct foo;".
So we make a stupid macro: */
#if defined(_MSC_VER) && !defined(_LANGUAGE_C_PLUS_PLUS) \
&& !defined(__cplusplus) && (_MSC_VER < 1700)
#define EXTERN_STRUCT extern struct
#else
#define EXTERN_STRUCT struct
#endif
/* Define some attributes, specific to the compiler */
#if defined(__GNUC__)
#define ATTRIBUTE_FORMAT_PRINTF(a, b) __attribute__ ((format (printf, a, b)))
#else
#define ATTRIBUTE_FORMAT_PRINTF(a, b)
#endif
#if !defined(_SIZE_T) && !defined(_SIZE_T_)
#include /* just for size_t -- how lame! */
#endif
/* Microsoft Visual Studio is not C99, but since VS2015 has included most C99 headers:
https://docs.microsoft.com/en-us/previous-versions/hh409293(v=vs.140)#c-runtime-library
These definitions recreate stdint.h types, but only in pre-2015 Visual Studio: */
#if defined(_MSC_VER) && _MSC_VER < 1900
typedef signed __int8 int8_t;
typedef signed __int16 int16_t;
typedef signed __int32 int32_t;
typedef signed __int64 int64_t;
typedef unsigned __int8 uint8_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_t;
typedef unsigned __int64 uint64_t;
#else
# include
#endif
/* for FILE, needed by sys_fopen() and sys_fclose() only */
#include
#define MAXPDSTRING 1000 /* use this for anything you want */
#define MAXPDARG 5 /* max number of args we can typecheck today */
/* signed and unsigned integer types the size of a pointer: */
#if !defined(PD_LONGINTTYPE)
#if defined(_WIN32) && defined(_WIN64)
#define PD_LONGINTTYPE long long
#else
#define PD_LONGINTTYPE long
#endif
#endif
#if !defined(PD_FLOATSIZE)
/* normally, our floats (t_float, t_sample,...) are 32bit */
# define PD_FLOATSIZE 32
#endif
#if PD_FLOATSIZE == 32
# define PD_FLOATTYPE float
/* an unsigned int of the same size as FLOATTYPE: */
# define PD_FLOATUINTTYPE uint32_t
#elif PD_FLOATSIZE == 64
# define PD_FLOATTYPE double
# define PD_FLOATUINTTYPE uint64_t
#else
# error invalid FLOATSIZE: must be 32 or 64
#endif
typedef PD_LONGINTTYPE t_int; /* pointer-size integer */
typedef PD_FLOATTYPE t_float; /* a float type at most the same size */
typedef PD_FLOATTYPE t_floatarg; /* float type for function calls */
typedef struct _symbol
{
const char *s_name;
struct _class **s_thing;
struct _symbol *s_next;
} t_symbol;
EXTERN_STRUCT _array;
#define t_array struct _array /* g_canvas.h */
/* pointers to glist and array elements go through a "stub" which sticks
around after the glist or array is freed. The stub itself is deleted when
both the glist/array is gone and the refcount is zero, ensuring that no
gpointers are pointing here. */
#define GP_NONE 0 /* the stub points nowhere (has been cut off) */
#define GP_GLIST 1 /* the stub points to a glist element */
#define GP_ARRAY 2 /* ... or array */
typedef struct _gstub
{
union
{
struct _glist *gs_glist; /* glist we're in */
struct _array *gs_array; /* array we're in */
} gs_un;
int gs_which; /* GP_GLIST/GP_ARRAY */
int gs_refcount; /* number of gpointers pointing here */
} t_gstub;
typedef struct _gpointer /* pointer to a gobj in a glist */
{
union
{
struct _scalar *gp_scalar; /* scalar we're in (if glist) */
union word *gp_w; /* raw data (if array) */
} gp_un;
int gp_valid; /* number which must match gpointee */
t_gstub *gp_stub; /* stub which points to glist/array */
} t_gpointer;
typedef union word
{
t_float w_float;
t_symbol *w_symbol;
t_gpointer *w_gpointer;
t_array *w_array;
struct _binbuf *w_binbuf;
int w_index;
} t_word;
typedef enum
{
A_NULL,
A_FLOAT,
A_SYMBOL,
A_POINTER,
A_SEMI,
A_COMMA,
A_DEFFLOAT,
A_DEFSYM,
A_DOLLAR,
A_DOLLSYM,
A_GIMME,
A_CANT
} t_atomtype;
#define A_DEFSYMBOL A_DEFSYM /* better name for this */
typedef struct _atom
{
t_atomtype a_type;
union word a_w;
} t_atom;
EXTERN_STRUCT _class;
#define t_class struct _class
EXTERN_STRUCT _outlet;
#define t_outlet struct _outlet
EXTERN_STRUCT _inlet;
#define t_inlet struct _inlet
EXTERN_STRUCT _binbuf;
#define t_binbuf struct _binbuf
EXTERN_STRUCT _clock;
#define t_clock struct _clock
EXTERN_STRUCT _outconnect;
#define t_outconnect struct _outconnect
EXTERN_STRUCT _glist;
#define t_glist struct _glist
#define t_canvas struct _glist /* LATER lose this */
EXTERN_STRUCT _template;
typedef t_class *t_pd; /* pure datum: nothing but a class pointer */
typedef struct _gobj /* a graphical object */
{
t_pd g_pd; /* pure datum header (class) */
struct _gobj *g_next; /* next in list */
} t_gobj;
typedef struct _scalar /* a graphical object holding data */
{
t_gobj sc_gobj; /* header for graphical object */
t_symbol *sc_template; /* template name (LATER replace with pointer) */
t_word sc_vec[1]; /* indeterminate-length array of words */
} t_scalar;
typedef struct _text /* patchable object - graphical, with text */
{
t_gobj te_g; /* header for graphical object */
t_binbuf *te_binbuf; /* holder for the text */
t_outlet *te_outlet; /* linked list of outlets */
t_inlet *te_inlet; /* linked list of inlets */
short te_xpix; /* x&y location (within the toplevel) */
short te_ypix;
short te_width; /* requested width in chars, 0 if auto */
unsigned int te_type:2; /* from defs below */
} t_text;
#define T_TEXT 0 /* just a textual comment */
#define T_OBJECT 1 /* a MAX style patchable object */
#define T_MESSAGE 2 /* a MAX type message */
#define T_ATOM 3 /* a cell to display a number or symbol */
#define te_pd te_g.g_pd
/* t_object is synonym for t_text (LATER unify them) */
typedef struct _text t_object;
#define ob_outlet te_outlet
#define ob_inlet te_inlet
#define ob_binbuf te_binbuf
#define ob_pd te_g.g_pd
#define ob_g te_g
typedef void (*t_method)(void);
typedef void *(*t_newmethod)(void);
/* in ARM 64 a varargs prototype generates a different function call sequence
from a fixed one, so in that special case we make a more restrictive
definition for t_gotfn. This will break some code in the "chaos" package
in Pd extended. (that code will run incorrectly anyhow so why not catch it
at compile time anyhow.) */
#if defined(__APPLE__) && defined(__aarch64__)
typedef void (*t_gotfn)(void *x);
#else
typedef void (*t_gotfn)(void *x, ...);
#endif
/* ---------------- pre-defined objects and symbols --------------*/
EXTERN t_pd pd_objectmaker; /* factory for creating "object" boxes */
EXTERN t_pd pd_canvasmaker; /* factory for creating canvases */
/* --------- prototypes from the central message system ----------- */
EXTERN void pd_typedmess(t_pd *x, t_symbol *s, int argc, t_atom *argv);
EXTERN void pd_forwardmess(t_pd *x, int argc, t_atom *argv);
EXTERN t_symbol *gensym(const char *s);
EXTERN t_gotfn getfn(const t_pd *x, t_symbol *s);
EXTERN t_gotfn zgetfn(const t_pd *x, t_symbol *s);
EXTERN void nullfn(void);
EXTERN void pd_vmess(t_pd *x, t_symbol *s, const char *fmt, ...);
/* the following macros are for sending non-type-checkable messages, i.e.,
using function lookup but circumventing type checking on arguments. Only
use for internal messaging protected by A_CANT so that the message can't
be generated at patch level. */
#define mess0(x, s) ((*getfn((x), (s)))((x)))
typedef void (*t_gotfn1)(void *x, void *arg1);
#define mess1(x, s, a) ((*(t_gotfn1)getfn((x), (s)))((x), (a)))
typedef void (*t_gotfn2)(void *x, void *arg1, void *arg2);
#define mess2(x, s, a,b) ((*(t_gotfn2)getfn((x), (s)))((x), (a),(b)))
typedef void (*t_gotfn3)(void *x, void *arg1, void *arg2, void *arg3);
#define mess3(x, s, a,b,c) ((*(t_gotfn3)getfn((x), (s)))((x), (a),(b),(c)))
typedef void (*t_gotfn4)(void *x,
void *arg1, void *arg2, void *arg3, void *arg4);
#define mess4(x, s, a,b,c,d) \
((*(t_gotfn4)getfn((x), (s)))((x), (a),(b),(c),(d)))
typedef void (*t_gotfn5)(void *x,
void *arg1, void *arg2, void *arg3, void *arg4, void *arg5);
#define mess5(x, s, a,b,c,d,e) \
((*(t_gotfn5)getfn((x), (s)))((x), (a),(b),(c),(d),(e)))
EXTERN void obj_list(t_object *x, t_symbol *s, int argc, t_atom *argv);
EXTERN t_pd *pd_newest(void);
/* --------------- memory management -------------------- */
EXTERN void *getbytes(size_t nbytes);
EXTERN void *getzbytes(size_t nbytes);
EXTERN void *copybytes(const void *src, size_t nbytes);
EXTERN void freebytes(void *x, size_t nbytes);
EXTERN void *resizebytes(void *x, size_t oldsize, size_t newsize);
/* -------------------- atoms ----------------------------- */
#define SETSEMI(atom) ((atom)->a_type = A_SEMI, (atom)->a_w.w_index = 0)
#define SETCOMMA(atom) ((atom)->a_type = A_COMMA, (atom)->a_w.w_index = 0)
#define SETPOINTER(atom, gp) ((atom)->a_type = A_POINTER, \
(atom)->a_w.w_gpointer = (gp))
#define SETFLOAT(atom, f) ((atom)->a_type = A_FLOAT, (atom)->a_w.w_float = (f))
#define SETSYMBOL(atom, s) ((atom)->a_type = A_SYMBOL, \
(atom)->a_w.w_symbol = (s))
#define SETDOLLAR(atom, n) ((atom)->a_type = A_DOLLAR, \
(atom)->a_w.w_index = (n))
#define SETDOLLSYM(atom, s) ((atom)->a_type = A_DOLLSYM, \
(atom)->a_w.w_symbol= (s))
EXTERN t_float atom_getfloat(const t_atom *a);
EXTERN t_int atom_getint(const t_atom *a);
EXTERN t_symbol *atom_getsymbol(const t_atom *a);
EXTERN t_symbol *atom_gensym(const t_atom *a);
EXTERN t_float atom_getfloatarg(int which, int argc, const t_atom *argv);
EXTERN t_int atom_getintarg(int which, int argc, const t_atom *argv);
EXTERN t_symbol *atom_getsymbolarg(int which, int argc, const t_atom *argv);
EXTERN void atom_string(const t_atom *a, char *buf, unsigned int bufsize);
/* ------------------ binbufs --------------- */
EXTERN t_binbuf *binbuf_new(void);
EXTERN void binbuf_free(t_binbuf *x);
EXTERN t_binbuf *binbuf_duplicate(const t_binbuf *y);
EXTERN void binbuf_text(t_binbuf *x, const char *text, size_t size);
EXTERN void binbuf_gettext(const t_binbuf *x, char **bufp, int *lengthp);
EXTERN void binbuf_clear(t_binbuf *x);
EXTERN void binbuf_add(t_binbuf *x, int argc, const t_atom *argv);
EXTERN void binbuf_addv(t_binbuf *x, const char *fmt, ...);
EXTERN void binbuf_addbinbuf(t_binbuf *x, const t_binbuf *y);
EXTERN void binbuf_addsemi(t_binbuf *x);
EXTERN void binbuf_restore(t_binbuf *x, int argc, const t_atom *argv);
EXTERN void binbuf_print(const t_binbuf *x);
EXTERN int binbuf_getnatom(const t_binbuf *x);
EXTERN t_atom *binbuf_getvec(const t_binbuf *x);
EXTERN int binbuf_resize(t_binbuf *x, int newsize);
EXTERN void binbuf_eval(const t_binbuf *x, t_pd *target, int argc, const t_atom *argv);
EXTERN int binbuf_read(t_binbuf *b, const char *filename, const char *dirname,
int crflag);
EXTERN int binbuf_read_via_canvas(t_binbuf *b, const char *filename, const t_canvas *canvas,
int crflag);
EXTERN int binbuf_read_via_path(t_binbuf *b, const char *filename, const char *dirname,
int crflag);
EXTERN int binbuf_write(const t_binbuf *x, const char *filename, const char *dir,
int crflag);
EXTERN void binbuf_evalfile(t_symbol *name, t_symbol *dir);
EXTERN t_symbol *binbuf_realizedollsym(t_symbol *s, int ac, const t_atom *av,
int tonew);
/* ------------------ clocks --------------- */
EXTERN t_clock *clock_new(void *owner, t_method fn);
EXTERN void clock_set(t_clock *x, double systime);
EXTERN void clock_delay(t_clock *x, double delaytime);
EXTERN void clock_unset(t_clock *x);
EXTERN void clock_setunit(t_clock *x, double timeunit, int sampflag);
EXTERN double clock_getlogicaltime(void);
EXTERN double clock_getsystime(void); /* OBSOLETE; use clock_getlogicaltime() */
EXTERN double clock_gettimesince(double prevsystime);
EXTERN double clock_gettimesincewithunits(double prevsystime,
double units, int sampflag);
EXTERN double clock_getsystimeafter(double delaytime);
EXTERN void clock_free(t_clock *x);
/* ----------------- pure data ---------------- */
EXTERN t_pd *pd_new(t_class *cls);
EXTERN void pd_free(t_pd *x);
EXTERN void pd_bind(t_pd *x, t_symbol *s);
EXTERN void pd_unbind(t_pd *x, t_symbol *s);
EXTERN t_pd *pd_findbyclass(t_symbol *s, const t_class *c);
EXTERN void pd_pushsym(t_pd *x);
EXTERN void pd_popsym(t_pd *x);
EXTERN void pd_bang(t_pd *x);
EXTERN void pd_pointer(t_pd *x, t_gpointer *gp);
EXTERN void pd_float(t_pd *x, t_float f);
EXTERN void pd_symbol(t_pd *x, t_symbol *s);
EXTERN void pd_list(t_pd *x, t_symbol *s, int argc, t_atom *argv);
EXTERN void pd_anything(t_pd *x, t_symbol *s, int argc, t_atom *argv);
#define pd_class(x) (*(x))
/* ----------------- pointers ---------------- */
EXTERN void gpointer_init(t_gpointer *gp);
EXTERN void gpointer_copy(const t_gpointer *gpfrom, t_gpointer *gpto);
EXTERN void gpointer_unset(t_gpointer *gp);
EXTERN int gpointer_check(const t_gpointer *gp, int headok);
/* ----------------- patchable "objects" -------------- */
EXTERN t_inlet *inlet_new(t_object *owner, t_pd *dest, t_symbol *s1,
t_symbol *s2);
EXTERN t_inlet *pointerinlet_new(t_object *owner, t_gpointer *gp);
EXTERN t_inlet *floatinlet_new(t_object *owner, t_float *fp);
EXTERN t_inlet *symbolinlet_new(t_object *owner, t_symbol **sp);
EXTERN t_inlet *signalinlet_new(t_object *owner, t_float f);
EXTERN void inlet_free(t_inlet *x);
EXTERN t_outlet *outlet_new(t_object *owner, t_symbol *s);
EXTERN void outlet_bang(t_outlet *x);
EXTERN void outlet_pointer(t_outlet *x, t_gpointer *gp);
EXTERN void outlet_float(t_outlet *x, t_float f);
EXTERN void outlet_symbol(t_outlet *x, t_symbol *s);
EXTERN void outlet_list(t_outlet *x, t_symbol *s, int argc, t_atom *argv);
EXTERN void outlet_anything(t_outlet *x, t_symbol *s, int argc, t_atom *argv);
EXTERN t_symbol *outlet_getsymbol(t_outlet *x);
EXTERN void outlet_free(t_outlet *x);
EXTERN t_object *pd_checkobject(t_pd *x);
/* -------------------- canvases -------------- */
EXTERN void glob_setfilename(void *dummy, t_symbol *name, t_symbol *dir);
EXTERN void canvas_setargs(int argc, const t_atom *argv);
EXTERN void canvas_getargs(int *argcp, t_atom **argvp);
EXTERN t_symbol *canvas_getcurrentdir(void);
EXTERN t_glist *canvas_getcurrent(void);
EXTERN void canvas_makefilename(const t_glist *c, const char *file,
char *result, int resultsize);
EXTERN t_symbol *canvas_getdir(const t_glist *x);
EXTERN char sys_font[]; /* default typeface set in s_main.c */
EXTERN char sys_fontweight[]; /* default font weight set in s_main.c */
EXTERN int sys_hostfontsize(int fontsize, int zoom);
EXTERN int sys_zoomfontwidth(int fontsize, int zoom, int worstcase);
EXTERN int sys_zoomfontheight(int fontsize, int zoom, int worstcase);
EXTERN int sys_fontwidth(int fontsize);
EXTERN int sys_fontheight(int fontsize);
EXTERN void canvas_dataproperties(t_glist *x, t_scalar *sc, t_binbuf *b);
EXTERN int canvas_open(const t_canvas *x, const char *name, const char *ext,
char *dirresult, char **nameresult, unsigned int size, int bin);
EXTERN t_float canvas_getsr(t_canvas *x);
EXTERN int canvas_getsignallength(t_canvas *x);
/* ---------------- widget behaviors ---------------------- */
EXTERN_STRUCT _widgetbehavior;
#define t_widgetbehavior struct _widgetbehavior
EXTERN_STRUCT _parentwidgetbehavior;
#define t_parentwidgetbehavior struct _parentwidgetbehavior
EXTERN const t_parentwidgetbehavior *pd_getparentwidget(t_pd *x);
/* -------------------- classes -------------- */
#define CLASS_DEFAULT 0 /* flags for new classes below */
#define CLASS_PD 1 /* non-canvasable (bare) pd such as an inlet */
#define CLASS_GOBJ 2 /* pd that can belong to a canvas */
#define CLASS_PATCHABLE 3 /* pd that also can have inlets and outlets */
#define CLASS_TYPEMASK 3
#define CLASS_NOINLET 8 /* suppress left inlet */
#define CLASS_MULTICHANNEL 0x10 /* can deal with multichannel sigs */
#define CLASS_NOPROMOTESIG 0x20 /* don't promote scalars to signals */
#define CLASS_NOPROMOTELEFT 0x40 /* not even the main (left) inlet */
/*
Setting a tilde object's CLASS_MULTICHANNEL flag declares that it can
deal with multichannel inputs. In this case the channel counts of
the inputs might not match; it's up to the dsp method to figure out what
to do. Also, the output signal vectors aren't allocated. The output
channel counts have to be specified by the object at DSP time. If
the object can't put itself on the DSP chain it then has to create
outputs anyway and arrange to zero them.
By default, if a tilde object's inputs are unconnected, Pd fills them
in by adding scalar-to-vector conversions to the DSP chain as needed before
calling the dsp method. This behavior can be suppressed for the left
(main) inlet by setting CLASS_NOPROMOTELEFT and for one or more non-main
inlets by setting CLASS_NOPROMOTESIG. Seeing this, the object can then
opt to supply a faster routine; for example, "+" can do a vector-scalar
add. In any case, signal outputs are all vectors, and are allocated
automatically unless the CLASS_MULTICHANNEL flag is also set.
*/
EXTERN t_class *class_new(t_symbol *name, t_newmethod newmethod,
t_method freemethod, size_t size, int flags, t_atomtype arg1, ...);
EXTERN t_class *class_new64(t_symbol *name, t_newmethod newmethod,
t_method freemethod, size_t size, int flags, t_atomtype arg1, ...);
EXTERN void class_free(t_class *c);
#ifdef PDINSTANCE
EXTERN t_class *class_getfirst(void);
#endif
EXTERN void class_addcreator(t_newmethod newmethod, t_symbol *s,
t_atomtype type1, ...);
EXTERN void class_addmethod(t_class *c, t_method fn, t_symbol *sel,
t_atomtype arg1, ...);
EXTERN void class_addbang(t_class *c, t_method fn);
EXTERN void class_addpointer(t_class *c, t_method fn);
EXTERN void class_doaddfloat(t_class *c, t_method fn);
EXTERN void class_addsymbol(t_class *c, t_method fn);
EXTERN void class_addlist(t_class *c, t_method fn);
EXTERN void class_addanything(t_class *c, t_method fn);
EXTERN void class_sethelpsymbol(t_class *c, t_symbol *s);
EXTERN void class_setwidget(t_class *c, const t_widgetbehavior *w);
EXTERN void class_setparentwidget(t_class *c, const t_parentwidgetbehavior *w);
EXTERN const char *class_getname(const t_class *c);
EXTERN const char *class_gethelpname(const t_class *c);
EXTERN const char *class_gethelpdir(const t_class *c);
EXTERN void class_setdrawcommand(t_class *c);
EXTERN int class_isdrawcommand(const t_class *c);
EXTERN void class_set_extern_dir(t_symbol *s);
EXTERN void class_domainsignalin(t_class *c, int onset);
#define CLASS_MAINSIGNALIN(c, type, field) \
class_domainsignalin(c, (char *)(&((type *)0)->field) - (char *)0)
/* prototype for functions to save Pd's to a binbuf */
typedef void (*t_savefn)(t_gobj *x, t_binbuf *b);
EXTERN void class_setsavefn(t_class *c, t_savefn f);
EXTERN t_savefn class_getsavefn(const t_class *c);
EXTERN void obj_saveformat(const t_object *x, t_binbuf *bb); /* add format to bb */
/* prototype for functions to open properties dialogs */
typedef void (*t_propertiesfn)(t_gobj *x, struct _glist *glist);
EXTERN void class_setpropertiesfn(t_class *c, t_propertiesfn f);
EXTERN t_propertiesfn class_getpropertiesfn(const t_class *c);
typedef void (*t_classfreefn)(t_class *);
EXTERN void class_setfreefn(t_class *c, t_classfreefn fn);
#ifndef PD_CLASS_DEF
#define class_addbang(x, y) class_addbang((x), (t_method)(y))
#define class_addpointer(x, y) class_addpointer((x), (t_method)(y))
#define class_addfloat(x, y) class_doaddfloat((x), (t_method)(y))
#define class_addsymbol(x, y) class_addsymbol((x), (t_method)(y))
#define class_addlist(x, y) class_addlist((x), (t_method)(y))
#define class_addanything(x, y) class_addanything((x), (t_method)(y))
#endif
#if PD_FLOATSIZE == 64
# define class_new class_new64
#endif
/* ------------ printing --------------------------------- */
EXTERN void post(const char *fmt, ...);
EXTERN void startpost(const char *fmt, ...);
EXTERN void poststring(const char *s);
EXTERN void postfloat(t_floatarg f);
EXTERN void postatom(int argc, const t_atom *argv);
EXTERN void endpost(void);
EXTERN void bug(const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(1, 2);
EXTERN void pd_error(const void *object, const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(2, 3);
/* for logpost(); does *not* work with verbose()! */
typedef enum {
PD_CRITICAL = 0,
PD_ERROR,
PD_NORMAL,
PD_DEBUG,
PD_VERBOSE
} t_loglevel;
EXTERN void logpost(const void *object, int level, const char *fmt, ...)
ATTRIBUTE_FORMAT_PRINTF(3, 4);
/* deprecated, use logpost() instead. */
EXTERN void verbose(int level, const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(2, 3);
/* ------------ system interface routines ------------------- */
EXTERN int sys_isabsolutepath(const char *dir);
EXTERN void sys_bashfilename(const char *from, char *to);
EXTERN void sys_unbashfilename(const char *from, char *to);
EXTERN int open_via_path(const char *dir, const char *name, const char *ext,
char *dirresult, char **nameresult, unsigned int size, int bin);
EXTERN int sched_geteventno(void);
EXTERN double sys_getrealtime(void);
EXTERN int (*sys_idlehook)(void); /* hook to add idle time computation */
/* Win32's open()/fopen() do not handle UTF-8 filenames so we need
* these internal versions that handle UTF-8 filenames the same across
* all platforms. They are recommended for use in external
* objectclasses as well so they work with Unicode filenames on Windows */
EXTERN int sys_open(const char *path, int oflag, ...);
EXTERN int sys_close(int fd);
EXTERN FILE *sys_fopen(const char *filename, const char *mode);
EXTERN int sys_fclose(FILE *stream);
/* ------------ threading ------------------- */
EXTERN void sys_lock(void);
EXTERN void sys_unlock(void);
EXTERN int sys_trylock(void);
/* --------------- signals ----------------------------------- */
typedef PD_FLOATTYPE t_sample;
typedef union _sampleint_union {
t_sample f;
PD_FLOATUINTTYPE i;
} t_sampleint_union;
#define MAXLOGSIG 32
#define MAXSIGSIZE (1 << MAXLOGSIG)
typedef struct _signal
{
union
{
int s_length; /* number of items per channel */
int s_n; /* for source compatibility: pre-0.54 name */
};
t_sample *s_vec; /* the samples, s_nchans vectors of s_length */
t_float s_sr; /* samples per second per channel */
int s_nchans; /* number of channels */
int s_overlap; /* number of times each sample appears */
int s_refcount; /* number of times signal is referenced */
int s_isborrowed; /* whether we're going to borrow our array */
int s_isscalar; /* scalar for an unconnected signal input */
struct _signal *s_borrowedfrom; /* signal to borrow it from */
struct _signal *s_nextfree; /* next in freelist */
struct _signal *s_nextused; /* next in used list */
int s_nalloc; /* allocated size of array in points */
} t_signal;
typedef t_int *(*t_perfroutine)(t_int *args);
EXTERN t_signal *signal_new(int length, int nchans, t_float sr,
t_sample *scalarptr);
EXTERN void signal_setmultiout(t_signal **sig, int nchans);
EXTERN t_int *plus_perform(t_int *args);
EXTERN t_int *plus_perf8(t_int *args);
EXTERN t_int *zero_perform(t_int *args);
EXTERN t_int *zero_perf8(t_int *args);
EXTERN t_int *copy_perform(t_int *args);
EXTERN t_int *copy_perf8(t_int *args);
EXTERN t_int *scalarcopy_perform(t_int *args);
EXTERN t_int *scalarcopy_perf8(t_int *args);
EXTERN void dsp_add_plus(t_sample *in1, t_sample *in2, t_sample *out, int n);
EXTERN void dsp_add_copy(t_sample *in, t_sample *out, int n);
EXTERN void dsp_add_scalarcopy(t_float *in, t_sample *out, int n);
EXTERN void dsp_add_zero(t_sample *out, int n);
EXTERN int sys_getblksize(void);
EXTERN t_float sys_getsr(void);
EXTERN int sys_get_inchannels(void);
EXTERN int sys_get_outchannels(void);
EXTERN void dsp_add(t_perfroutine f, int n, ...);
EXTERN void dsp_addv(t_perfroutine f, int n, t_int *vec);
EXTERN void pd_fft(t_float *buf, int npoints, int inverse);
EXTERN int ilog2(int n);
EXTERN void mayer_fht(t_sample *fz, int n);
EXTERN void mayer_fft(int n, t_sample *real, t_sample *imag);
EXTERN void mayer_ifft(int n, t_sample *real, t_sample *imag);
EXTERN void mayer_realfft(int n, t_sample *real);
EXTERN void mayer_realifft(int n, t_sample *real);
EXTERN float *cos_table;
#define LOGCOSTABSIZE 9
#define COSTABSIZE (1<: receiver on the GUI side (e.g. a Tcl/Tk 'proc')
* : string of format specifiers
* <...>: values according to the format specifiers
*
* the can be a NULL pointer (in which case it is ignored)
* the user of NULL as a is discouraged
* depending on the format specifiers, one or more values are passed
* 'f' : : a floating point number
* 'i' : : an integer number
* 's' : : a string
* 'r' : : a raw string
* 'x' : : a generic pointer
* 'o' : : an graphical object
* '^' : : a toplevel window (legacy)
* 'c' : : a canvas (on a window)
* 'F' : : array of t_float's
* 'S' : : array of strings
* 'R' : : array of raw strings
* 'a' : : list of atoms
* 'A' : : array of atoms
* 'w' : : list of floatwords
* 'W' : : array of floatwords
* 'm' : : a Pd message
* 'p' : : a pascal string (explicit size; not \0-terminated)
* 'k' : : a color (or kolor, if you prefer)
* ' ' : none : ignored
* the use of the specifiers 'x^' is discouraged
* raw-strings ('rR') should only be used for constant, well-known strings
*/
EXTERN void pdgui_vmess(const char* destination, const char* fmt, ...);
/* improved dialog window creation
* this will insert a first argument to based on
* which the GUI can then use to callback.
* gfxstub_new() ensures that the given receiver will be available,
* even if the has been removed in the meantime.
* see pdgui_vmess() for a description of and the varargs
*/
EXTERN void pdgui_stub_vnew(t_pd *owner, const char* destination, void *key, const char* fmt, ...);
EXTERN void pdgui_stub_deleteforkey(void *key);
extern t_class *glob_pdobject; /* object to send "pd" messages */
/*------------- Max 0.26 compatibility --------------------*/
/* the following reflects the new way classes are laid out, with the class
pointing to the messlist and not vice versa. Externs shouldn't feel it. */
typedef t_class *t_externclass;
EXTERN void c_extern(t_externclass *cls, t_newmethod newroutine,
t_method freeroutine, t_symbol *name, size_t size, int tiny, \
t_atomtype arg1, ...);
EXTERN void c_addmess(t_method fn, t_symbol *sel, t_atomtype arg1, ...);
#define t_getbytes getbytes
#define t_freebytes freebytes
#define t_resizebytes resizebytes
#define typedmess pd_typedmess
#define vmess pd_vmess
/* A definition to help gui objects straddle 0.34-0.35 changes. If this is
defined, there is a "te_xpix" field in objects, not a "te_xpos" as before: */
#define PD_USE_TE_XPIX
#ifndef _MSC_VER /* Microoft compiler can't handle "inline" function/macros */
#if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
/* a test for NANs and denormals. Should only be necessary on i386. */
#if PD_FLOATSIZE == 32
typedef union
{
t_float f;
unsigned int ui;
} t_bigorsmall32;
static inline int PD_BADFLOAT(t_float f) /* malformed float */
{
t_bigorsmall32 pun;
pun.f = f;
pun.ui &= 0x7f800000;
return((pun.ui == 0) | (pun.ui == 0x7f800000));
}
static inline int PD_BIGORSMALL(t_float f) /* exponent outside (-64,64) */
{
t_bigorsmall32 pun;
pun.f = f;
return((pun.ui & 0x20000000) == ((pun.ui >> 1) & 0x20000000));
}
#elif PD_FLOATSIZE == 64
typedef union
{
t_float f;
unsigned int ui[2];
} t_bigorsmall64;
static inline int PD_BADFLOAT(t_float f) /* malformed double */
{
t_bigorsmall64 pun;
pun.f = f;
pun.ui[1] &= 0x7ff00000;
return((pun.ui[1] == 0) | (pun.ui[1] == 0x7ff00000));
}
static inline int PD_BIGORSMALL(t_float f) /* exponent outside (-512,512) */
{
t_bigorsmall64 pun;
pun.f = f;
return((pun.ui[1] & 0x20000000) == ((pun.ui[1] >> 1) & 0x20000000));
}
#endif /* PD_FLOATSIZE */
#else /* not INTEL or ARM */
#define PD_BADFLOAT(f) 0
#define PD_BIGORSMALL(f) 0
#endif
#else /* _MSC_VER */
#if PD_FLOATSIZE == 32
#define PD_BADFLOAT(f) ((((*(unsigned int*)&(f))&0x7f800000)==0) || \
(((*(unsigned int*)&(f))&0x7f800000)==0x7f800000))
/* more stringent test: anything not between 1e-19 and 1e19 in absolute val */
#define PD_BIGORSMALL(f) ((((*(unsigned int*)&(f))&0x60000000)==0) || \
(((*(unsigned int*)&(f))&0x60000000)==0x60000000))
#else /* 64 bits... don't know what to do here */
#define PD_BADFLOAT(f) (!(((f) >= 0) || ((f) <= 0)))
#define PD_BIGORSMALL(f) ((f) > 1e150 || (f) < -1e150 \
|| (f) > -1e-150 && (f) < 1e-150 )
#endif
#endif /* _MSC_VER */
/* get version number at run time */
EXTERN void sys_getversion(int *major, int *minor, int *bugfix);
/* get floatsize at run time */
EXTERN unsigned int sys_getfloatsize(void);
EXTERN_STRUCT _instancemidi;
#define t_instancemidi struct _instancemidi
EXTERN_STRUCT _instanceinter;
#define t_instanceinter struct _instanceinter
EXTERN_STRUCT _instancecanvas;
#define t_instancecanvas struct _instancecanvas
EXTERN_STRUCT _instanceugen;
#define t_instanceugen struct _instanceugen
EXTERN_STRUCT _instancestuff;
#define t_instancestuff struct _instancestuff
#ifndef PDTHREADS
#define PDTHREADS 1
#endif
struct _pdinstance
{
double pd_systime; /* global time in Pd ticks */
t_clock *pd_clock_setlist; /* list of set clocks */
t_canvas *pd_canvaslist; /* list of all root canvases */
struct _template *pd_templatelist; /* list of all templates */
int pd_instanceno; /* ordinal number of this instance */
t_symbol **pd_symhash; /* symbol table hash table */
t_instancemidi *pd_midi; /* private stuff for x_midi.c */
t_instanceinter *pd_inter; /* private stuff for s_inter.c */
t_instanceugen *pd_ugen; /* private stuff for d_ugen.c */
t_instancecanvas *pd_gui; /* semi-private stuff in g_canvas.h */
t_instancestuff *pd_stuff; /* semi-private stuff in s_stuff.h */
t_pd *pd_newest; /* most recently created object */
#ifdef PDINSTANCE
t_symbol pd_s_pointer;
t_symbol pd_s_float;
t_symbol pd_s_symbol;
t_symbol pd_s_bang;
t_symbol pd_s_list;
t_symbol pd_s_anything;
t_symbol pd_s_signal;
t_symbol pd_s__N;
t_symbol pd_s__X;
t_symbol pd_s_x;
t_symbol pd_s_y;
t_symbol pd_s_;
#endif
#if PDTHREADS
int pd_islocked;
#endif
};
#define t_pdinstance struct _pdinstance
EXTERN t_pdinstance pd_maininstance;
/* m_pd.c */
#ifdef PDINSTANCE
EXTERN t_pdinstance *pdinstance_new(void);
EXTERN void pd_setinstance(t_pdinstance *x);
EXTERN void pdinstance_free(t_pdinstance *x);
#endif /* PDINSTANCE */
#if defined(PDTHREADS) && defined(PDINSTANCE)
#ifdef _MSC_VER
#define PERTHREAD __declspec(thread)
#else
#define PERTHREAD __thread
#endif /* _MSC_VER */
#else
#define PERTHREAD
#endif
#ifdef PDINSTANCE
extern PERTHREAD t_pdinstance *pd_this;
EXTERN t_pdinstance **pd_instances;
EXTERN int pd_ninstances;
#else
#define pd_this (&pd_maininstance)
#endif /* PDINSTANCE */
#ifdef PDINSTANCE
#define s_pointer (pd_this->pd_s_pointer)
#define s_float (pd_this->pd_s_float)
#define s_symbol (pd_this->pd_s_symbol)
#define s_bang (pd_this->pd_s_bang)
#define s_list (pd_this->pd_s_list)
#define s_anything (pd_this->pd_s_anything)
#define s_signal (pd_this->pd_s_signal)
#define s__N (pd_this->pd_s__N)
#define s__X (pd_this->pd_s__X)
#define s_x (pd_this->pd_s_x)
#define s_y (pd_this->pd_s_y)
#define s_ (pd_this->pd_s_)
#else
EXTERN t_symbol s_pointer, s_float, s_symbol, s_bang, s_list, s_anything,
s_signal, s__N, s__X, s_x, s_y, s_;
#endif
EXTERN t_canvas *pd_getcanvaslist(void);
EXTERN int pd_getdspstate(void);
/* x_text.c */
EXTERN t_binbuf *text_getbufbyname(t_symbol *s); /* get binbuf from text obj */
EXTERN void text_notifybyname(t_symbol *s); /* notify it was modified */
/* g_undo.c */
/* store two message-sets to be sent to the object's method for 'undo'ing
* resp. 'redo'ing the current state of an object.
* this creates an internal copy of the atom-lists (so the caller is responsible
* for freeing any dynamically allocated data)
* this is a no-op if called during 'undo' (resp. 'redo').
*/
EXTERN void pd_undo_set_objectstate(t_canvas*canvas, t_pd*x, t_symbol*s,
int undo_argc, t_atom*undo_argv,
int redo_argc, t_atom*redo_argv);
#if defined(_LANGUAGE_C_PLUS_PLUS) || defined(__cplusplus)
}
#endif
#define __m_pd_h_
#endif /* __m_pd_h_ */
hvcc-0.16.0/hvcc/generators/c2pdext/templates/Makefile 0000644 0000000 0000000 00000001147 00000000000 017516 0 ustar 00 # Makefile to build class '{name}' for Pure Data.
# Needs Makefile.pdlibbuilder as helper makefile for platform-dependent build
# settings and rules.
# library name
lib.name = {{name}}
ldlibs += -lstdc++
# input source file (class name == source file basename)
class.sources = pdext/{{name}}~.c
common.sources = $(filter-out pdext/{{name}}~.c, $(wildcard pdext/*.c))
common.sources += $(wildcard pdext/*.cpp)
# all extra files to be included in binary distribution of the library
#datafiles = helloworld-help.pd helloworld-meta.pd README.md
PDLIBBUILDER_DIR=.
include $(PDLIBBUILDER_DIR)/Makefile.pdlibbuilder
hvcc-0.16.0/hvcc/generators/c2pdext/templates/pd_external.c 0000644 0000000 0000000 00000012243 00000000000 020526 0 ustar 00 {{copyright}}
#if _MSC_VER || _WIN32 || _WIN64
#include
#define DLL_EXPORT __declspec(dllexport) extern
#else
#include
#define DLL_EXPORT
#endif
#include
#include "m_pd.h"
#include "Heavy_{{name}}.h"
static t_class *{{struct_name}}_class;
typedef struct _{{struct_name}} {
t_object x_obj;
{# declare inlets and outlets #}
{%- for i in range(1, num_input_channels) %}
t_inlet *inlet{{i}};
{%- endfor %}
{%- for i in range(num_output_channels) %}
t_outlet *outlet{{i}};
{%- endfor %}
t_outlet *msgOutlet;
HeavyContextInterface *hv;
t_float f; // dummy
} t_{{struct_name}};
static void printHook(HeavyContextInterface *c, const char *receiverName, const char *msgString, const HvMessage *m) {
double timestampMs = 1000.0 * ((double) hv_msg_getTimestamp(m)) / hv_getSampleRate(c);
post("[{{display_name}} @ %0.3gms] %s: %s", timestampMs, receiverName, msgString);
}
static void sendHook(HeavyContextInterface *c, const char *receiverName, unsigned int receiverHash, const HvMessage * m) {
if (!strcmp(receiverName, "HV_TO_PD")) {
t_outlet *const outlet = ((t_{{struct_name}} *) hv_getUserData(c))->msgOutlet;
if (hv_msg_getNumElements(m) == 1) {
if (hv_msg_isFloat(m, 0)) {
outlet_float(outlet, hv_msg_getFloat(m, 0));
} else if (hv_msg_isBang(m, 0)) {
outlet_bang(outlet);
} else if (hv_msg_isSymbol(m, 0)) {
outlet_symbol(outlet, gensym(hv_msg_getSymbol(m, 0)));
} else return;
} else {
const int argc = (int) hv_msg_getNumElements(m);
t_atom *argv = (t_atom *) alloca(argc*sizeof(t_atom));
for (int i = 0; i < argc; i++) {
if (hv_msg_isFloat(m, i)) {
SETFLOAT(argv+i, hv_msg_getFloat(m, i));
} else if (hv_msg_isSymbol(m, i)) {
SETSYMBOL(argv+i, gensym(hv_msg_getSymbol(m, i)));
} else return;
}
outlet_list(outlet, NULL, argc, argv);
}
}
}
static void *{{struct_name}}_new(t_symbol *s, int argc, t_atom *argv) {
t_{{struct_name}} *x = (t_{{struct_name}} *) pd_new({{struct_name}}_class);
{%- for i in range(1, num_input_channels) %}
x->inlet{{i}} = signalinlet_new(&x->x_obj, 0.0f);
{%- endfor %}
{%- for i in range(num_output_channels) %}
x->outlet{{i}} = outlet_new(&x->x_obj, &s_signal);
{%- endfor %}
x->msgOutlet = outlet_new(&x->x_obj, &s_anything);
x->hv = hv_{{name}}_new((double) sys_getsr());
hv_setUserData(x->hv, x);
hv_setPrintHook(x->hv, printHook);
hv_setSendHook(x->hv, sendHook);
return (void *) x;
}
static void {{struct_name}}_free(t_{{struct_name}} *x) {
{%- for i in range(num_input_channels-1) %}
inlet_free(x->inlet{{i+1}});
{%- endfor %}
{%- for i in range(num_output_channels) %}
outlet_free(x->outlet{{i}});
{%- endfor %}
outlet_free(x->msgOutlet);
hv_delete(x->hv);
}
static t_int *{{struct_name}}_perform(t_int *w) {
t_{{struct_name}} *x = (t_{{struct_name}} *) w[1];
{%- if num_input_channels > 0 %}
float *inputChannels[{{num_input_channels}}] = {
{%- for i in range(num_input_channels) %}
(t_sample *) w[{{i+2}}],
{%- endfor %}
};
{%- else %}
float **inputChannels = NULL;
{%- endif %}
{%- if num_output_channels > 0 %}
float *outputChannels[{{num_output_channels}}] = {
{%- for i in range(num_output_channels) %}
(t_sample *) w[{{num_input_channels+i+2}}],
{%- endfor %}
};
{%- else %}
float **outputChannels = NULL;
{%- endif %}
int n = (int) (w[{{num_input_channels+num_output_channels+2}}]);
hv_process(x->hv, inputChannels, outputChannels, n);
return (w+{{num_input_channels+num_output_channels+3}});
}
static void {{struct_name}}_dsp(t_{{struct_name}} *x, t_signal **sp) {
dsp_add({{struct_name}}_perform,
{{num_input_channels+num_output_channels+2}}, x,
{%- for i in range(num_input_channels) %}
sp[{{i}}]->s_vec,
{%- endfor %}
{%- for i in range(num_input_channels|max(1), num_input_channels|max(1)+num_output_channels) %}
sp[{{i}}]->s_vec,
{%- endfor %}
sp[0]->s_n);
}
static void {{struct_name}}_onMessage(t_{{struct_name}} *x, t_symbol *s0, int argc, t_atom* argv) {
// convert a Pd message into a Heavy message
HvMessage *msg = (HvMessage *) alloca(hv_msg_getByteSize(argc > 0 ? argc : 1));
if (argc > 0) {
hv_msg_init(msg, argc, 0);
for (int i = 0; i < argc; i++) {
switch (argv[i].a_type) {
case A_FLOAT: hv_msg_setFloat(msg, i, atom_getfloat(argv+i)); break;
case A_SYMBOL: hv_msg_setSymbol(msg, i, atom_getsymbol(argv+i)->s_name); break;
default: return;
}
}
} else {
hv_msg_init(msg, 1, 0);
hv_msg_setBang(msg, 0);
}
hv_sendMessageToReceiver(x->hv, hv_stringToHash(s0->s_name), 0.0, msg);
}
DLL_EXPORT void {{struct_name}}_setup() {
{{struct_name}}_class = class_new(gensym("{{display_name}}"),
(t_newmethod) {{struct_name}}_new,
(t_method) {{struct_name}}_free,
sizeof(t_{{struct_name}}),
CLASS_DEFAULT, A_GIMME, 0);
class_addmethod({{struct_name}}_class, (t_method) {{struct_name}}_dsp, gensym("dsp"), 0);
class_addanything({{struct_name}}_class, (t_method) {{struct_name}}_onMessage);
CLASS_MAINSIGNALIN({{struct_name}}_class, t_{{struct_name}}, f);
}
{# force new line #}
hvcc-0.16.0/hvcc/generators/c2unity/__init__.py 0000644 0000000 0000000 00000000000 00000000000 016200 0 ustar 00 hvcc-0.16.0/hvcc/generators/c2unity/c2unity.py 0000644 0000000 0000000 00000010744 00000000000 016056 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2021-2026 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 jinja2
import os
import shutil
import time
from typing import Optional
from pathlib import Path
from ..copyright import copyright_manager
from ..filters import filter_templates
from hvcc.interpreters.pd2hv.NotificationEnum import NotificationEnum
from hvcc.types.compiler import Generator, CompilerResp, CompilerNotif, CompilerMsg, ExternInfo
from hvcc.types.meta import Meta
class c2unity(Generator):
"""Generates a Audio Native Plugin wrapper for Unity 5.
"""
@classmethod
def compile(
cls,
c_src_dir: Path,
out_dir: Path,
externs: ExternInfo,
patch_name: str,
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()
in_parameter_list = externs.parameters.inParam
out_parameter_list = externs.parameters.outParam
event_list = externs.events.inEvent
out_event_list = externs.events.outEvent
table_list = externs.tables
out_dir = Path(out_dir, "unity")
patch_name = patch_name.lower() if patch_name is not None else "heavy"
copyright_c = copyright_manager.get_copyright_for_c(copyright)
templates_dir = Path(Path(__file__).parent, "templates")
# initialise the jinja template environment
env = jinja2.Environment()
env.loader = jinja2.FileSystemLoader(
encoding="utf-8-sig", searchpath=[templates_dir]
)
try:
# ensure that the output directory does not exist
out_dir = out_dir.absolute()
if out_dir.exists():
shutil.rmtree(out_dir)
patch_src_dir = Path(out_dir, "include", "Heavy")
if patch_src_dir.exists():
shutil.rmtree(patch_src_dir)
shutil.copytree(c_src_dir, patch_src_dir)
heavy_src_files = [f for f in os.listdir(c_src_dir) if f.endswith(".c") or f.endswith(".cpp")]
# generate files from templates
for f in env.list_templates(filter_func=filter_templates):
file_path = Path(out_dir, f)
file_path = Path(str(file_path).replace("{{name}}", patch_name))
if not file_path.parent.exists():
os.makedirs(file_path.parent)
with open(file_path, "w") as g:
g.write(env.get_template(f).render(
name=patch_name,
in_params=in_parameter_list,
out_params=out_parameter_list,
out_events=out_event_list,
events=event_list,
tables=table_list,
pool_sizes_kb=externs.memoryPoolSizesKb,
num_input_channels=num_input_channels,
num_output_channels=num_output_channels,
heavy_src_files=heavy_src_files,
copyright=copyright_c))
return CompilerResp(
stage="c2unity",
in_dir=c_src_dir,
out_dir=out_dir,
compile_time=time.time() - tick
)
except Exception as e:
return CompilerResp(
stage="c2unity",
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
)
hvcc-0.16.0/hvcc/generators/c2unity/static/source/unity/AudioPluginInterface.h 0000644 0000000 0000000 00000026374 00000000000 024266 0 ustar 00 #pragma once
#define UNITY_AUDIO_PLUGIN_API_VERSION 0x010000
#ifndef UNITY_PREFIX_CONFIGURE_H
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(_WIN64)
# define UNITY_WIN 1
#elif defined(__MACH__) || defined(__APPLE__)
# define UNITY_OSX 1
#elif defined(__ANDROID__)
# define UNITY_ANDROID 1
#elif defined(__linux__)
# define UNITY_LINUX 1
#elif defined(__PS3__)
# define UNITY_PS3 1
#elif defined(__SPU__)
# define UNITY_SPU 1
#endif
#if defined(_AMD64_) || defined(__LP64__)
# define UNITY_64 1
# define UNITY_32 0
#else
# define UNITY_64 0
# define UNITY_32 1
#endif
#ifndef SInt16_defined
# define SInt16_defined
typedef signed short SInt16;
#endif
#ifndef UInt16_defined
# define UInt16_defined
typedef unsigned short UInt16;
#endif
#ifndef UInt8_defined
# define UInt8_defined
typedef unsigned char UInt8;
#endif
#ifndef SInt8_defined
# define SInt8_defined
typedef signed char SInt8;
#endif
#if UNITY_64
# if UNITY_LINUX
# ifndef SInt32_defined
# define SInt32_defined
typedef signed int SInt32;
# endif
# ifndef UInt32_defined
# define UInt32_defined
typedef unsigned int UInt32;
# endif
# ifndef UInt64_defined
# define UInt64_defined
typedef unsigned long UInt64;
# endif
# ifndef SInt64_defined
# define SInt64_defined
typedef signed long SInt64;
# endif
# elif UNITY_OSX
# ifndef SInt32_defined
# define SInt32_defined
typedef signed int SInt32;
# endif
# ifndef UInt32_defined
# define UInt32_defined
typedef unsigned int UInt32;
# endif
# ifndef UInt64_defined
# define UInt64_defined
typedef unsigned long long UInt64;
# endif
# ifndef SInt64_defined
# define SInt64_defined
typedef signed long long SInt64;
# endif
# elif UNITY_WIN
# ifndef SInt32_defined
# define SInt32_defined
typedef signed long SInt32;
# endif
# ifndef UInt32_defined
# define UInt32_defined
typedef unsigned long UInt32;
# endif
# ifndef UInt64_defined
# define UInt64_defined
typedef unsigned long long UInt64;
# endif
# ifndef SInt64_defined
# define SInt64_defined
typedef signed long long SInt64;
# endif
# elif UNITY_ANDROID
# ifndef SInt32_defined
# define SInt32_defined
typedef signed int SInt32;
# endif
# ifndef UInt32_defined
# define UInt32_defined
typedef unsigned int UInt32;
# endif
# ifndef UInt64_defined
# define UInt64_defined
typedef unsigned long long UInt64;
# endif
# ifndef SInt64_defined
# define SInt64_defined
typedef signed long long SInt64;
# endif
#endif
#else
# ifndef SInt32_defined
# define SInt32_defined
typedef signed int SInt32;
# endif
# ifndef UInt32_defined
# define UInt32_defined
typedef unsigned int UInt32;
# endif
# ifndef UInt64_defined
# define UInt64_defined
typedef unsigned long long UInt64;
# endif
# ifndef SInt64_defined
# define SInt64_defined
typedef signed long long SInt64;
# endif
#endif
#endif
#if UNITY_WIN
#define UNITY_AUDIODSP_CALLBACK __stdcall
#elif UNITY_OSX
#define UNITY_AUDIODSP_CALLBACK
#else
#define UNITY_AUDIODSP_CALLBACK
#endif
// Attribute to make function be exported from a plugin
#if UNITY_WIN
#define UNITY_AUDIODSP_EXPORT_API __declspec(dllexport)
#else
#define UNITY_AUDIODSP_EXPORT_API
#endif
#if defined(__CYGWIN32__)
#define UNITY_AUDIODSP_CALLBACK __stdcall
#elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(_WIN64)
#define UNITY_AUDIODSP_CALLBACK __stdcall
#elif defined(__MACH__) || defined(__ANDROID__) || defined(__linux__) || defined(__QNX__)
#define UNITY_AUDIODSP_CALLBACK
#else
#define UNITY_AUDIODSP_CALLBACK
#endif
#define UNITY_AUDIODSP_RESULT int
#if !UNITY_SPU // asserts require _exit() to be defined
#include
#endif
enum
{
UNITY_AUDIODSP_OK = 0,
UNITY_AUDIODSP_ERR_UNSUPPORTED = 1,
};
struct UnityAudioEffectState;
typedef UNITY_AUDIODSP_RESULT (UNITY_AUDIODSP_CALLBACK * UnityAudioEffect_CreateCallback)(UnityAudioEffectState* state);
typedef UNITY_AUDIODSP_RESULT (UNITY_AUDIODSP_CALLBACK * UnityAudioEffect_ReleaseCallback)(UnityAudioEffectState* state);
typedef UNITY_AUDIODSP_RESULT (UNITY_AUDIODSP_CALLBACK * UnityAudioEffect_ResetCallback)(UnityAudioEffectState* state);
typedef UNITY_AUDIODSP_RESULT (UNITY_AUDIODSP_CALLBACK * UnityAudioEffect_ProcessCallback)(UnityAudioEffectState* state, float* inbuffer, float* outbuffer, unsigned int length, int inchannels, int outchannels);
typedef UNITY_AUDIODSP_RESULT (UNITY_AUDIODSP_CALLBACK * UnityAudioEffect_SetPositionCallback)(UnityAudioEffectState* state, unsigned int pos);
typedef UNITY_AUDIODSP_RESULT (UNITY_AUDIODSP_CALLBACK * UnityAudioEffect_SetFloatParameterCallback)(UnityAudioEffectState* state, int index, float value);
typedef UNITY_AUDIODSP_RESULT (UNITY_AUDIODSP_CALLBACK * UnityAudioEffect_GetFloatParameterCallback)(UnityAudioEffectState* state, int index, float* value, char *valuestr);
typedef UNITY_AUDIODSP_RESULT (UNITY_AUDIODSP_CALLBACK * UnityAudioEffect_GetFloatBufferCallback)(UnityAudioEffectState* state, const char* name, float* buffer, int numsamples);
enum UnityAudioEffectDefinitionFlags
{
UnityAudioEffectDefinitionFlags_IsSideChainTarget = 1 << 0, // Does this effect need a side chain buffer and can it be targeted by a Send?
};
enum UnityAudioEffectStateFlags
{
UnityAudioEffectStateFlags_IsPlaying = 1 << 0, // Set when engine is in play mode. Also true while paused.
UnityAudioEffectStateFlags_IsPaused = 1 << 1, // Set when engine is paused mode.
UnityAudioEffectStateFlags_IsMuted = 1 << 2, // Set when effect is being muted (only available in the editor)
UnityAudioEffectStateFlags_IsSideChainTarget = 1 << 3, // Does this effect need a side chain buffer and can it be targeted by a Send?
};
struct UnityAudioEffectState
{
union
{
struct
{
UInt32 structsize; // Size of this struct
UInt32 samplerate; // System sample rate
UInt64 currdsptick; // Pointer to a sample counter marking the start of the current block being processed
UInt64 prevdsptick; // Used for determining when DSPs are bypassed and so sidechain info becomes invalid
float* sidechainbuffer; // Side-chain buffers to read from
void* effectdata; // Internal data for the effect
UInt32 flags; // Various flags through which information can be queried from the host
void* internal; // Internal data, do not touch!
};
unsigned char pad[64]; // This entire structure must be a multiple of 16 bytes (and and instance 16 byte aligned) for PS3 SPU DMA requirements
};
#ifdef __cplusplus
template inline T* GetEffectData() const
{
#if !UNITY_SPU // asserts require _exit() to be defined
assert(effectdata);
assert(internal);
#endif
return (T*)effectdata;
}
#endif
};
struct UnityAudioParameterDefinition
{
char name[16]; // Display name on the GUI
char unit[16]; // Scientific unit of parameter to be appended after the value in textboxes
const char* description; // Description of parameter (displayed in tool tips, automatically generated documentation, etc.)
float min; // Minimum value of the parameter
float max; // Maximum value of the parameter
float defaultval; // Default and initial value of the parameter
float displayscale; // Scale factor used only for the display of parameters (i.e. 100 for a percentage value ranging from 0 to 1)
float displayexponent; // Exponent for mapping parameters to sliders
};
struct UnityAudioEffectDefinition
{
UInt32 structsize; // Size of this struct
UInt32 paramstructsize; // Size of paramdesc fields
UInt32 apiversion; // Plugin API version
UInt32 pluginversion; // Version of this plugin
UInt32 channels; // Number of channels. Effects should set this to 0 and process any number of input/output channels they get in the process callback. Generator elements should specify a >0 value here.
UInt32 numparameters; // The number of parameters exposed by this plugin.
UInt64 flags; // Various capabilities and requirements of the plugin.
char name[32]; // Name used for registration of the effect. This name will also be displayed in the GUI.
UnityAudioEffect_CreateCallback create; // The create callback is called when DSP unit is created and can be null.
UnityAudioEffect_ReleaseCallback release; // The release callback is called just before the plugin is freed and should free any data associated with this specific instance of the plugin. No further callbacks related to the instance will happen after this function has been called.
UnityAudioEffect_ResetCallback reset; // The reset callback is called by the user to bring back the plugin instance into its initial state. Use to avoid clicks or artifacts.
UnityAudioEffect_ProcessCallback process; // The processing callback is repeatedly called with a block of input audio to read from and an output block to write to.
UnityAudioEffect_SetPositionCallback setposition; // The position callback can be used for implementing seek operations.
UnityAudioParameterDefinition* paramdefs; // A pointer to the definitions of the parameters exposed by this plugin. This data pointed to must remain valid for the whole lifetime of the dynamic library (ideally it's static).
UnityAudioEffect_SetFloatParameterCallback setfloatparameter; // This is called whenever one of the exposed parameters is changed.
UnityAudioEffect_GetFloatParameterCallback getfloatparameter; // This is called to query parameter values.
UnityAudioEffect_GetFloatBufferCallback getfloatbuffer; // Get N samples of named buffer. Used for displaying analysis data from the runtime.
};
// This function fills in N pointers for the N effects contained in the library and returns N.
extern "C" UNITY_AUDIODSP_EXPORT_API int UnityGetAudioEffectDefinitions(UnityAudioEffectDefinition*** descptr);
hvcc-0.16.0/hvcc/generators/c2unity/static/source/unity/AudioPluginUtil.cpp 0000644 0000000 0000000 00000012642 00000000000 023627 0 ustar 00 #include "AudioPluginUtil.h"
char* strnew(const char* src)
{
char* newstr = new char[strlen(src) + 1];
strcpy(newstr, src);
return newstr;
}
void RegisterParameter(
UnityAudioEffectDefinition& definition,
const char* name,
const char* unit,
float minval,
float maxval,
float defaultval,
float displayscale,
float displayexponent,
int enumvalue,
const char* description
)
{
assert(defaultval >= minval);
assert(defaultval <= maxval);
strcpy_s(definition.paramdefs[enumvalue].name, name);
strcpy_s(definition.paramdefs[enumvalue].unit, unit);
definition.paramdefs[enumvalue].description = (description != NULL) ? strnew(description) : (name != NULL) ? strnew(name) : NULL;
definition.paramdefs[enumvalue].defaultval = defaultval;
definition.paramdefs[enumvalue].displayscale = displayscale;
definition.paramdefs[enumvalue].displayexponent = displayexponent;
definition.paramdefs[enumvalue].min = minval;
definition.paramdefs[enumvalue].max = maxval;
if (enumvalue >= (int)definition.numparameters)
definition.numparameters = enumvalue + 1;
}
// Helper function to fill default values from the effect definition into the params array -- called by Create callbacks
void InitParametersFromDefinitions(
InternalEffectDefinitionRegistrationCallback registereffectdefcallback,
float* params
)
{
UnityAudioEffectDefinition definition;
memset(&definition, 0, sizeof(definition));
registereffectdefcallback(definition);
for (UInt32 n = 0; n < definition.numparameters; n++)
{
params[n] = definition.paramdefs[n].defaultval;
delete[] definition.paramdefs[n].description;
}
delete[] definition.paramdefs; // assumes that definition.paramdefs was allocated by registereffectdefcallback or is NULL
}
void DeclareEffect(
UnityAudioEffectDefinition& definition,
const char* name,
UnityAudioEffect_CreateCallback createcallback,
UnityAudioEffect_ReleaseCallback releasecallback,
UnityAudioEffect_ProcessCallback processcallback,
UnityAudioEffect_SetFloatParameterCallback setfloatparametercallback,
UnityAudioEffect_GetFloatParameterCallback getfloatparametercallback,
UnityAudioEffect_GetFloatBufferCallback getfloatbuffercallback,
InternalEffectDefinitionRegistrationCallback registereffectdefcallback
)
{
memset(&definition, 0, sizeof(definition));
strcpy_s(definition.name, name);
definition.structsize = sizeof(UnityAudioEffectDefinition);
definition.paramstructsize = sizeof(UnityAudioParameterDefinition);
definition.apiversion = UNITY_AUDIO_PLUGIN_API_VERSION;
definition.pluginversion = 0x010000;
definition.create = createcallback;
definition.release = releasecallback;
definition.process = processcallback;
definition.setfloatparameter = setfloatparametercallback;
definition.getfloatparameter = getfloatparametercallback;
definition.getfloatbuffer = getfloatbuffercallback;
registereffectdefcallback(definition);
}
#if UNITY_PS3
#define DECLARE_EFFECT(namestr,ns) \
extern char _binary_spu_ ## ns ## _spu_elf_start[];
#include "PluginList.h"
#undef DECLARE_EFFECT
#endif
#define DECLARE_EFFECT(namestr,ns) \
namespace ns \
{ \
UNITY_AUDIODSP_RESULT UNITY_AUDIODSP_CALLBACK CreateCallback (UnityAudioEffectState* state); \
UNITY_AUDIODSP_RESULT UNITY_AUDIODSP_CALLBACK ReleaseCallback (UnityAudioEffectState* state); \
UNITY_AUDIODSP_RESULT UNITY_AUDIODSP_CALLBACK ProcessCallback (UnityAudioEffectState* state, float* inbuffer, float* outbuffer, unsigned int length, int inchannels, int outchannels); \
UNITY_AUDIODSP_RESULT UNITY_AUDIODSP_CALLBACK SetFloatParameterCallback (UnityAudioEffectState* state, int index, float value); \
UNITY_AUDIODSP_RESULT UNITY_AUDIODSP_CALLBACK GetFloatParameterCallback (UnityAudioEffectState* state, int index, float* value, char *valuestr); \
UNITY_AUDIODSP_RESULT UNITY_AUDIODSP_CALLBACK GetFloatBufferCallback (UnityAudioEffectState* state, const char* name, float* buffer, int numsamples); \
int InternalRegisterEffectDefinition(UnityAudioEffectDefinition& definition); \
}
#include "PluginList.h"
#undef DECLARE_EFFECT
#if UNITY_PS3
#define DECLARE_EFFECT(namestr,ns) \
DeclareEffect( \
definition[numeffects++], \
namestr, \
ns::CreateCallback, \
ns::ReleaseCallback, \
(UnityAudioEffect_ProcessCallback)_binary_spu_ ## ns ## _spu_elf_start, \
ns::SetFloatParameterCallback, \
ns::GetFloatParameterCallback, \
ns::GetFloatBufferCallback, \
ns::InternalRegisterEffectDefinition);
#else
#define DECLARE_EFFECT(namestr,ns) \
DeclareEffect( \
definition[numeffects++], \
namestr, \
ns::CreateCallback, \
ns::ReleaseCallback, \
ns::ProcessCallback, \
ns::SetFloatParameterCallback, \
ns::GetFloatParameterCallback, \
ns::GetFloatBufferCallback, \
ns::InternalRegisterEffectDefinition);
#endif
extern "C" UNITY_AUDIODSP_EXPORT_API int UnityGetAudioEffectDefinitions(UnityAudioEffectDefinition*** definitionptr)
{
static UnityAudioEffectDefinition definition[256];
static UnityAudioEffectDefinition* definitionp[256];
static int numeffects = 0;
if (numeffects == 0)
{
#include "PluginList.h"
}
for (int n = 0; n < numeffects; n++)
definitionp[n] = &definition[n];
*definitionptr = definitionp;
return numeffects;
}
hvcc-0.16.0/hvcc/generators/c2unity/static/source/unity/AudioPluginUtil.h 0000644 0000000 0000000 00000002567 00000000000 023301 0 ustar 00 #pragma once
#include "AudioPluginInterface.h"
#include
#include
#include
#include
#include
#if UNITY_WIN
# include
#else
# if UNITY_SPU
# include
# include "ps3/AudioPluginInterfacePS3.h"
# else
# include
# endif
# define strcpy_s strcpy
#endif
typedef int (*InternalEffectDefinitionRegistrationCallback)(UnityAudioEffectDefinition& desc);
void RegisterParameter(
UnityAudioEffectDefinition& desc,
const char* name,
const char* unit,
float minval,
float maxval,
float defaultval,
float displayscale,
float displayexponent,
int enumvalue,
const char* description = NULL);
void InitParametersFromDefinitions(
InternalEffectDefinitionRegistrationCallback registereffectdefcallback,
float* params);
void DeclareEffect(
UnityAudioEffectDefinition& desc,
const char* name,
UnityAudioEffect_CreateCallback createcallback,
UnityAudioEffect_ReleaseCallback releasecallback,
UnityAudioEffect_ProcessCallback processcallback,
UnityAudioEffect_SetFloatParameterCallback setfloatparametercallback,
UnityAudioEffect_GetFloatParameterCallback getfloatparametercallback,
UnityAudioEffect_GetFloatBufferCallback getfloatbuffercallback,
InternalEffectDefinitionRegistrationCallback registereffectdefcallback);
hvcc-0.16.0/hvcc/generators/c2unity/static/xcode/Info.plist 0000644 0000000 0000000 00000002365 00000000000 020450 0 ustar 00
CFBundleDevelopmentRegionEnglishCFBundleExecutable${EXECUTABLE_NAME}CFBundleIconFileCFBundleIdentifier$(PRODUCT_BUNDLE_IDENTIFIER)CFBundleInfoDictionaryVersion6.0CFBundleName${PRODUCT_NAME}CFBundlePackageTypeBNDLCFBundleShortVersionString1.0CFBundleSignature????CFBundleVersion1CFPlugInDynamicRegisterFunctionCFPlugInDynamicRegistrationNOCFPlugInFactories00000000-0000-0000-0000-000000000000MyFactoryFunctionCFPlugInTypes00000000-0000-0000-0000-00000000000000000000-0000-0000-0000-000000000000CFPlugInUnloadFunction
hvcc-0.16.0/hvcc/generators/c2unity/templates/CMakeLists.txt 0000644 0000000 0000000 00000003737 00000000000 020651 0 ustar 00 cmake_minimum_required(VERSION 3.29.0)
project(Hv_{{name}})
set(TARGET_NAME_LIB ${PROJECT_NAME}_AudioLib)
set(TARGET_NAME_PLUGIN ${PROJECT_NAME}_AudioPlugin)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED 17)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set(CMAKE_STATIC_LIBRARY_PREFIX "")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build/$)
set(CMAKE_SHARED_LIBRARY_PREFIX "")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build/$)
include(FetchContent)
FetchContent_Declare(
unitysdk
DOWNLOAD_EXTRACT_TIMESTAMP OFF
URL https://github.com/Unity-Technologies/NativeAudioPlugins/archive/refs/heads/master.zip
SOURCE_DIR "${CMAKE_SOURCE_DIR}/vendor/UnityAudioSDK"
PATCH_COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/src/PluginList.h ${CMAKE_SOURCE_DIR}/vendor/UnityAudioSDK/NativeCode
)
FetchContent_GetProperties(unitysdk)
if (NOT unitysdk_POPULATED)
FetchContent_MakeAvailable(unitysdk)
endif()
add_library(
objlib OBJECT
)
set_target_properties(
objlib PROPERTIES
POSITION_INDEPENDENT_CODE True
)
target_sources(
objlib PRIVATE
{%- for file in heavy_src_files %}
${CMAKE_SOURCE_DIR}/include/Heavy/{{file}}
{%- endfor %}
)
target_include_directories(
objlib PRIVATE
${CMAKE_SOURCE_DIR}/include/Heavy
)
add_library(${TARGET_NAME_LIB} SHARED $)
add_custom_command(
TARGET ${TARGET_NAME_LIB}
POST_BUILD
COMMAND ${CMAKE_COMMAND}
ARGS -E copy ${CMAKE_SOURCE_DIR}/src/Hv_{{name}}_AudioLib.cs
${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/Hv_{{name}}_AudioLib.cs
)
add_library(${TARGET_NAME_PLUGIN} SHARED $)
target_sources(
${TARGET_NAME_PLUGIN} PRIVATE
${CMAKE_SOURCE_DIR}/src/{{name}}.cpp
${CMAKE_SOURCE_DIR}/vendor/UnityAudioSDK/NativeCode/AudioPluginUtil.cpp
)
target_include_directories(
${TARGET_NAME_PLUGIN} PRIVATE
${CMAKE_SOURCE_DIR}/include/Heavy
${CMAKE_SOURCE_DIR}/vendor/UnityAudioSDK/NativeCode
)
hvcc-0.16.0/hvcc/generators/c2unity/templates/src/Hv_{{name}}_AudioLib.cs 0000644 0000000 0000000 00000036022 00000000000 023325 0 ustar 00 {%- set hasParams = true if in_params | length > 0 -%}
{%- set hasEvents = true if events|length > 0 -%}
{%- set hasTables = true if tables|length > 0 -%}
{{copyright}}
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.Assertions;
using AOT;
#if UNITY_EDITOR
using UnityEditor;
[CustomEditor(typeof(Hv_{{name}}_AudioLib))]
public class Hv_{{name}}_Editor : Editor {
[MenuItem("Heavy/{{name}}")]
static void CreateHv_{{name}}() {
GameObject target = Selection.activeGameObject;
if (target != null) {
target.AddComponent();
}
}
{%- if hasParams or hasEvents or hasTables %}
private Hv_{{name}}_AudioLib _dsp;
private void OnEnable() {
_dsp = target as Hv_{{name}}_AudioLib;
}
public override void OnInspectorGUI() {
bool isEnabled = _dsp.IsInstantiated();
if (!isEnabled) {
EditorGUILayout.LabelField("Press Play!", EditorStyles.centeredGreyMiniLabel);
}
GUILayout.BeginVertical();
{% if hasEvents -%}
// EVENTS
GUI.enabled = isEnabled;
EditorGUILayout.Space();
{%- for k, v in events %}
// {{v.display}}
if (GUILayout.Button("{{v.display}}")) {
_dsp.SendEvent(Hv_{{name}}_AudioLib.Event.{{k|title}});
}
{%- endfor %}
{%- endif %}
{% if hasParams -%}
// PARAMETERS
GUI.enabled = true;
EditorGUILayout.Space();
EditorGUI.indentLevel++;
{%- for k, v in in_params %}
// {{v.display}}
GUILayout.BeginHorizontal();
float {{k}} = _dsp.GetFloatParameter(Hv_{{name}}_AudioLib.Parameter.{{k|title}});
float new{{k|title}} = EditorGUILayout.Slider("{{v.display}}", {{k}}, {{v.attributes.min}}f, {{v.attributes.max}}f);
if ({{k}} != new{{k|title}}) {
_dsp.SetFloatParameter(Hv_{{name}}_AudioLib.Parameter.{{k|title}}, new{{k|title}});
}
GUILayout.EndHorizontal();
{%- endfor %}
EditorGUI.indentLevel--;
{%- endif %}
{% if hasTables -%}
// TABLES
GUI.enabled = true;
EditorGUILayout.Space();
EditorGUI.indentLevel++;
{%- for k, v in tables %}
// {{v.display}}
EditorGUI.BeginChangeCheck();
AudioClip {{k}}Clip = EditorGUILayout.ObjectField("{{v.display}}", _dsp.{{k}}Clip, typeof(AudioClip), false) as AudioClip;
if (EditorGUI.EndChangeCheck()) {
_dsp.{{k}}Clip = {{k}}Clip;
}
{%- endfor %}
EditorGUI.indentLevel--;
{%- endif %}
GUILayout.EndVertical();
}
{%- endif %}
}
#endif // UNITY_EDITOR
[RequireComponent (typeof (AudioSource))]
public class Hv_{{name}}_AudioLib : MonoBehaviour {
{% if hasEvents %}
// Events are used to trigger bangs in the patch context (thread-safe).
// Example usage:
/*
void Start () {
Hv_{{name}}_AudioLib script = GetComponent();
script.SendEvent(Hv_{{name}}_AudioLib.Event.{{events[0][0]|title}});
}
*/
public enum Event : uint {
{%- for k, v in events %}
{{k|title}} = {{v.hash}},
{%- endfor %}
}
{% endif %}
{%- if hasParams %}
// Parameters are used to send float messages into the patch context (thread-safe).
// Example usage:
/*
void Start () {
Hv_{{name}}_AudioLib script = GetComponent();
// Get and set a parameter
float {{in_params[0][0]}} = script.GetFloatParameter(Hv_{{name}}_AudioLib.Parameter.{{in_params[0][0]|title}});
script.SetFloatParameter(Hv_{{name}}_AudioLib.Parameter.{{in_params[0][0]|title}}, {{in_params[0][0]}} + 0.1f);
}
*/
public enum Parameter : uint {
{%- for k,v in in_params %}
{{k|title}} = {{v.hash}},
{%- endfor %}
}
{% endif %}
{%- if hasTables %}
// Tables within the patch context can be filled directly with audio content
// Example usage:
/*
public AudioClip clip;
void Start () {
Hv_{{name}}_AudioLib script = GetComponent();
// copy clip contents into a temporary buffer
float[] buffer = new float[clip.samples];
clip.GetData(buffer, 0);
// fill a buffer called "channelL"
looper.FillTableWithFloatBuffer((uint) Hv_{{name}}_AudioLib.Table.Channell, buffer);
// notify a (non-exposed) receiver of the buffer size
looper.SendFloatToReceiver("setTableSize-channelL", clip.samples);
}
*/
public enum Table : uint {
{%- for k,v in tables %}
{{k|title}} = {{v.hash}},
{%- endfor %}
}
{% endif %}
// Delegate method for receiving float messages from the patch context (thread-safe).
// Example usage:
/*
void Start () {
Hv_{{name}}_AudioLib script = GetComponent();
script.RegisterSendHook();
script.FloatReceivedCallback += OnFloatMessage;
}
void OnFloatMessage(Hv_{{name}}_AudioLib.FloatMessage message) {
Debug.Log(message.receiverName + ": " + message.value);
}
*/
public class FloatMessage {
public string receiverName;
public float value;
public FloatMessage(string name, float x) {
receiverName = name;
value = x;
}
}
public delegate void FloatMessageReceived(FloatMessage message);
public FloatMessageReceived FloatReceivedCallback;
{%- for k, v in in_params %}
public float {{k}} = {{v.attributes.default}}f;
{%- endfor %}
{%- for k, v in tables %}
public AudioClip {{k}}Clip = null;
{%- endfor %}
// internal state
private Hv_{{name}}_Context _context;
public bool IsInstantiated() {
return (_context != null);
}
public void RegisterSendHook() {
_context.RegisterSendHook();
}
{% if hasEvents %}
// see Hv_{{name}}_AudioLib.Event for definitions
public void SendEvent(Hv_{{name}}_AudioLib.Event e) {
if (IsInstantiated()) _context.SendBangToReceiver((uint) e);
}
{% endif %}
{%- if hasParams %}
// see Hv_{{name}}_AudioLib.Parameter for definitions
public float GetFloatParameter(Hv_{{name}}_AudioLib.Parameter param) {
switch (param) {
{%- for k, v in in_params %}
case Parameter.{{k|title}}: return {{k}};
{%- endfor %}
default: return 0.0f;
}
}
public void SetFloatParameter(Hv_{{name}}_AudioLib.Parameter param, float x) {
switch (param) {
{%- for k, v in in_params %}
case Parameter.{{k|title}}: {
x = Mathf.Clamp(x, {{v.attributes.min}}f, {{v.attributes.max}}f);
{{k}} = x;
break;
}
{%- endfor %}
default: return;
}
if (IsInstantiated()) _context.SendFloatToReceiver((uint) param, x);
}
{% endif %}
public void SendFloatToReceiver(string receiverName, float x) {
_context.SendFloatToReceiver(StringToHash(receiverName), x);
}
public void FillTableWithMonoAudioClip(string tableName, AudioClip clip) {
if (clip.channels > 1) {
Debug.LogWarning("Hv_{{name}}_AudioLib: Only loading first channel of '" +
clip.name + "' into table '" +
tableName + "'. Multi-channel files are not supported.");
}
float[] buffer = new float[clip.samples]; // copy only the 1st channel
clip.GetData(buffer, 0);
_context.FillTableWithFloatBuffer(StringToHash(tableName), buffer);
}
public void FillTableWithMonoAudioClip(uint tableHash, AudioClip clip) {
if (clip.channels > 1) {
Debug.LogWarning("Hv_{{name}}_AudioLib: Only loading first channel of '" +
clip.name + "' into table '" +
tableHash + "'. Multi-channel files are not supported.");
}
float[] buffer = new float[clip.samples]; // copy only the 1st channel
clip.GetData(buffer, 0);
_context.FillTableWithFloatBuffer(tableHash, buffer);
}
public void FillTableWithFloatBuffer(string tableName, float[] buffer) {
_context.FillTableWithFloatBuffer(StringToHash(tableName), buffer);
}
public void FillTableWithFloatBuffer(uint tableHash, float[] buffer) {
_context.FillTableWithFloatBuffer(tableHash, buffer);
}
public uint StringToHash(string str) {
return _context.StringToHash(str);
}
private void Awake() {
_context = new Hv_{{name}}_Context((double) AudioSettings.outputSampleRate);
{% if hasTables -%}
// Note: only copies first channel from audio clips
{%- for k, v in tables %}
if ({{k}}Clip != null) {
// load buffer {{v.display}}
int length = {{k}}Clip.samples;
float[] buffer = new float[length];
{{k}}Clip.GetData(buffer, 0);
_context.FillTableWithFloatBuffer((uint) Hv_{{name}}_AudioLib.Table.{{k|title}}, buffer);
_context.SendFloatToReceiver(_context.StringToHash("setTableSize-{{v.display}}"), length);
}
{%- endfor %}
{%- endif %}
}
{% if hasParams %}
private void Start() {
{%- for k, v in in_params %}
_context.SendFloatToReceiver((uint) Parameter.{{k|title}}, {{k}});
{%- endfor %}
}
{% endif %}
private void Update() {
// retreive sent messages
if (_context.IsSendHookRegistered()) {
Hv_{{name}}_AudioLib.FloatMessage tempMessage;
while ((tempMessage = _context.msgQueue.GetNextMessage()) != null) {
FloatReceivedCallback(tempMessage);
}
}
}
private void OnAudioFilterRead(float[] buffer, int numChannels) {
Assert.AreEqual(numChannels, _context.GetNumOutputChannels()); // invalid channel configuration
_context.Process(buffer, buffer.Length / numChannels); // process dsp
}
}
class Hv_{{name}}_Context {
#if UNITY_IOS && !UNITY_EDITOR
private const string _dllName = "__Internal";
#else
private const string _dllName = "Hv_{{name}}_AudioLib";
#endif
// Thread-safe message queue
public class SendMessageQueue {
private readonly object _msgQueueSync = new object();
private readonly Queue _msgQueue = new Queue();
public Hv_{{name}}_AudioLib.FloatMessage GetNextMessage() {
lock (_msgQueueSync) {
return (_msgQueue.Count != 0) ? _msgQueue.Dequeue() : null;
}
}
public void AddMessage(string receiverName, float value) {
Hv_{{name}}_AudioLib.FloatMessage msg = new Hv_{{name}}_AudioLib.FloatMessage(receiverName, value);
lock (_msgQueueSync) {
_msgQueue.Enqueue(msg);
}
}
}
public readonly SendMessageQueue msgQueue = new SendMessageQueue();
private readonly GCHandle gch;
private readonly IntPtr _context; // handle into unmanaged memory
private SendHook _sendHook = null;
[DllImport (_dllName)]
private static extern IntPtr hv_{{name}}_new_with_options(double sampleRate, int poolKb, int inQueueKb, int outQueueKb);
[DllImport (_dllName)]
private static extern int hv_processInlineInterleaved(IntPtr ctx,
[In] float[] inBuffer, [Out] float[] outBuffer, int numSamples);
[DllImport (_dllName)]
private static extern void hv_delete(IntPtr ctx);
[DllImport (_dllName)]
private static extern double hv_getSampleRate(IntPtr ctx);
[DllImport (_dllName)]
private static extern int hv_getNumInputChannels(IntPtr ctx);
[DllImport (_dllName)]
private static extern int hv_getNumOutputChannels(IntPtr ctx);
[DllImport (_dllName)]
private static extern void hv_setSendHook(IntPtr ctx, SendHook sendHook);
[DllImport (_dllName)]
private static extern void hv_setPrintHook(IntPtr ctx, PrintHook printHook);
[DllImport (_dllName)]
private static extern int hv_setUserData(IntPtr ctx, IntPtr userData);
[DllImport (_dllName)]
private static extern IntPtr hv_getUserData(IntPtr ctx);
[DllImport (_dllName)]
private static extern void hv_sendBangToReceiver(IntPtr ctx, uint receiverHash);
[DllImport (_dllName)]
private static extern void hv_sendFloatToReceiver(IntPtr ctx, uint receiverHash, float x);
[DllImport (_dllName)]
private static extern uint hv_msg_getTimestamp(IntPtr message);
[DllImport (_dllName)]
private static extern bool hv_msg_hasFormat(IntPtr message, string format);
[DllImport (_dllName)]
private static extern float hv_msg_getFloat(IntPtr message, int index);
[DllImport (_dllName)]
private static extern bool hv_table_setLength(IntPtr ctx, uint tableHash, uint newSampleLength);
[DllImport (_dllName)]
private static extern IntPtr hv_table_getBuffer(IntPtr ctx, uint tableHash);
[DllImport (_dllName)]
private static extern float hv_samplesToMilliseconds(IntPtr ctx, uint numSamples);
[DllImport (_dllName)]
private static extern uint hv_stringToHash(string s);
private delegate void PrintHook(IntPtr context, string printName, string str, IntPtr message);
private delegate void SendHook(IntPtr context, string sendName, uint sendHash, IntPtr message);
public Hv_{{name}}_Context(double sampleRate, int poolKb={{pool_sizes_kb.internal}}, int inQueueKb={{pool_sizes_kb.inputQueue}}, int outQueueKb={{pool_sizes_kb.outputQueue}}) {
gch = GCHandle.Alloc(msgQueue);
_context = hv_{{name}}_new_with_options(sampleRate, poolKb, inQueueKb, outQueueKb);
hv_setPrintHook(_context, new PrintHook(OnPrint));
hv_setUserData(_context, GCHandle.ToIntPtr(gch));
}
~Hv_{{name}}_Context() {
hv_delete(_context);
GC.KeepAlive(_context);
GC.KeepAlive(_sendHook);
gch.Free();
}
public void RegisterSendHook() {
// Note: send hook functionality only applies to messages containing a single float value
if (_sendHook == null) {
_sendHook = new SendHook(OnMessageSent);
hv_setSendHook(_context, _sendHook);
}
}
public bool IsSendHookRegistered() {
return (_sendHook != null);
}
public double GetSampleRate() {
return hv_getSampleRate(_context);
}
public int GetNumInputChannels() {
return hv_getNumInputChannels(_context);
}
public int GetNumOutputChannels() {
return hv_getNumOutputChannels(_context);
}
public void SendBangToReceiver(uint receiverHash) {
hv_sendBangToReceiver(_context, receiverHash);
}
public void SendFloatToReceiver(uint receiverHash, float x) {
hv_sendFloatToReceiver(_context, receiverHash, x);
}
public void FillTableWithFloatBuffer(uint tableHash, float[] buffer) {
if (hv_table_getBuffer(_context, tableHash) != IntPtr.Zero) {
hv_table_setLength(_context, tableHash, (uint) buffer.Length);
Marshal.Copy(buffer, 0, hv_table_getBuffer(_context, tableHash), buffer.Length);
} else {
Debug.Log(string.Format("Table '{0}' doesn't exist in the patch context.", tableHash));
}
}
public uint StringToHash(string s) {
return hv_stringToHash(s);
}
public int Process(float[] buffer, int numFrames) {
return hv_processInlineInterleaved(_context, buffer, buffer, numFrames);
}
[MonoPInvokeCallback(typeof(PrintHook))]
private static void OnPrint(IntPtr context, string printName, string str, IntPtr message) {
float timeInSecs = hv_samplesToMilliseconds(context, hv_msg_getTimestamp(message)) / 1000.0f;
Debug.Log(string.Format("{0} [{1:0.000}]: {2}", printName, timeInSecs, str));
}
[MonoPInvokeCallback(typeof(SendHook))]
private static void OnMessageSent(IntPtr context, string sendName, uint sendHash, IntPtr message) {
if (hv_msg_hasFormat(message, "f")) {
SendMessageQueue msgQueue = (SendMessageQueue) GCHandle.FromIntPtr(hv_getUserData(context)).Target;
msgQueue.AddMessage(sendName, hv_msg_getFloat(message, 0));
}
}
}
{# force new line #}
hvcc-0.16.0/hvcc/generators/c2unity/templates/src/PluginList.h 0000644 0000000 0000000 00000000064 00000000000 021131 0 ustar 00 DECLARE_EFFECT("{{name}}", Hv_{{name}}_UnityPlugin)
hvcc-0.16.0/hvcc/generators/c2unity/templates/src/{{name}}.cpp 0000644 0000000 0000000 00000010101 00000000000 021263 0 ustar 00 {{copyright}}
// http://docs.unity3d.com/500/Documentation/Manual/AudioMixerNativeAudioPlugin.html
#include "AudioPluginUtil.h"
#include "Heavy_{{name}}.hpp"
namespace Hv_{{name}}_UnityPlugin {
enum Param {
{%- for k, v in in_params %}
P_{{k|upper}},
{%- endfor %}
P_NUM_HV_PARAMS_
};
struct EffectData {
struct Data {
float p[{{in_params|length if in_params|length > 0 else 1}}];
HeavyContextInterface *context;
} data;
};
int InternalRegisterEffectDefinition(UnityAudioEffectDefinition& definition) {
int numparams = P_NUM_HV_PARAMS_;
definition.paramdefs = new UnityAudioParameterDefinition[numparams];
// channels will be set to 0 if numInputChannels > 0 else it will be set to numOutputChannels
definition.channels = {{0 if num_input_channels else num_output_channels}};
{%- for k, v in in_params %}
{%- if v.display|length > 15 %}
#if HV_WIN
// Unity Windows doesn't seem to like parameter names that are longer than 15 chars
AudioPluginUtil::RegisterParameter(definition, "{{v.display|cap(15)}}", "", {{v.attributes.min}}f, {{v.attributes.max}}f, {{v.attributes.default}}f, 1.0f, 1.0f, P_{{k|upper}}, "{{v.display}}");
#else
AudioPluginUtil::RegisterParameter(definition, "{{v.display}}", "", {{v.attributes.min}}f, {{v.attributes.max}}f, {{v.attributes.default}}f, 1.0f, 1.0f, P_{{k|upper}}, "{{v.display}}");
#endif
{%- else %}
AudioPluginUtil::RegisterParameter(definition, "{{v.display}}", "", {{v.attributes.min}}f, {{v.attributes.max}}f, {{v.attributes.default}}f, 1.0f, 1.0f, P_{{k|upper}}, "{{v.display}}");
{%- endif %}
{%- endfor %}
return numparams;
}
UNITY_AUDIODSP_RESULT UNITY_AUDIODSP_CALLBACK CreateCallback(UnityAudioEffectState* state) {
EffectData* effectdata = new EffectData;
effectdata->data.context = new Heavy_{{name}}((double) state->samplerate, {{pool_sizes_kb.internal}}, {{pool_sizes_kb.inputQueue}}, {{pool_sizes_kb.outputQueue}});
state->effectdata = effectdata;
AudioPluginUtil::InitParametersFromDefinitions(InternalRegisterEffectDefinition, effectdata->data.p);
return UNITY_AUDIODSP_OK;
}
UNITY_AUDIODSP_RESULT UNITY_AUDIODSP_CALLBACK ReleaseCallback(UnityAudioEffectState* state) {
EffectData::Data* data = &state->GetEffectData()->data;
delete data->context;
delete data;
return UNITY_AUDIODSP_OK;
}
UNITY_AUDIODSP_RESULT UNITY_AUDIODSP_CALLBACK SetFloatParameterCallback(
UnityAudioEffectState* state, int index, float value) {
EffectData::Data *data = &state->GetEffectData()->data;
switch (index) {
{%- for k, v in in_params %}
case P_{{k|upper}}: data->context->sendFloatToReceiver(Heavy_{{name}}::Parameter::In::{{k|upper}}, value); break;
{%- endfor %}
default: return UNITY_AUDIODSP_ERR_UNSUPPORTED;
}
data->p[index] = value;
return UNITY_AUDIODSP_OK;
}
UNITY_AUDIODSP_RESULT UNITY_AUDIODSP_CALLBACK GetFloatParameterCallback(
UnityAudioEffectState* state, int index, float* value, char *valuestr) {
EffectData::Data* data = &state->GetEffectData()->data;
if (index < 0 || index >= P_NUM_HV_PARAMS_) return UNITY_AUDIODSP_ERR_UNSUPPORTED;
if (value != NULL) *value = data->p[index];
if (valuestr != NULL) valuestr[0] = 0;
return UNITY_AUDIODSP_OK;
}
int UNITY_AUDIODSP_CALLBACK GetFloatBufferCallback(UnityAudioEffectState* state,
const char* name, float* buffer, int numsamples) {
return UNITY_AUDIODSP_OK;
}
UNITY_AUDIODSP_RESULT UNITY_AUDIODSP_CALLBACK ProcessCallback(
UnityAudioEffectState* state, float* inbuffer, float* outbuffer,
unsigned int length, int inchannels, int outchannels) {
EffectData::Data* data = &state->GetEffectData()->data;
if (state->flags & UnityAudioEffectStateFlags_IsPaused) return UNITY_AUDIODSP_OK;
hv_assert(inchannels == data->context->getNumInputChannels());
hv_assert(outchannels == data->context->getNumOutputChannels());
data->context->processInlineInterleaved(inbuffer, outbuffer, length);
return UNITY_AUDIODSP_OK;
}
}
hvcc-0.16.0/hvcc/generators/c2wwise/__init__.py 0000644 0000000 0000000 00000000000 00000000000 016166 0 ustar 00 hvcc-0.16.0/hvcc/generators/c2wwise/c2wwise.py 0000644 0000000 0000000 00000014141 00000000000 016025 0 ustar 00 # Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2021-2026 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 shutil
import time
import jinja2
from typing import Optional
from pathlib import Path
from ..copyright import copyright_manager
from ..filters import filter_plugin_id
from hvcc.interpreters.pd2hv.NotificationEnum import NotificationEnum
from hvcc.types.compiler import Generator, CompilerResp, CompilerNotif, CompilerMsg, ExternInfo
from hvcc.types.meta import Meta
class c2wwise(Generator):
"""Generates a plugin wrapper for Audiokinetic's Wwise game audio middleware
platform.
"""
@classmethod
def compile(
cls,
c_src_dir: Path,
out_dir: Path,
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()
in_parameter_list = externs.parameters.inParam
out_parameter_list = externs.parameters.outParam
event_list = externs.events.inEvent
out_event_list = externs.events.outEvent
table_list = externs.tables
patch_name = patch_name or "heavy"
copyright_c = copyright_manager.get_copyright_for_c(copyright)
copyright_xml = copyright_manager.get_copyright_for_xml(copyright)
templates_dir = Path(Path(__file__).parent, "templates")
is_source_plugin = num_input_channels == 0
plugin_type = "Source" if is_source_plugin else "FX"
plugin_id = filter_plugin_id(patch_name)
out_dir = Path(out_dir, "wwise")
if not out_dir.exists():
out_dir.mkdir()
env = jinja2.Environment()
env.loader = jinja2.FileSystemLoader(
encoding="utf-8-sig",
searchpath=[templates_dir])
try:
channel_config = None
if num_output_channels == 0:
# Workaround to support patches that don't have any outputs,
# e.g. that only ever set RTPCs or post events
channel_config = "AK_SPEAKER_SETUP_MONO"
elif num_output_channels == 1:
channel_config = "AK_SPEAKER_SETUP_MONO"
elif num_output_channels == 2:
channel_config = "AK_SPEAKER_SETUP_STEREO"
elif num_output_channels == 6:
channel_config = "AK_SPEAKER_SETUP_5POINT1"
elif num_output_channels == 8:
channel_config = "AK_SPEAKER_SETUP_7POINT1"
elif num_output_channels == 12:
channel_config = "AK_SPEAKER_SETUP_DOLBY_7_1_4"
else:
raise Exception("Wwise plugins support only mono, stereo, 5.1, 7.1, "
"and 7.1.4 (Atmos) configurations, number of [dac~] channels should be appropriate")
if plugin_type == "FX":
if num_input_channels != num_output_channels:
raise Exception("Wwise FX plugins require the same input/output channel"
"configuration (i.e. [adc~ 1] -> [dac~ 1]).")
patch_src_dir = Path(out_dir, "SoundEnginePlugin", "Heavy")
if patch_src_dir.exists():
shutil.rmtree(patch_src_dir)
shutil.copytree(c_src_dir, patch_src_dir)
src_ext_list = ["h", "hpp", "c", "cpp", "xml", "def", "rc", "lua", "json"]
for f in env.list_templates(extensions=src_ext_list):
file = Path(f)
file_dir = Path(out_dir, file.parent)
file_name = file.name.replace("{{name}}", patch_name)
file_name = file_name.replace("{{plugin_type}}", plugin_type)
file_path = Path(file_dir, file_name)
if not file_path.parent.exists():
file_path.parent.mkdir(parents=True)
with open(file_path, "w") as g:
g.write(env.get_template(f).render(
name=patch_name,
parameters=in_parameter_list,
out_params=out_parameter_list,
out_events=out_event_list,
events=event_list,
tables=table_list,
pool_sizes_kb=externs.memoryPoolSizesKb,
is_source=is_source_plugin,
num_input_channels=num_input_channels,
num_output_channels=num_output_channels,
channel_config=channel_config,
plugin_type=plugin_type,
plugin_id=plugin_id,
copyright=copyright_xml if file_name.endswith(".xml") else copyright_c))
return CompilerResp(
stage="c2wwise",
in_dir=c_src_dir,
out_dir=out_dir,
compile_time=time.time() - tick
)
except Exception as e:
return CompilerResp(
stage="c2wwise",
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
)
hvcc-0.16.0/hvcc/generators/c2wwise/templates/Includes/libnyquist/AudioDecoder.h 0000644 0000000 0000000 00000005573 00000000000 024550 0 ustar 00 /*
Copyright (c) 2015, Dimitri Diakopoulos All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef AUDIO_DECODER_H
#define AUDIO_DECODER_H
#include "Common.h"
#include
#include