├── .gitignore
├── LICENSE
├── Makefile
├── README.md
├── images
├── Chords.png
├── Comb.png
├── ExpSeg.png
├── Expander.png
├── Filter.png
├── FormulaOne.png
├── GateSequencer.png
├── LineSeg.png
├── Osc.png
├── PDFilterSweep.png
├── PulseWave.png
├── RndH.png
├── Sequencer.png
├── StereoDelay.png
├── Wavefolder.png
├── Waveshaping2.png
└── waveshaping1.png
├── plugin.json
├── presets
└── FormulaOne
│ ├── Chords.vcvm
│ ├── Comb.vcvm
│ ├── GateSequencer.vcvm
│ ├── LineSeg.vcvm
│ ├── PDFilterSweep.vcvm
│ ├── PhaseOscillator.vcvm
│ ├── RndH.vcvm
│ ├── Sequencer.vcvm
│ ├── SimpleOscillator.vcvm
│ ├── StereoDelay.vcvm
│ └── TRCFilter.vcvm
├── res
├── FormulaOne.svg
├── FormulaOneEdit.svg
├── FreeMonoBold.ttf
├── SmallPort.svg
├── TrimpotWhite.svg
└── TrimpotWhite9mm.svg
└── src
├── FormulaOne.cpp
├── FormulaOne.hpp
├── FormulaOneEdit.cpp
├── exprtk.hpp
├── functions.hpp
├── plugin.cpp
├── plugin.hpp
├── rnd.cpp
├── rnd.h
├── textfield.cpp
└── textfield.hpp
/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /dist
3 | /plugin.so
4 | /plugin.dylib
5 | /plugin.dll
6 | res/scales.json
7 | res/addsynth.json
8 | res/genrscale.js
9 | .DS_Store
10 | .idea
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # If RACK_DIR is not defined when calling the Makefile, default to two directories above
2 | RACK_DIR ?= ../..
3 |
4 | # FLAGS will be passed to both the C and C++ compiler
5 | FLAGS +=
6 | include $(RACK_DIR)/arch.mk
7 | ifdef ARCH_WIN
8 | FLAGS += -Wa,-mbig-obj
9 | endif
10 | CFLAGS +=
11 | CXXFLAGS +=
12 |
13 | # Careful about linking to shared libraries, since you can't assume much about the user's environment and library search path.
14 | # Static libraries are fine, but they should be added to this plugin's build system.
15 | LDFLAGS +=
16 |
17 | # Add .cpp files to the build
18 | SOURCES += $(wildcard src/*.cpp)
19 |
20 | # Add files to the ZIP package when running `make dist`
21 | # The compiled plugin and "plugin.json" are automatically added.
22 | DISTRIBUTABLES += res
23 | DISTRIBUTABLES += $(wildcard LICENSE*)
24 | DISTRIBUTABLES += $(wildcard presets)
25 |
26 | # Include the Rack plugin Makefile framework
27 | include $(RACK_DIR)/plugin.mk
28 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # Formula One
3 |
4 | A script/formula evaluation module for VCVRack. This module can do almost anything, and it is quite fast.
5 |
6 | 
7 |
8 | It is based on the [exprtk expression library](http://www.partow.net/programming/exprtk/index.html)
9 | which has very good [benchmarks](https://github.com/ArashPartow/math-parser-benchmark-project#the-rounds).
10 |
11 | It is not intended for replacing native modules because they still will be faster i.e. consume less CPU
12 | and provide more usability. It is for experimenting,learning and making special things.
13 |
14 | ## Examples and Usecases
15 |
16 | The examples are available as factory presets.
17 |
18 |
19 |
20 |
21 | - [Simple polyphonic oscillator](#simple-polyphonic-oscillator)
22 | - [Step by Step](#step-by-step)
23 | - [External Phase Controlled Oscillator](#external-phase-controlled-oscillator)
24 | - [Wave Folder](#wave-folder)
25 | - [Simple Polyphonic Filter](#simple-polyphonic-filter)
26 | - [Step by Step](#step-by-step-1)
27 | - [Polyphonic Comb Filter (Chorus,Flanger or whatever)](#polyphonic-comb-filter-chorusflanger-or-whatever)
28 | - [Step by Step](#step-by-step-2)
29 | - [Stereo Delay](#stereo-delay)
30 | - [Polyphonic Random and Hold](#polyphonic-random-and-hold)
31 | - [Simple Sequencer](#simple-sequencer)
32 | - [Chord Sequencer](#chord-sequencer)
33 | - [Gate Sequencer](#gate-sequencer)
34 | - [A Line Segment Envelope Generator](#a-line-segment-envelope-generator)
35 | - [Waveshaping](#waveshaping)
36 | - [Wavshaping with exp segemnts](#wavshaping-with-exp-segemnts)
37 | - [CZ-Series PD Filter sweep](#cz-series-pd-filter-sweep)
38 |
39 |
40 |
41 | ### Simple polyphonic oscillator
42 |
43 | 
44 |
45 | ```
46 | var freq := 261.626 * pow(2,w);
47 | var p:= bufr(chn);
48 | var o:=a*4*(
49 | sin(2*pi*p)+
50 | sin(6*pi*p)/3+
51 | sin(10*pi*p)/5+
52 | sin(14*pi*p)/7
53 | );
54 | var phs:= p+stim*freq;
55 | phs-=trunc(phs);
56 | bufw(chn,phs);
57 | var out:=o;
58 | ```
59 | #### Step by Step
60 | `var freq := 261.626 * pow(2,w);` Declare a variable freq and assign
61 | the computed frequency from the input `w` (V/Oct).
62 |
63 | `var p:= bufr(chn);` Read the current phase for `chn` from the buffer
64 | into the variable p. The script is evaluated for every
65 | channel which is detected in the `w` input and provides the
66 | value of the input `w` for the current channel via the global variable `w`.
67 | The current channel number is set in the global variable `chn`.
68 |
69 | `var o:=a*4*(sin(2*pi*p)+sin(6*pi*p)/3+sin(10*pi*p)/5+sin(14*pi*p)/7);`
70 | compute the output sample from the current phase. the global variable `a` holds the current
71 | value of the knob `a`;
72 |
73 | `var phs:= p+stim*freq; phs-=trunc(phs);` compute the next phase using the global variable
74 | `stim` which holds the current sample time (`1/sampleRate`).
75 |
76 | `bufw(chn,phs);` write the new phase into the buffer.
77 |
78 | `var out:=o;` the last expression value in the script is returned to the module and written
79 | into the output `out`.
80 |
81 | ### External Phase Controlled Oscillator
82 | 
83 |
84 | ```Tcl
85 | a*5*(
86 | sin(2*pi*t)+
87 | sin(6*pi*t)/3+
88 | sin(10*pi*t)/5+
89 | sin(14*pi*t)/7
90 | )
91 | ```
92 |
93 | This example does the same as the one above but uses the special input `t` as phase.
94 | The values of `t` are normalized from -5V/5V to 0V/1V.
95 |
96 | ### Wave Folder
97 | 
98 |
99 | `sin((c*5)*t*2*pi+b*5)*a*5`
100 |
101 | The same principle can be used to make a wave folder.
102 | The knob `c` controls the depth and the knob `b` controls the offset.
103 |
104 | ### Simple Polyphonic Filter
105 |
106 | This example shows how to implement a simple LP filter. The algorithm is directly taken from the VCV Rack
107 | [source](https://github.com/VCVRack/Rack/blob/v2/include/dsp/filter.hpp)
108 |
109 | 
110 |
111 | ```Tcl
112 | var f := clamp(2^(c*8),0.001,0.5);
113 | var fc := 2/f;
114 | var out:= (x + bufr(chn) - buf1r(chn) * (1 - fc)) /(1 + fc);
115 | bufw(chn,x);
116 | buf1w(chn,out);
117 | dcb(chn,out);
118 | ```
119 | #### Step by Step
120 |
121 | `var f := clamp(2^(c*8),0.001,0.5); var fc := 2/f;` Compute the cutoff frequency from the knob `c`.
122 |
123 | ```
124 | var out:= (x + bufr(chn) - buf1r(chn) * (1 - fc)) /(1 + fc);
125 | bufw(chn,x);
126 | buf1w(chn,out);
127 | ```
128 | Compute the next filter sample. The two needed state variables are stored per channel in the
129 | buffer (0) and buffer 1. There are 4 buffers of size 4096 available which can be read
130 | via bufr,buf1r,buf2r,buf3r and written via bufw,buf1w,buf2w,buf3w
131 |
132 | `dcb(chn,out);` output the dc blocked sample.
133 |
134 | ### Polyphonic Comb Filter (Chorus,Flanger or whatever)
135 |
136 | 
137 | ```Tcl
138 | var delay_ms:= 2.1;
139 | var len:= delay_ms/1000*sr;
140 | rblen(chn,len);
141 | var c0 := clamp((c+1)/2+y/10*a,0,0.99);
142 | var x0 := rbget(chn,c0*len);
143 | var nx := x/2 + x0 * d;
144 | rbpush(chn,nx);
145 | var out:=x0+nx;
146 | var x1 := rbget(chn,c0*len/2);
147 | out1:=dcb2(chn,x1+nx);
148 | dcb(chn,out);
149 | ```
150 | This example shows the use of the provided ring buffers.
151 |
152 | #### Step by Step
153 |
154 | `var delay_ms:= 2.1; var len:= delay_ms/1000*sr;` Define the delay length and compute
155 | the buffer size in samples using the global variabel `sr` (sample rate).
156 |
157 | `rblen(chn,len);` set the ring buffer length for each channel. There are 16 ring buffers available with
158 | maximum length of 48000.
159 |
160 | `var c0 := clamp((c+1)/2+y/10*a,0,0.99);`. Compute the relative read position of the buffer
161 | using the knob value c and the input `y` attenuated with the knob value `a`.
162 |
163 | `var x0 := rbget(chn,c0*len);` read the buffer value at the computed index.
164 |
165 | `var nx := x/2 + x0 * d; rbpush(chn,nx);` mix with the input,
166 | add feedback attenuated with knob `d`and write it into the ring buffer.
167 |
168 | `var out:=x0+nx`; store the output in the variable `out`.
169 |
170 | `var x1 := rbget(chn,c0*len/2);` read a second value from the buffer for a stereo effect.
171 |
172 | `out1:=dcb2(chn,x1+nx);` output the 'right' stereo channel in output `out1` via
173 | setting the global variable `out1`. There are two dc blockers available per channel.
174 |
175 |
176 | `dcb(chn,out)`; output the left channel in `out`. (The value of the last expression is
177 | always written into the output `out`).
178 |
179 | ### Stereo Delay
180 |
181 | 
182 |
183 | ```Tcl
184 | var delayR:= 0.375;
185 | var delayL:= 0.750;
186 | var lenR:= delayR*sr;
187 | var lenL:= delayL*sr;
188 | rblen(0,lenL);
189 | rblen(1,lenR);
190 |
191 | var xL := rbget(0,0);
192 | var xR := rbget(1,0);
193 | rbpush(0,x+a*xL);
194 | rbpush(1,x+a*xR);
195 | out1:=dcb(0,(xR*b)+(x*(1-b)));
196 | dcb(1,xL*b+x*(1-b));
197 | ```
198 |
199 | Similar to the last example. The knob `a` controls the feedback and the knob `b` dry/wet.
200 | The position in the call rbget is relative to the last write position + 1. With position 0 it returns the
201 | sample which was written length samples before. `rbget(0,-1)` would return the last written sample.
202 |
203 |
204 | ### Polyphonic Random and Hold
205 |
206 | 
207 |
208 | ```Tcl
209 | if(st(chn,z)>0) {
210 | bufw(chn,rnd());
211 | }
212 | var out := bufr(chn)*10*a;
213 | ```
214 | There are 4 Schmitt Triggers available per channel (st,st1,st2,st3).
215 | The buffer is used to hold the value until the next trigger arrives.
216 | NB the polyphony is determined by the input channels in the following way:
217 | If the input t is connected the channels of t is used, otherwise the maximum of the
218 | channels of the inputs w,x,y,z. The image above shows the case with one channel.
219 |
220 | ### Simple Sequencer
221 |
222 | 
223 |
224 | ```Tcl
225 | var seq[8] := { 0,3/12,5/12,
226 | 7/12,10/12,7/12,
227 | 5/12,3/12};
228 |
229 | if(st(0,z)>0) {
230 | v1:=v1+1;
231 | if(v1>=8) v1:=0;
232 | }
233 | if(st1(0,y)>0) v1:=0;
234 | var out := seq[v1];
235 | ```
236 |
237 | This example shows the use of one of the 8 global variables v1-v8.
238 | `v1` is used as the current position of the sequence which is advanced on arrival
239 | on a trigger on input `z`. The input `y` is used for resetting the sequence.
240 |
241 | ### Chord Sequencer
242 |
243 | 
244 |
245 | ```Tcl
246 | if(v3==0) {
247 | var n := -1;
248 | for(var i:=0;i<36;i+=1) {
249 | bufw(i,n);
250 | n+=1/12;
251 | };
252 | v3:=1;
253 | };
254 | var chord[16]:={9,12,16,19, 9,12,14,18,
255 | 7,11,14,18, 7,11,12,16};
256 | if(st(0,z)>0) {v1+=1; if(v1>=4) v1:=0;}
257 | var out: = bufr(chord[chn+v1*4]);
258 |
259 | ```
260 |
261 | All variables and buffers are cleared if the script is compiled due to a change.
262 | Here the variable v3 is used to fill a buffer with note values (V/Oct) only once.
263 | (otherwise it costs much CPU).
264 |
265 | Similar to the RndH example, a trigger on the input `z` advances the sequence. The notes of a single
266 | chord are distributed in 4 channels.
267 |
268 | ### Gate Sequencer
269 |
270 | 
271 |
272 | ```Tcl
273 | var data[16] := {
274 | 1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,1};
275 | var dur := 10; var gate:=0;
276 | if(st(chn,z)>0) {
277 | if(data[v1]>0) {
278 | bufw(chn,dur);
279 | } else {
280 | bufw(chn,0);
281 | }
282 | };
283 | if(bufr(chn)>0) {
284 | gate:=10; bufw(chn,bufr(chn)-1);
285 | };
286 | if(st1(0,z)>0) {
287 | v1+=1;
288 | if(v1>=16) v1:=0;
289 | };
290 | var out:= gate;
291 | ```
292 | This example shows how to make pulses. The duration is defined in samples.
293 | When a trigger arrives and the current position (v1) has a one, a state variable
294 | is set to the duration and decremented on each run until it is zero.
295 | While greater than zero the output is set to 10V.
296 |
297 | ### A Line Segment Envelope Generator
298 |
299 | 
300 |
301 | ```Tcl
302 | var vals[6]:={0,5,3,2,2,0};
303 | var durs[5]:={0.3,0.2,0.5,0.3,0.2};
304 | if(st(0,w)>0) { v1:=0; v2:=0; v3:=0; }
305 | var end:=0;
306 | if(v1>=v2+durs[v3]) { // advance
307 | if(v3>=5) end:=1;
308 | else { v2+=durs[v3]; v3+=1; }
309 | };
310 | var o;var pct;
311 | if(end==1) {
312 | o:=vals[5];
313 | } else {
314 | pct:= (v1-v2)/durs[v3];
315 | o:=vals[v3]+(vals[v3+1]-vals[v3])*pct;
316 | };
317 | v1+=stim;
318 | o;
319 | ```
320 | Outputs line segments according to the values and duration arrays. It is triggered via the
321 | `w` input.
322 | As this is a common usecase the example can be simplified as so:
323 | ```Tcl
324 | if(st(0,w)>0) { v1:=0; }
325 | var o:=lseg(v1,0,0.3,5,0.2,3,0.5,2,0.3,
326 | 2,0.2,0);
327 | v1+=stim;
328 | o;
329 | ```
330 | The function `lseg` takes as shown the phase as first parameter then alternating value
331 | and duration. If the last value is missing the first value is used instead.
332 |
333 | Additionally there is a function eseg providing exponential segments
334 |
335 | 
336 |
337 | ```Tcl
338 | var o:=eseg(v1, 0,0.3,-5, 5,0.5,-4,
339 | 2,0.3,0, 2,0.5,-3, 0);
340 | if(st(0,w)>0) v1:=0;
341 |
342 | v1+=stim;
343 | o;
344 | ```
345 |
346 | The `eseg` function takes three alternating parameters value, duration, bending.
347 | The sign of the bending parameter determines if the curve
348 | is fast decaying/raising (<0) or slow decaying/raising (>0).
349 |
350 |
351 | ### Waveshaping
352 | 
353 |
354 | This example shows the definition and use of a user defined function.
355 | ```Tcl
356 | function sign(f) {
357 | f<0?-1:(f>0?1:0);
358 | }
359 | var p:= scl1(-a,0.01,10);
360 | // waveshaping function
361 | sign(x)*(1-p/(abs(x)+p))*5;
362 | ```
363 |
364 | Functions must be defined always on top, before the normal code starts.
365 | The return value of a function is always the value of the last expression.
366 |
367 |
368 | There are some convenient functions provided for scaling:
369 |
370 | `scl(input,minInput,maxInput,minOutput,maxOutput)`
371 |
372 | this function scales the input expected in the range minInput,maxInput to the
373 | target range minOutput,maxOutput.
374 |
375 | `scl1` as shown in the example is a shortcut for
376 |
377 | `scl(input,-1,1,minOutput,maxOutput)`
378 |
379 | -- suited for the knob inputs.
380 |
381 | #### Wavshaping with exp segemnts
382 |
383 | 
384 |
385 | ```Tcl
386 | eseg(t,-1,0.5,a*10,1,0.5,b*10,-1)*5
387 | ```
388 | This example shows the use of the `eseg` function (see above) for waveshaping.
389 | Play around with knob a and b.
390 |
391 | ### CZ-Series PD Filter sweep
392 |
393 | 
394 |
395 | ```Tcl
396 | var freq := 261.626 * pow(2,w);
397 | var p:= bufr(chn);
398 | var phs:= p+stim*freq;
399 | if(phs>=1) phs:=0;
400 | bufw(chn,phs);
401 | var inv:=1-p;
402 | out1:=p;
403 | var freq2 := 261.626 * pow(2,a*5);
404 | var p2:= buf2r(chn);
405 | var o:= sin(2*pi*p2)*inv;
406 | var phs2:= p2+stim*freq2;
407 | phs2-=trunc(phs2);
408 | if(phs==0) phs2:=0;
409 | buf2w(chn,phs2);
410 |
411 | var out:=o*5;
412 | ```
413 |
414 | This script implements the
415 | [Resonant Filter Simulation](https://en.wikipedia.org/wiki/Phase_distortion_synthesis)
416 | from the Casio CZ Series. The knob `a` controls the resonant frequency.
417 |
418 |
--------------------------------------------------------------------------------
/images/Chords.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docb/dbRackFormulaOne/bd8afc4813e49af5032b5f44303fac309f859fdb/images/Chords.png
--------------------------------------------------------------------------------
/images/Comb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docb/dbRackFormulaOne/bd8afc4813e49af5032b5f44303fac309f859fdb/images/Comb.png
--------------------------------------------------------------------------------
/images/ExpSeg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docb/dbRackFormulaOne/bd8afc4813e49af5032b5f44303fac309f859fdb/images/ExpSeg.png
--------------------------------------------------------------------------------
/images/Expander.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docb/dbRackFormulaOne/bd8afc4813e49af5032b5f44303fac309f859fdb/images/Expander.png
--------------------------------------------------------------------------------
/images/Filter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docb/dbRackFormulaOne/bd8afc4813e49af5032b5f44303fac309f859fdb/images/Filter.png
--------------------------------------------------------------------------------
/images/FormulaOne.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docb/dbRackFormulaOne/bd8afc4813e49af5032b5f44303fac309f859fdb/images/FormulaOne.png
--------------------------------------------------------------------------------
/images/GateSequencer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docb/dbRackFormulaOne/bd8afc4813e49af5032b5f44303fac309f859fdb/images/GateSequencer.png
--------------------------------------------------------------------------------
/images/LineSeg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docb/dbRackFormulaOne/bd8afc4813e49af5032b5f44303fac309f859fdb/images/LineSeg.png
--------------------------------------------------------------------------------
/images/Osc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docb/dbRackFormulaOne/bd8afc4813e49af5032b5f44303fac309f859fdb/images/Osc.png
--------------------------------------------------------------------------------
/images/PDFilterSweep.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docb/dbRackFormulaOne/bd8afc4813e49af5032b5f44303fac309f859fdb/images/PDFilterSweep.png
--------------------------------------------------------------------------------
/images/PulseWave.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docb/dbRackFormulaOne/bd8afc4813e49af5032b5f44303fac309f859fdb/images/PulseWave.png
--------------------------------------------------------------------------------
/images/RndH.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docb/dbRackFormulaOne/bd8afc4813e49af5032b5f44303fac309f859fdb/images/RndH.png
--------------------------------------------------------------------------------
/images/Sequencer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docb/dbRackFormulaOne/bd8afc4813e49af5032b5f44303fac309f859fdb/images/Sequencer.png
--------------------------------------------------------------------------------
/images/StereoDelay.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docb/dbRackFormulaOne/bd8afc4813e49af5032b5f44303fac309f859fdb/images/StereoDelay.png
--------------------------------------------------------------------------------
/images/Wavefolder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docb/dbRackFormulaOne/bd8afc4813e49af5032b5f44303fac309f859fdb/images/Wavefolder.png
--------------------------------------------------------------------------------
/images/Waveshaping2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docb/dbRackFormulaOne/bd8afc4813e49af5032b5f44303fac309f859fdb/images/Waveshaping2.png
--------------------------------------------------------------------------------
/images/waveshaping1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docb/dbRackFormulaOne/bd8afc4813e49af5032b5f44303fac309f859fdb/images/waveshaping1.png
--------------------------------------------------------------------------------
/plugin.json:
--------------------------------------------------------------------------------
1 | {
2 | "slug": "dbRackFormulaOne",
3 | "name": "dbRackFormulaOne",
4 | "version": "2.0.2",
5 | "license": "GPL-3.0-or-later",
6 | "brand": "docB",
7 | "author": "docB",
8 | "authorEmail": "",
9 | "authorUrl": "https://docb.io",
10 | "pluginUrl": "https://github.com/docb/dbRackFormulaOne",
11 | "manualUrl": "https://github.com/docb/dbRackFormulaOne",
12 | "sourceUrl": "https://github.com/docb/dbRackFormulaOne",
13 | "donateUrl": "https://www.paypal.com/paypalme/docb222",
14 | "changelogUrl": "",
15 | "modules": [
16 | {
17 | "slug": "FormulaOne",
18 | "name": "Formula One",
19 | "description": "A fast script evaluator",
20 | "tags": ["Polyphonic"]
21 | },
22 | {
23 | "slug": "FormulaOneEdit",
24 | "name": "FormulaOneEdit",
25 | "description": "Expander for editing scripts",
26 | "tags": ["Expander"]
27 | }
28 | ]
29 | }
30 |
--------------------------------------------------------------------------------
/presets/FormulaOne/Chords.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "dbRackFormulaOne",
3 | "model": "FormulaOne",
4 | "version": "2.0.0",
5 | "params": [
6 | {
7 | "value": 0.67250025272369385,
8 | "id": 0
9 | },
10 | {
11 | "value": 0.62250030040740967,
12 | "id": 1
13 | },
14 | {
15 | "value": 0.069999665021896362,
16 | "id": 2
17 | },
18 | {
19 | "value": 0.0,
20 | "id": 3
21 | }
22 | ],
23 | "data": {
24 | "formula": "if(v3==0) {\n var n := -1;\n for(var i:=0;i<36;i+=1) {\n bufw(i,n);\n n+=1/12;\n };\n v3:=1;\n};\nvar chord[16]:={9,12,16,19, 9,12,14,18,\n 7,11,14,18, 7,11,12,16};\nif(st(0,z)>0) {v1+=1; if(v1>=4) v1:=0;}\nvar out: = bufr(chord[chn+v1*4]);\n\n"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/presets/FormulaOne/Comb.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "dbRackFormulaOne",
3 | "model": "FormulaOne",
4 | "version": "2.0.0",
5 | "params": [
6 | {
7 | "value": 0.27250084280967712,
8 | "id": 0
9 | },
10 | {
11 | "value": 0.69000023603439331,
12 | "id": 1
13 | },
14 | {
15 | "value": 0.075000613927841187,
16 | "id": 2
17 | },
18 | {
19 | "value": 0.49749964475631714,
20 | "id": 3
21 | },
22 | {
23 | "value": 0.0,
24 | "id": 4
25 | }
26 | ],
27 | "data": {
28 | "formula": "var delay_ms:= 2.1;\nvar len:= delay_ms/1000*sr;\nrblen(chn,len);\nvar c0 := clamp((c+1)/2+y/10*a,0,0.99);\nvar x0 := rbget(chn,c0*len); \nvar nx := x/2 + x0 * d;\nrbpush(chn,nx);\nvar out:=x0+nx;\nvar x1 := rbget(chn,c0*len/2);\nout1:=dcb2(chn,x1+nx);\ndcb(chn,out);\n\n\n"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/presets/FormulaOne/GateSequencer.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "dbRackFormulaOne",
3 | "model": "FormulaOne",
4 | "version": "2.0.0",
5 | "params": [
6 | {
7 | "value": 0.67250025272369385,
8 | "id": 0
9 | },
10 | {
11 | "value": 0.62250030040740967,
12 | "id": 1
13 | },
14 | {
15 | "value": 0.069999665021896362,
16 | "id": 2
17 | },
18 | {
19 | "value": 0.0,
20 | "id": 3
21 | },
22 | {
23 | "value": 0.0,
24 | "id": 4
25 | }
26 | ],
27 | "data": {
28 | "formula": "var data[16] := { \n1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,1};\nvar dur := 10; var gate:=0;\nif(st(chn,z)>0) {\n if(data[v1]>0) {\n bufw(chn,dur);\n } else {\n bufw(chn,0);\n } \n};\nif(bufr(chn)>0) { \n gate:=10; bufw(chn,bufr(chn)-1);\n};\nif(st1(0,z)>0) {\n v1+=1;\n if(v1>=16) v1:=0;\n};\nvar out:= gate;\n"
29 | }
30 | }
--------------------------------------------------------------------------------
/presets/FormulaOne/LineSeg.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "dbRackFormulaOne",
3 | "model": "FormulaOne",
4 | "version": "2.0.0",
5 | "params": [
6 | {
7 | "value": 0.67250025272369385,
8 | "id": 0
9 | },
10 | {
11 | "value": 0.62250030040740967,
12 | "id": 1
13 | },
14 | {
15 | "value": 0.069999665021896362,
16 | "id": 2
17 | },
18 | {
19 | "value": 0.0,
20 | "id": 3
21 | },
22 | {
23 | "value": 0.0,
24 | "id": 4
25 | }
26 | ],
27 | "data": {
28 | "formula": "var vals[6]:={0,5,3,2,2,0};\nvar durs[5]:={0.3,0.2,0.5,0.3,0.2};\nif(st(0,w)>0) { v1:=0; v2:=0; v3:=0; }\nvar end:=0;\nif(v1>=v2+durs[v3]) {\n if(v3>=5) end:=1;\n else { v2+=durs[v3]; v3+=1; }\n};\nvar o;var pct;\nif(end==1) {\n o:=vals[5]; \n} else {\n pct:= (v1-v2)/durs[v3];\n o:=vals[v3]+(vals[v3+1]-vals[v3])*pct; \n};\nv1+=stim;\no;\n"
29 | }
30 | }
--------------------------------------------------------------------------------
/presets/FormulaOne/PDFilterSweep.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "dbRackFormulaOne",
3 | "model": "FormulaOne",
4 | "version": "2.0.0",
5 | "params": [
6 | {
7 | "value": 0.12999996542930603,
8 | "id": 0
9 | },
10 | {
11 | "value": -1.0,
12 | "id": 1
13 | },
14 | {
15 | "value": -1.0,
16 | "id": 2
17 | },
18 | {
19 | "value": -1.0,
20 | "id": 3
21 | },
22 | {
23 | "value": 0.0,
24 | "id": 4
25 | }
26 | ],
27 | "data": {
28 | "formula": "var freq := 261.626 * pow(2,w);\nvar p:= bufr(chn);\nvar phs:= p+stim*freq;\nif(phs>=1) phs:=0;\nbufw(chn,phs);\nvar inv:=1-p;\nout1:=p;\nvar freq2 := 261.626 * pow(2,a*5);\nvar p2:= buf2r(chn);\nvar o:= sin(2*pi*p2)*inv;\nvar phs2:= p2+stim*freq2;\nphs2-=trunc(phs2);\nif(phs==0) phs2:=0;\nbuf2w(chn,phs2);\n\nvar out:=dcb(chn,o*5); \n"
29 | }
30 | }
--------------------------------------------------------------------------------
/presets/FormulaOne/PhaseOscillator.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "dbRackFormulaOne",
3 | "model": "FormulaOne",
4 | "version": "2.0.0",
5 | "params": [
6 | {
7 | "value": 0.67000055313110352,
8 | "id": 0
9 | },
10 | {
11 | "value": 0.65000027418136597,
12 | "id": 1
13 | },
14 | {
15 | "value": 0.075000613927841187,
16 | "id": 2
17 | },
18 | {
19 | "value": 0.49749964475631714,
20 | "id": 3
21 | },
22 | {
23 | "value": 0.0,
24 | "id": 4
25 | }
26 | ],
27 | "data": {
28 | "formula": "a*5*(\n sin(2*pi*t)+\n sin(6*pi*t)/3+\n sin(10*pi*t)/5+\n sin(14*pi*t)/7\n)"
29 | }
30 | }
--------------------------------------------------------------------------------
/presets/FormulaOne/RndH.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "dbRackFormulaOne",
3 | "model": "FormulaOne",
4 | "version": "2.0.0",
5 | "params": [
6 | {
7 | "value": 0.20000040531158447,
8 | "id": 0
9 | },
10 | {
11 | "value": 0.62250030040740967,
12 | "id": 1
13 | },
14 | {
15 | "value": 0.069999665021896362,
16 | "id": 2
17 | },
18 | {
19 | "value": 0.0,
20 | "id": 3
21 | }
22 | ],
23 | "data": {
24 | "formula": "if(st(chn,z)>0) {\n bufw(chn,rnd());\n}\nvar out := bufr(chn)*10*a;\n"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/presets/FormulaOne/Sequencer.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "dbRackFormulaOne",
3 | "model": "FormulaOne",
4 | "version": "2.0.0",
5 | "params": [
6 | {
7 | "value": 0.67250025272369385,
8 | "id": 0
9 | },
10 | {
11 | "value": 0.62250030040740967,
12 | "id": 1
13 | },
14 | {
15 | "value": 0.069999665021896362,
16 | "id": 2
17 | },
18 | {
19 | "value": 0.0,
20 | "id": 3
21 | }
22 | ],
23 | "data": {
24 | "formula": "var seq[8] := { 0,3/12,5/12,\n 7/12,10/12,7/12,\n 5/12,3/12};\n\nif(st(0,z)>0) { \n v1:=v1+1;\n if(v1>=8) v1:=0;\n} \nif(st1(0,y)>0) v1:=0;\nvar out := seq[v1];\n"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/presets/FormulaOne/SimpleOscillator.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "dbRackFormulaOne",
3 | "model": "FormulaOne",
4 | "version": "2.0.0",
5 | "params": [
6 | {
7 | "value": 0.70500016212463379,
8 | "id": 0
9 | },
10 | {
11 | "value": 0.62250030040740967,
12 | "id": 1
13 | },
14 | {
15 | "value": 0.069999665021896362,
16 | "id": 2
17 | },
18 | {
19 | "value": 0.0,
20 | "id": 3
21 | },
22 | {
23 | "value": 0.0,
24 | "id": 4
25 | }
26 | ],
27 | "data": {
28 | "formula": "var freq := 261.626 * pow(2,w);\nvar p:= bufr(chn);\nvar o:=a*4*(\nsin(2*pi*p)+\nsin(6*pi*p)/3+\nsin(10*pi*p)/5+\nsin(14*pi*p)/7\n);\nvar phs:= p+stim*freq;\nphs-=trunc(phs);\nbufw(chn,phs);\nvar out:=o; \n"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/presets/FormulaOne/StereoDelay.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "dbRackFormulaOne",
3 | "model": "FormulaOne",
4 | "version": "2.0.0",
5 | "params": [
6 | {
7 | "value": 0.67000055313110352,
8 | "id": 0
9 | },
10 | {
11 | "value": 0.65000027418136597,
12 | "id": 1
13 | },
14 | {
15 | "value": 0.075000613927841187,
16 | "id": 2
17 | },
18 | {
19 | "value": 0.49749964475631714,
20 | "id": 3
21 | },
22 | {
23 | "value": 0.0,
24 | "id": 4
25 | }
26 | ],
27 | "data": {
28 | "formula": "var delayR:= 0.375;\nvar delayL:= 0.750;\nvar lenR:= delayR*sr;\nvar lenL:= delayL*sr;\nrblen(0,lenL);\nrblen(1,lenR);\n\nvar xL := rbget(0,0); \nvar xR := rbget(1,0);\nrbpush(0,x+a*xL);\nrbpush(1,x+a*xR);\nout1:=dcb(0,(xR*b)+(x*(1-b)));\ndcb(1,xL*b+x*(1-b));\n\n\n\n"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/presets/FormulaOne/TRCFilter.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "dbRackFormulaOne",
3 | "model": "FormulaOne",
4 | "version": "2.0.0",
5 | "params": [
6 | {
7 | "value": 0.67250025272369385,
8 | "id": 0
9 | },
10 | {
11 | "value": 0.62250030040740967,
12 | "id": 1
13 | },
14 | {
15 | "value": -0.57749992609024048,
16 | "id": 2
17 | },
18 | {
19 | "value": 0.0,
20 | "id": 3
21 | }
22 | ],
23 | "data": {
24 | "formula": "var f := clamp(2^(c*8),0.001,0.5);\nvar fc := 2/f;\nvar out:= (x + bufr(chn) - buf1r(chn) * (1 - fc)) /(1 + fc);\nbufw(chn,x); \nbuf1w(chn,out);\n// dcblock\ndcb(chn,out);"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/res/FreeMonoBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/docb/dbRackFormulaOne/bd8afc4813e49af5032b5f44303fac309f859fdb/res/FreeMonoBold.ttf
--------------------------------------------------------------------------------
/res/SmallPort.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
196 |
--------------------------------------------------------------------------------
/res/TrimpotWhite.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
82 |
--------------------------------------------------------------------------------
/res/TrimpotWhite9mm.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
82 |
--------------------------------------------------------------------------------
/src/FormulaOne.cpp:
--------------------------------------------------------------------------------
1 | #include "plugin.hpp"
2 |
3 |
4 | #include "FormulaOne.hpp"
5 |
6 |
7 | void FormulaOne::process(const ProcessArgs &args) {
8 | if(compiled) {
9 | sr=args.sampleRate;
10 | smpTime=args.sampleTime;
11 | a=params[A_PARAM].getValue();
12 | b=params[B_PARAM].getValue();
13 | c=params[C_PARAM].getValue();
14 | d=params[D_PARAM].getValue();
15 | e=params[E_PARAM].getValue();
16 | int channels=1;
17 | if(inputs[P_INPUT].isConnected()) {
18 | channels=inputs[P_INPUT].getChannels();
19 | } else {
20 | switch(polyMode) {
21 | case X:
22 | channels=inputs[X_INPUT].getChannels();
23 | break;
24 | case Y:
25 | channels=inputs[Y_INPUT].getChannels();
26 | break;
27 | case Z:
28 | channels=inputs[Z_INPUT].getChannels();
29 | break;
30 | case W:
31 | channels=inputs[W_INPUT].getChannels();
32 | break;
33 | default:
34 | channels=inputs[W_INPUT].getChannels();
35 | channels=std::max(channels,inputs[X_INPUT].getChannels());
36 | channels=std::max(channels,inputs[Y_INPUT].getChannels());
37 | channels=std::max(channels,inputs[Z_INPUT].getChannels());
38 | break;
39 | }
40 | if(channels==0)
41 | channels=1;
42 | }
43 | for(int c=0;c=1.0f)
66 | blinkPhase-=1.0f;
67 | if(compiled) {
68 | lights[OK_LIGHT].value=0;
69 | lights[ERROR_LIGHT].value=1;
70 | } else {
71 | lights[OK_LIGHT].value=(blinkPhase<0.5f)?1.0f:0.0f;
72 | lights[ERROR_LIGHT].value=0;
73 | }
74 | }
75 |
76 | struct FormulaTextField : MTextField {
77 | FormulaOne *module;
78 |
79 | FormulaTextField() : MTextField() {
80 | //bgColor=nvgRGB(0x30,0x30,0x30);
81 | }
82 |
83 | void setModule(FormulaOne *module) {
84 | this->module=module;
85 | }
86 |
87 | void onChange(const event::Change &e) override {
88 | if(module) {
89 | module->formula=getText();
90 | module->compile();
91 | module->extDirty=true;
92 | }
93 | }
94 |
95 | void draw(const DrawArgs &args) override {
96 | if(module&&(module->dirty)) {
97 | int cursorSave=cursor;
98 | setText(module->formula);
99 | cursor=selection=cursorSave;
100 | if(cursor<0 || cursor>(int)text.size()) selection=cursor=0;
101 | module->dirty=false;
102 | module->extDirty=true;
103 | }
104 | MTextField::draw(args);
105 | }
106 | };
107 |
108 | struct FormulaOneWidget : ModuleWidget {
109 | float ms=mm2px(Vec(5.08f,0)).x;
110 | std::vector polyModeLabels={"Max","x","y","z","w"};
111 | FormulaOneWidget(FormulaOne *module) {
112 | ScrollWidget *scrollWidget;
113 | setModule(module);
114 | setPanel(createPanel(asset::plugin(pluginInstance,"res/FormulaOne.svg")));
115 |
116 | addChild(createWidget(Vec(RACK_GRID_WIDTH,0)));
117 | addChild(createWidget(Vec(box.size.x-2*RACK_GRID_WIDTH,0)));
118 | addChild(createWidget(Vec(RACK_GRID_WIDTH,RACK_GRID_HEIGHT-RACK_GRID_WIDTH)));
119 | addChild(createWidget(Vec(box.size.x-2*RACK_GRID_WIDTH,RACK_GRID_HEIGHT-RACK_GRID_WIDTH)));
120 | scrollWidget=new TextScrollWidget();
121 | scrollWidget->box.size=mm2px(Vec(85,68));
122 | scrollWidget->box.pos=mm2px(Vec(3.5f,MHEIGHT-54-68));
123 | INFO("%f",scrollWidget->box.size.y);
124 | addChild(scrollWidget);
125 | auto textField=createWidget(Vec(0,0));
126 | textField->setModule(module);
127 | textField->box.size=mm2px(Vec(200,160));
128 | textField->multiline=true;
129 | textField->scroll=scrollWidget;
130 | scrollWidget->container->addChild(textField);
131 | addChild(createLight>(mm2px(Vec(45,MHEIGHT-29)),module,FormulaOne::ERROR_LIGHT));
132 | addParam(createParam(mm2px(Vec(8.75,MHEIGHT-34.5-8.985f)),module,FormulaOne::A_PARAM));
133 | addParam(createParam(mm2px(Vec(21.5,MHEIGHT-34.5-8.985f)),module,FormulaOne::B_PARAM));
134 | addParam(createParam(mm2px(Vec(34.5,MHEIGHT-34.5-8.985f)),module,FormulaOne::C_PARAM));
135 | addParam(createParam(mm2px(Vec(47.5,MHEIGHT-34.5-8.985f)),module,FormulaOne::D_PARAM));
136 | addParam(createParam(mm2px(Vec(60.5,MHEIGHT-34.5-8.985f)),module,FormulaOne::E_PARAM));
137 | addInput(createInput(mm2px(Vec(10,MHEIGHT-10-6.27f)),module,FormulaOne::P_INPUT));
138 | addInput(createInput(mm2px(Vec(23,MHEIGHT-10-6.27f)),module,FormulaOne::W_INPUT));
139 | addInput(createInput(mm2px(Vec(36,MHEIGHT-10-6.27f)),module,FormulaOne::X_INPUT));
140 | addInput(createInput(mm2px(Vec(49,MHEIGHT-10-6.27f)),module,FormulaOne::Y_INPUT));
141 | addInput(createInput(mm2px(Vec(62,MHEIGHT-10-6.27f)),module,FormulaOne::Z_INPUT));
142 |
143 | addOutput(createOutput(mm2px(Vec(79.212,MHEIGHT-10-6.27f)),module,FormulaOne::V_OUTPUT));
144 | addOutput(createOutput(mm2px(Vec(79.212,MHEIGHT-24-6.27f)),module,FormulaOne::V1_OUTPUT));
145 | addOutput(createOutput(mm2px(Vec(79.212,MHEIGHT-38-6.27f)),module,FormulaOne::V2_OUTPUT));
146 | }
147 | void appendContextMenu(Menu *menu) override {
148 | FormulaOne *module=dynamic_cast(this->module);
149 | assert(module);
150 | menu->addChild(new MenuSeparator);
151 | auto inSelect=new LabelIntSelectItem(&module->polyMode,polyModeLabels);
152 | inSelect->text="Channels from";
153 | inSelect->rightText=polyModeLabels[module->polyMode]+" "+RIGHT_ARROW;
154 | menu->addChild(inSelect);
155 |
156 |
157 | }
158 | };
159 |
160 |
161 | Model *modelFormulaOne=createModel("FormulaOne");
--------------------------------------------------------------------------------
/src/FormulaOne.hpp:
--------------------------------------------------------------------------------
1 | //
2 | // Created by docb on 2/20/22.
3 | //
4 |
5 | #ifndef DBCAEMODULES_FASTFORMULA_HPP
6 | #define DBCAEMODULES_FASTFORMULA_HPP
7 |
8 | #include "exprtk.hpp"
9 | #include "rnd.h"
10 | #include "textfield.hpp"
11 | #include
12 | #include "functions.hpp"
13 | #include
14 |
15 | typedef exprtk::symbol_table symbol_table_t;
16 | typedef exprtk::expression expression_t;
17 | typedef exprtk::parser parser_t;
18 | typedef exprtk::parser_error::type err_t;
19 | typedef exprtk::lexer::parser_helper prsrhlpr_t;
20 | typedef exprtk::function_compositor compositor_t;
21 | typedef typename compositor_t::function function_t;
22 |
23 | template
24 | struct RB {
25 | T data[S]={};
26 | size_t pos=0;
27 | size_t len=128;
28 |
29 | RB() {
30 | clear();
31 | }
32 |
33 | void setLen(int l) {
34 | if(l>1&&l=len)
42 | pos=0;
43 | }
44 |
45 | T get(int relPos) {
46 | int idx=pos+relPos;
47 | while(idx<0)
48 | idx+=len;
49 | return data[idx%len];
50 | }
51 |
52 | void clear() {
53 | pos=0;
54 | len=1024;
55 | for(size_t k=0;k
61 | struct RBuffers {
62 | RB buffers[K];
63 |
64 | void setLen(int nr,int l) {
65 | if(nr<0||nr>=K)
66 | return;
67 | buffers[nr].setLen(l);
68 | }
69 |
70 | void push(int nr,T t) {
71 | if(nr<0||nr>=K)
72 | return;
73 | buffers[nr].push(t);
74 | }
75 |
76 | T get(int nr,int relPos) {
77 | if(nr<0||nr>=K)
78 | return T(0);
79 | return buffers[nr].get(relPos);
80 | }
81 |
82 | void clear() {
83 | for(size_t j=0;j
91 | struct Buffer {
92 | T data[S]={};
93 |
94 | Buffer() {
95 | clear();
96 | }
97 |
98 | void write(int idx,T t) {
99 | if(idx<0||idx>=S)
100 | return;
101 | data[idx]=t;
102 | }
103 |
104 | T read(int idx) {
105 | if(idx<0||idx>=S)
106 | return T(0);
107 | return data[idx];
108 | }
109 |
110 | void clear() {
111 | for(size_t k=0;k
117 | struct Random : public exprtk::ifunction {
118 | using exprtk::ifunction::operator();
119 | RND rnd;
120 |
121 | Random() : exprtk::ifunction(0) {
122 | }
123 |
124 | inline T operator()() override {
125 | return T(rnd.nextDouble());
126 | }
127 | };
128 |
129 | template
130 | struct BetaRnd : public exprtk::ifunction {
131 | using exprtk::ifunction::operator();
132 | RND rnd;
133 |
134 | BetaRnd() : exprtk::ifunction(2) {
135 | }
136 |
137 | inline T operator()(const T &a, const T &b) override {
138 | return T(rnd.nextBeta(a,b));
139 | }
140 | };
141 |
142 | template
143 | struct Weibull : public exprtk::ifunction {
144 | using exprtk::ifunction::operator();
145 | RND rnd;
146 |
147 | Weibull() : exprtk::ifunction(1) {
148 | }
149 |
150 | inline T operator()(const T &a) override {
151 | return T(rnd.nextWeibull(a));
152 | }
153 | };
154 |
155 | template
156 | struct Cauchy : public exprtk::ifunction {
157 | using exprtk::ifunction::operator();
158 | RND rnd;
159 |
160 | Cauchy() : exprtk::ifunction(1) {
161 | }
162 |
163 | inline T operator()(const T &a) override {
164 | return T(rnd.nextCauchy(a));
165 | }
166 | };
167 |
168 | template
169 | struct Coin : public exprtk::ifunction {
170 | using exprtk::ifunction::operator();
171 | RND rnd;
172 |
173 | Coin() : exprtk::ifunction(1) {
174 | }
175 |
176 | inline T operator()(const T &a) override {
177 | return T(rnd.nextCoin(a)?T(0):T(1));
178 | }
179 | };
180 |
181 | template
182 | struct STrigger : public exprtk::ifunction {
183 | using exprtk::ifunction::operator();
184 | dsp::SchmittTrigger st[16];
185 |
186 | STrigger() : exprtk::ifunction(2) {
187 | }
188 |
189 | inline T operator()(const T &nr,const T &v) override {
190 | int n=int(nr);
191 | if(n>=0&&n<16)
192 | return st[n].process(v)?1.f:0.f;
193 | return T(0);
194 | }
195 | };
196 |
197 | template
198 | struct DCBlock : public exprtk::ifunction {
199 | using exprtk::ifunction::operator();
200 | DCBlocker dcb[16];
201 |
202 | DCBlock() : exprtk::ifunction(2) {
203 | }
204 |
205 | inline T operator()(const T &nr,const T &v) override {
206 | int n=int(nr);
207 | if(n>=0&&n<16)
208 | return dcb[n].process(v);
209 | return T(0);
210 | }
211 |
212 | void reset() {
213 | for(int k=0;k<16;k++) {
214 | dcb[k].reset();
215 | }
216 | }
217 | };
218 |
219 | template
220 | struct RBSetLength : public exprtk::ifunction {
221 | using exprtk::ifunction::operator();
222 | RBuffers *buffers=nullptr;
223 |
224 | RBSetLength() : exprtk::ifunction(2) {
225 | }
226 |
227 | inline T operator()(const T &nr,const T &v) override {
228 | if(buffers)
229 | buffers->setLen(int(nr),int(v));
230 | return v;
231 | }
232 | };
233 |
234 | template
235 | struct RBPush : public exprtk::ifunction {
236 | using exprtk::ifunction::operator();
237 | RBuffers *buffers=nullptr;
238 |
239 | RBPush() : exprtk::ifunction(2) {
240 | }
241 |
242 | inline T operator()(const T &nr,const T &v) override {
243 | if(buffers) {
244 | buffers->push(int(nr),v);
245 | return v;
246 | }
247 | return T(0);
248 | }
249 | };
250 |
251 | template
252 | struct RBGet : public exprtk::ifunction {
253 | using exprtk::ifunction::operator();
254 | RBuffers *buffers=nullptr;
255 |
256 | RBGet() : exprtk::ifunction(2) {
257 | }
258 |
259 | inline T operator()(const T &nr,const T &v) override {
260 | if(buffers)
261 | return buffers->get(int(nr),int(v));
262 | return T(0);
263 | }
264 | };
265 |
266 | template
267 | struct BufferWrite : public exprtk::ifunction {
268 | using exprtk::ifunction::operator();
269 | Buffer *buffer=nullptr;
270 |
271 | BufferWrite() : exprtk::ifunction(2) {
272 | }
273 |
274 | inline T operator()(const T &idx,const T &v) override {
275 | if(buffer) {
276 | buffer->write(int(idx),v);
277 | }
278 | return v;
279 | }
280 | };
281 |
282 | template
283 | struct BufferRead : public exprtk::ifunction {
284 | using exprtk::ifunction::operator();
285 | Buffer *buffer=nullptr;
286 |
287 | BufferRead() : exprtk::ifunction(1) {
288 | }
289 |
290 | inline T operator()(const T &pos) override {
291 | return buffer?buffer->read((int)pos):0.f;
292 | }
293 | };
294 |
295 | template
296 | struct Poly : public exprtk::ivararg_function {
297 | inline T operator()(const std::vector &arglist) override {
298 | T result=T(0);
299 | if(!arglist.empty()) {
300 | T in=arglist[0];
301 | std::vector params;
302 | for(int i=1;i
311 | struct Chebyshev : public exprtk::ivararg_function {
312 | inline T operator()(const std::vector &arglist) override {
313 | T result=T(0);
314 | if(!arglist.empty()) {
315 | T in=arglist[0];
316 | std::vector params;
317 | for(int i=1;i
326 | struct LinSeg : public exprtk::ivararg_function {
327 | inline T operator()(const std::vector &arglist) override {
328 | T result=T(0);
329 | if(!arglist.empty()) {
330 | T in=arglist[0];
331 | std::vector params;
332 | for(int i=1;i
341 | struct ExpSeg : public exprtk::ivararg_function {
342 | inline T operator()(const std::vector &arglist) override {
343 | T result=T(0);
344 | if(!arglist.empty()) {
345 | T in=arglist[0];
346 | std::vector params;
347 | for(int i=1;i
356 | struct Spline : public exprtk::ivararg_function {
357 | inline T operator()(const std::vector &arglist) override {
358 | T result=T(0);
359 | if(!arglist.empty()) {
360 | T in=arglist[0];
361 | std::vector params;
362 | for(size_t i=1;i
371 | struct Saw : public exprtk::ifunction {
372 | using exprtk::ifunction::operator();
373 |
374 | Saw() : exprtk::ifunction(1) {
375 | }
376 |
377 | inline T operator()(const T &v) override {
378 | return saw(v);
379 | }
380 |
381 | };
382 |
383 | template
384 | struct Tri : public exprtk::ifunction {
385 | using exprtk::ifunction::operator();
386 |
387 | Tri() : exprtk::ifunction(1) {
388 | }
389 |
390 | inline T operator()(const T &v) override {
391 | return tri(v);
392 | }
393 |
394 | };
395 |
396 | template
397 | struct InX : public exprtk::ifunction {
398 | using exprtk::ifunction::operator();
399 | M *m=nullptr;
400 | explicit InX(M *_m) : exprtk::ifunction(1),m(_m) {
401 | }
402 |
403 | inline T operator()(const T &v) override {
404 | if(v>=0 && v<16) {
405 | return m->inputs[M::X_INPUT].getVoltage((int)v);
406 | }
407 | return 0;
408 | }
409 | };
410 |
411 | template
412 | struct InY : public exprtk::ifunction {
413 | using exprtk::ifunction::operator();
414 | M *m=nullptr;
415 | explicit InY(M *_m) : exprtk::ifunction(1),m(_m) {
416 | }
417 |
418 | inline T operator()(const T &v) override {
419 | if(v>=0 && v<16) {
420 | return m->inputs[M::Y_INPUT].getVoltage((int)v);
421 | }
422 | return 0;
423 | }
424 | };
425 |
426 | template
427 | struct InZ : public exprtk::ifunction {
428 | using exprtk::ifunction::operator();
429 | M *m=nullptr;
430 | explicit InZ(M *_m) : exprtk::ifunction(1),m(_m) {
431 | }
432 |
433 | inline T operator()(const T &v) override {
434 | if(v>=0 && v<16) {
435 | return m->inputs[M::Z_INPUT].getVoltage((int)v);
436 | }
437 | return 0;
438 | }
439 | };
440 |
441 | template
442 | struct InW : public exprtk::ifunction {
443 | using exprtk::ifunction::operator();
444 | M *m=nullptr;
445 | explicit InW(M *_m) : exprtk::ifunction(1),m(_m) {
446 | }
447 |
448 | inline T operator()(const T &v) override {
449 | if(v>=0 && v<16) {
450 | return m->inputs[M::W_INPUT].getVoltage((int)v);
451 | }
452 | return 0;
453 | }
454 | };
455 |
456 | template
457 | struct Scale : public exprtk::ifunction {
458 | using exprtk::ifunction::operator();
459 |
460 | Scale() : exprtk::ifunction(5) {
461 | }
462 |
463 | inline T operator()(const T &v,const T &min,const T &max,const T &newmin,const T &newmax) override {
464 | return scale(v,min,max,newmin,newmax);
465 | }
466 |
467 | };
468 |
469 | template
470 | struct Scale1 : public exprtk::ifunction {
471 | using exprtk::ifunction::operator();
472 |
473 | Scale1() : exprtk::ifunction(3) {
474 | }
475 |
476 | inline T operator()(const T &v,const T &min,const T &max) override {
477 | return scale(v,-1.f,1.f,min,max);
478 | }
479 | };
480 |
481 | struct function_definition {
482 | std::string name;
483 | std::string body;
484 | std::vector var_list;
485 |
486 | void clear() {
487 | name.clear();
488 | body.clear();
489 | var_list.clear();
490 | }
491 | };
492 |
493 | enum func_parse_result {
494 | e_parse_unknown=0,e_parse_success=1,e_parse_partial=2,e_parse_lexfail=4,e_parse_notfunc=8
495 | };
496 |
497 | struct parse_function_definition_impl : public exprtk::lexer::parser_helper {
498 | func_parse_result process(std::string &func_def,function_definition &fd) {
499 | if(!init(func_def))
500 | return e_parse_lexfail;
501 | if(!token_is(token_t::e_symbol,"function"))
502 | return e_parse_notfunc;
503 | if(!token_is(token_t::e_symbol,prsrhlpr_t::e_hold))
504 | return e_parse_partial;
505 |
506 | fd.name=current_token().value;
507 |
508 | next_token();
509 |
510 | if(!token_is(token_t::e_lbracket))
511 | return e_parse_partial;
512 |
513 | if(!token_is(token_t::e_rbracket)) {
514 | std::vector var_list;
515 |
516 | for(;;) {
517 | // (x,y,z,....w)
518 | if(!token_is(token_t::e_symbol,prsrhlpr_t::e_hold))
519 | return e_parse_partial;
520 |
521 | var_list.push_back(current_token().value);
522 |
523 | next_token();
524 |
525 | if(token_is(token_t::e_rbracket))
526 | break;
527 |
528 | if(!token_is(token_t::e_comma))
529 | return e_parse_partial;
530 | }
531 |
532 | var_list.swap(fd.var_list);
533 | }
534 |
535 | std::size_t body_begin=current_token().position;
536 | std::size_t body_end=current_token().position;
537 |
538 | int bracket_stack=0;
539 |
540 | if(!token_is(token_t::e_lcrlbracket,prsrhlpr_t::e_hold))
541 | return e_parse_partial;
542 |
543 | for(;;) {
544 | body_end=current_token().position;
545 |
546 | if(token_is(token_t::e_lcrlbracket))
547 | bracket_stack++;
548 | else if(token_is(token_t::e_rcrlbracket)) {
549 | if(0==--bracket_stack)
550 | break;
551 | } else {
552 | if(lexer().finished())
553 | return e_parse_partial;
554 |
555 | next_token();
556 | }
557 | }
558 |
559 | std::size_t size=body_end-body_begin+1;
560 |
561 | fd.body=func_def.substr(body_begin,size);
562 |
563 | const std::size_t index=body_begin+size;
564 |
565 | if(index rBuffers;
629 | RBPush bufPush;
630 | RBGet bufGet;
631 | RBSetLength bufSetLength;
632 |
633 | Buffer buf[4];
634 | BufferWrite bufWrite[4];
635 | BufferRead bufRead[4];
636 |
637 | Random random;
638 | STrigger strigger[4];
639 | DCBlock dcb;
640 | DCBlock dcb2;
641 |
642 | int polyMode=MAX;
643 |
644 | Poly poly;
645 | Chebyshev chebyshev;
646 | LinSeg linseg;
647 | ExpSeg expseg;
648 | Spline spline;
649 | Saw saw;
650 | Tri tri;
651 | Scale scale;
652 | Scale1 scale1;
653 | Cauchy cauchy;
654 | Coin coin;
655 | BetaRnd beta;
656 | Weibull weibull;
657 | InX inX{this};
658 | InY inY{this};
659 | InZ inZ{this};
660 | InW inW{this};
661 |
662 | FormulaOne() {
663 | config(PARAMS_LEN,INPUTS_LEN,OUTPUTS_LEN,LIGHTS_LEN);
664 | configInput(X_INPUT,"x");
665 | configInput(Y_INPUT,"y");
666 | configInput(Z_INPUT,"z");
667 | configInput(W_INPUT,"w");
668 | configInput(P_INPUT,"t - Phase ([-5,5] -> [0,1])");
669 | configOutput(V_OUTPUT,"CV");
670 | configOutput(V1_OUTPUT,"CV 1");
671 | configOutput(V2_OUTPUT,"CV 2");
672 | configParam(A_PARAM,-1,1,0,"a");
673 | configParam(B_PARAM,-1,1,0,"b");
674 | configParam(C_PARAM,-1,1,0,"c");
675 | configParam(D_PARAM,-1,1,0,"d");
676 | configParam(E_PARAM,-1,1,0,"e");
677 | symbol_table.add_variable("y",y);
678 | symbol_table.add_variable("z",z);
679 | symbol_table.add_variable("x",x);
680 | symbol_table.add_variable("w",w);
681 | symbol_table.add_variable("t",t);
682 | symbol_table.add_variable("a",a);
683 | symbol_table.add_variable("b",b);
684 | symbol_table.add_variable("c",c);
685 | symbol_table.add_variable("d",d);
686 | symbol_table.add_variable("e",e);
687 | symbol_table.add_variable("v1",v1);
688 | symbol_table.add_variable("v2",v2);
689 | symbol_table.add_variable("v3",v3);
690 | symbol_table.add_variable("v4",v4);
691 | symbol_table.add_variable("v5",v5);
692 | symbol_table.add_variable("v6",v6);
693 | symbol_table.add_variable("v7",v7);
694 | symbol_table.add_variable("v8",v8);
695 | symbol_table.add_variable("out1",out1);
696 | symbol_table.add_variable("out2",out2);
697 | symbol_table.add_variable("chn",channel);
698 | symbol_table.add_variable("sr",sr);
699 | symbol_table.add_variable("stim",smpTime);
700 | for(int k=0;k<4;k++) {
701 | bufWrite[k].buffer=&buf[k];
702 | bufRead[k].buffer=&buf[k];
703 | }
704 | bufGet.buffers=&rBuffers;
705 | bufPush.buffers=&rBuffers;
706 | bufSetLength.buffers=&rBuffers;
707 | symbol_table.add_function("rblen",bufSetLength);
708 | symbol_table.add_function("rbget",bufGet);
709 | symbol_table.add_function("rbpush",bufPush);
710 | symbol_table.add_function("bufr",bufRead[0]);
711 | symbol_table.add_function("bufw",bufWrite[0]);
712 | symbol_table.add_function("buf1r",bufRead[1]);
713 | symbol_table.add_function("buf1w",bufWrite[1]);
714 | symbol_table.add_function("buf2r",bufRead[2]);
715 | symbol_table.add_function("buf2w",bufWrite[2]);
716 | symbol_table.add_function("buf3r",bufRead[3]);
717 | symbol_table.add_function("buf3w",bufWrite[3]);
718 |
719 | symbol_table.add_function("st",strigger[0]);
720 | symbol_table.add_function("st1",strigger[1]);
721 | symbol_table.add_function("st2",strigger[2]);
722 | symbol_table.add_function("st3",strigger[3]);
723 | symbol_table.add_function("rnd",random);
724 | symbol_table.add_function("dcb",dcb);
725 | symbol_table.add_function("dcb2",dcb2);
726 | symbol_table.add_function("poly",poly);
727 | symbol_table.add_function("chb",chebyshev);
728 | symbol_table.add_function("lseg",linseg);
729 | symbol_table.add_function("eseg",expseg);
730 | symbol_table.add_function("spl",spline);
731 | symbol_table.add_function("saw",saw);
732 | symbol_table.add_function("tri",tri);
733 | symbol_table.add_function("scl",scale);
734 | symbol_table.add_function("scl1",scale1);
735 | symbol_table.add_function("getX",inX);
736 | symbol_table.add_function("getY",inY);
737 | symbol_table.add_function("getZ",inZ);
738 | symbol_table.add_function("getW",inW);
739 | symbol_table.add_constants();
740 | compositor.add_auxiliary_symtab(symbol_table);
741 | expression.register_symbol_table(compositor.symbol_table());
742 | expression.register_symbol_table(symbol_table);
743 | }
744 |
745 | void clear() {
746 | for(int k=0;k<4;k++) {
747 | buf[k].clear();
748 | }
749 | v1=0;
750 | v2=0;
751 | v3=0;
752 | v4=0;
753 | v5=0;
754 | v6=0;
755 | v7=0;
756 | v8=0;
757 | dcb.reset();
758 | }
759 |
760 | func_parse_result parse_function_definition(std::string &func_def,function_definition &cf) {
761 | parse_function_definition_impl parser;
762 | return parser.process(func_def,cf);
763 | }
764 |
765 | bool resolveFunctions(std::string &form) {
766 | std::string::size_type pos=form.find("function",0);
767 | if(pos==std::string::npos)
768 | return false;
769 | form=form.substr(pos);
770 | function_definition fd;
771 |
772 | func_parse_result fp_result=parse_function_definition(form,fd);
773 |
774 | if(e_parse_success==fp_result) {
775 | std::string vars;
776 |
777 | for(std::size_t i=0;i(100e-6));
818 | std::string form=formula;
819 | resolveFunctions(form);
820 | //INFO("code after parsing functions %s",form.c_str());
821 | compiled=parser.compile(form,expression);
822 | if(!compiled) {
823 | INFO("Error: %s\n",parser.error().c_str());
824 |
825 | for(std::size_t i=0;ievent->setSelectedWidget(container->children.front());
865 | }
866 | }
867 |
868 | };
869 | #endif //DBCAEMODULES_FASTFORMULA_HPP
870 |
--------------------------------------------------------------------------------
/src/FormulaOneEdit.cpp:
--------------------------------------------------------------------------------
1 | #include "plugin.hpp"
2 | #include "textfield.hpp"
3 | #include "FormulaOne.hpp"
4 | extern Model *modelFormulaOne;
5 | struct FormulaOneEdit : Module {
6 | enum ParamId {
7 | PARAMS_LEN
8 | };
9 | enum InputId {
10 | INPUTS_LEN
11 | };
12 | enum OutputId {
13 | OUTPUTS_LEN
14 | };
15 | enum LightId {
16 | LIGHTS_LEN
17 | };
18 | FormulaOne *formel1=nullptr;
19 |
20 | FormulaOneEdit() {
21 | config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN);
22 | }
23 |
24 | void process(const ProcessArgs& args) override {
25 | if(leftExpander.module) {
26 | if(leftExpander.module->model==modelFormulaOne) {
27 | if(formel1==nullptr) {
28 | formel1=reinterpret_cast(leftExpander.module);
29 | formel1->extDirty=true;
30 | }
31 | } else {
32 | formel1=nullptr;
33 | }
34 | } else {
35 | formel1=nullptr;
36 | }
37 | }
38 | };
39 | struct TestTextField : MTextField {
40 |
41 | TestTextField() : MTextField() {
42 | }
43 |
44 | void onChange(const event::Change &e) override {
45 | }
46 |
47 | void draw(const DrawArgs& args) override {
48 | MTextField::draw(args);
49 | }
50 | };
51 |
52 | struct ExtFormulaTextField : MTextField {
53 | FormulaOneEdit *module;
54 |
55 | ExtFormulaTextField() : MTextField() {
56 | //bgColor=nvgRGB(0x30,0x30,0x30);
57 | }
58 |
59 | void setModule(FormulaOneEdit *module) {
60 | this->module=module;
61 | }
62 |
63 | void onChange(const event::Change &e) override {
64 | if(module&&module->formel1) {
65 | module->formel1->formula=getText();
66 | module->formel1->compile();
67 | module->formel1->dirty=true;
68 | }
69 | }
70 |
71 | void draw(const DrawArgs &args) override {
72 | if(module&&module->formel1&&module->formel1->extDirty) {
73 | int cursorSave=cursor;
74 | setText(module->formel1->formula.substr(0));
75 | cursor=selection=cursorSave;
76 | module->formel1->extDirty=false;
77 | } else {
78 |
79 | }
80 | MTextField::draw(args);
81 | }
82 | };
83 |
84 | struct FormulaOneEditWidget : ModuleWidget {
85 | ScrollWidget *scrollWidget;
86 | FormulaOneEditWidget(FormulaOneEdit* module) {
87 | setModule(module);
88 | setPanel(createPanel(asset::plugin(pluginInstance, "res/FormulaOneEdit.svg")));
89 |
90 | addChild(createWidget(Vec(RACK_GRID_WIDTH, 0)));
91 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
92 | addChild(createWidget(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
93 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
94 | scrollWidget = new TextScrollWidget();
95 | scrollWidget->box.size=mm2px(Vec(176,115));
96 | scrollWidget->box.pos = mm2px(Vec(3.5f,MHEIGHT-7-115));
97 | INFO("%f",scrollWidget->box.size.y);
98 | addChild(scrollWidget);
99 | auto textField=createWidget(Vec(0,0));
100 | textField->box.size=mm2px(Vec(200,400));
101 | textField->multiline=true;
102 | textField->setModule(module);
103 | textField->scroll = scrollWidget;
104 |
105 | scrollWidget->container->addChild(textField);
106 | }
107 | };
108 |
109 |
110 | Model* modelFormulaOneEdit = createModel("FormulaOneEdit");
--------------------------------------------------------------------------------
/src/functions.hpp:
--------------------------------------------------------------------------------
1 | //
2 | // Created by docb on 2/24/22.
3 | //
4 |
5 | #ifndef DBRACKFORMULAONE_FUNCTIONS_HPP
6 | #define DBRACKFORMULAONE_FUNCTIONS_HPP
7 |
8 | template
9 | T chebyshev(T in,std::vector coeff) {
10 | int len=coeff.size();
11 | if(len<2)
12 | return 0;
13 | T v1[len*2];
14 | T v2[len*2];
15 | v1[0]=v1[1]=T(1);
16 |
17 | for(int i=2;ii) {
31 | for(int j=1;j= 0 ; --i) {
39 | sum *= in;
40 | sum += v2[i];
41 | }
42 | return sum;
43 | }
44 | template
45 | T linseg(T in,const std::vector ¶ms) {
46 | int len = params.size();
47 | if(len==0) return T(0);
48 | if(len==1) return params[0];
49 | float s=0;
50 | float pre=0;
51 | int j=1;
52 | for(;j=len) return params[len%2==1?len-1:0];
58 | float pct=params[j]==0?1:(in-pre)/params[j];
59 | return params[j-1]+pct*(params[(j+1)
62 | T expseg(T in,const std::vector ¶ms) {
63 | int len = params.size();
64 | if(len<4 || (len-1)%3!=0) return T(0);
65 | float s=0;
66 | float pre=0;
67 | int j=1;
68 | for(;j=len) return params[len-1];
74 | float pct=params[j]==0?1:(in-pre)/params[j];
75 | float f=params[j+1]==0?pct:(1 - std::exp( pct*params[j+1])) / (1 - std::exp(params[j+1]));
76 | return params[j-1]+f*(params[j+2]-params[j-1]);
77 | }
78 |
79 | template
80 | T spline(T phs,const std::vector &pts) {
81 | int len = pts.size();
82 | if(phs>=1.f) phs=0.99999f;
83 | if(phs<0.f) phs = 0.f;
84 | int idx0 = int(phs*float(len))%len;
85 | int idx1 = (idx0+1)%len;
86 | int idx2 = (idx0+2)%len;
87 | int idx3 = (idx0+3)%len;
88 | float a0=pts[idx3]-pts[idx2]-pts[idx0]+pts[idx1];
89 | float a1=pts[idx0]-pts[idx1]-a0;
90 | float a2=pts[idx2]-pts[idx0];
91 | float a3=pts[idx1];
92 | float start = float(idx0)/float(len);
93 | float stepPhs = (phs - start)*len;
94 | return ((a0*stepPhs+a1)*stepPhs+a2)*stepPhs+a3;
95 | }
96 |
97 | template
98 | T poly(T in,const std::vector &coeff) {
99 | if(coeff.size()==0)
100 | return 0;
101 | int last=coeff.size()-1;
102 | T sum=coeff[last];
103 | for(int i=last;i>=0;--i) {
104 | sum*=in;
105 | sum+=coeff[i];
106 | }
107 | return sum;
108 | }
109 | template
110 | T scale(T v,T min, T max, T newmin, T newmax) {
111 | T pct = (v-min)/(max-min);
112 | return newmin+pct*(newmax-newmin);
113 | }
114 |
115 | template
116 | T saw(T v) {
117 | return v-std::floor(v+0.5);
118 | }
119 |
120 | template
121 | T tri(T v) {
122 | return 2*std::fabs(2*saw(v))-1;
123 | }
124 |
125 | template
126 | T pls(T v) {
127 | T t=tri(v);
128 | if(t>0)
129 | return 1;
130 | if(t<0)
131 | return -1;
132 | return 0;
133 | }
134 |
135 | #endif //DBRACKFORMULAONE_FUNCTIONS_HPP
136 |
--------------------------------------------------------------------------------
/src/plugin.cpp:
--------------------------------------------------------------------------------
1 | #include "plugin.hpp"
2 |
3 |
4 | Plugin* pluginInstance;
5 |
6 | extern Model* modelFormulaOne;
7 | extern Model* modelFormulaOneEdit;
8 |
9 | void init(Plugin* p) {
10 | pluginInstance = p;
11 |
12 | // Add modules here
13 | p->addModel(modelFormulaOne);
14 | p->addModel(modelFormulaOneEdit);
15 |
16 | // Any other plugin initialization may go here.
17 | // As an alternative, consider lazy-loading assets and lookup tables when your module is created to reduce startup times of Rack.
18 | }
19 |
--------------------------------------------------------------------------------
/src/plugin.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 |
4 |
5 | using namespace rack;
6 |
7 | // Declare the Plugin, defined in plugin.cpp
8 | extern Plugin* pluginInstance;
9 |
10 | // Declare each Model, defined in each module source file
11 | // extern Model* modelMyModule;
12 | #define TWOPIF 6.2831853f
13 | #define MHEIGHT 128.5f
14 |
15 | template
16 | struct DCBlocker {
17 | T x = 0.f;
18 | T y = 0.f;
19 | T process(T v) {
20 | y = v - x + y * 0.99f;
21 | x = v;
22 | return y;
23 | }
24 | void reset() {
25 | x = 0.f;
26 | y = 0.f;
27 | }
28 | };
29 | struct TrimbotWhite : SvgKnob {
30 | TrimbotWhite() {
31 | minAngle=-0.8*M_PI;
32 | maxAngle=0.8*M_PI;
33 | setSvg(APP->window->loadSvg(asset::plugin(pluginInstance,"res/TrimpotWhite.svg")));
34 | }
35 | };
36 |
37 | struct TrimbotWhite9 : SvgKnob {
38 | TrimbotWhite9() {
39 | minAngle=-0.8*M_PI;
40 | maxAngle=0.8*M_PI;
41 | setSvg(APP->window->loadSvg(asset::plugin(pluginInstance,"res/TrimpotWhite9mm.svg")));
42 | }
43 | };
44 |
45 | struct SmallPort : app::SvgPort {
46 | SmallPort() {
47 | setSvg(APP->window->loadSvg(asset::plugin(pluginInstance,"res/SmallPort.svg")));
48 | }
49 | };
50 |
51 | struct LabelIntSelectItem : MenuItem {
52 | int *value;
53 | std::vector labels;
54 |
55 | LabelIntSelectItem(int *val,std::vector _labels) : value(val),labels(std::move(_labels)) {
56 | }
57 |
58 | Menu *createChildMenu() override {
59 | Menu *menu=new Menu;
60 | for(unsigned k=0;kaddChild(createCheckMenuItem(labels[k],"",[=]() {
62 | return *value==int(k);
63 | },[=]() {
64 | *value=k;
65 | }));
66 | }
67 | return menu;
68 | }
69 | };
--------------------------------------------------------------------------------
/src/rnd.cpp:
--------------------------------------------------------------------------------
1 | #include "rnd.h"
2 | #include
3 | static std::atomic counter {0};
4 |
5 | void RND::reset(unsigned long long rseed) {
6 | if(rseed==0) {
7 | state=seed=(unsigned long long)(time(nullptr)+(counter++))*1234567;
8 | } else {
9 | state=seed=rseed*1234567;
10 | }
11 | }
--------------------------------------------------------------------------------
/src/rnd.h:
--------------------------------------------------------------------------------
1 | #ifndef RND_H
2 | #define RND_H
3 |
4 | #include "cmath"
5 | #include
6 |
7 | class RND {
8 | public:
9 |
10 | explicit RND(unsigned long long rseed=0) {
11 | reset(rseed);
12 | }
13 |
14 | void reset() {
15 | state=seed;
16 | }
17 |
18 | void reset(unsigned long long rseed);
19 |
20 | unsigned long next() {
21 | state=(a*state+c)%m;
22 | unsigned long ret=state>>16;
23 | return ret;
24 | }
25 |
26 | double nextDouble() {
27 | return (double)next()/(double)(m>>16);
28 | }
29 |
30 | bool nextCoin(float thresh = 0.5f) {
31 | return nextDouble() > thresh;
32 | }
33 |
34 | int nextRange(int from, int to) {
35 | if(from==to) return from;
36 | if(from=1)
56 | break;
57 | }
58 | return r1/r2;
59 | }
60 |
61 | double nextWeibull(double dens) {
62 | if(dens<=1) return nextDouble();
63 | return pow(-(log(1.f-(nextDouble() * .63))), (dens));
64 | }
65 |
66 | double nextTri(int count) {
67 | if(count<2) return nextDouble();
68 | double z=0;
69 | for(int j=0;jr)
92 | r=z;
93 | }
94 | return r;
95 | }
96 | double nextCauchy(double _a) {
97 | if (_a > 1.0)
98 | _a = 1.0;
99 | else if (_a < 0.0001)
100 | _a = 0.0001;
101 | double rnd = nextDouble();
102 | return (1/_a)*tan(atan(10*_a)*(rnd))*0.1;
103 | }
104 | double nextExp(double _a) {
105 | if (_a > 1.0)
106 | _a = 1.0;
107 | else if (_a < 0.0001)
108 | _a = 0.0001;
109 | double _c=log(1.0-(0.999*_a));
110 | double r=nextDouble()*0.999*_a;
111 | return (log(1.0-r)/_c);
112 | }
113 | private:
114 | unsigned long long state;
115 | unsigned long long seed;
116 | unsigned long long a=25214903917;
117 | unsigned long long c=11;
118 | unsigned long long m=0x0001000000000000ULL;
119 | };
120 |
121 | #endif // RND_H
122 |
--------------------------------------------------------------------------------
/src/textfield.cpp:
--------------------------------------------------------------------------------
1 | #include "plugin.hpp"
2 | #include "textfield.hpp"
3 | #include
4 | #include
5 |
6 | struct MTextFieldCopyItem : ui::MenuItem {
7 | WeakPtr textField;
8 |
9 | void onAction(const ActionEvent &e) override {
10 | if(!textField)
11 | return;
12 | textField->copyClipboard();
13 | APP->event->setSelectedWidget(textField);
14 | }
15 | };
16 |
17 |
18 | struct MTextFieldCutItem : ui::MenuItem {
19 | WeakPtr textField;
20 |
21 | void onAction(const ActionEvent &e) override {
22 | if(!textField)
23 | return;
24 | textField->cutClipboard();
25 | APP->event->setSelectedWidget(textField);
26 | }
27 | };
28 |
29 |
30 | struct MTextFieldPasteItem : ui::MenuItem {
31 | WeakPtr textField;
32 |
33 | void onAction(const ActionEvent &e) override {
34 | if(!textField)
35 | return;
36 | textField->pasteClipboard();
37 | APP->event->setSelectedWidget(textField);
38 | }
39 | };
40 |
41 |
42 | struct MTextFieldSelectAllItem : ui::MenuItem {
43 | WeakPtr textField;
44 |
45 | void onAction(const ActionEvent &e) override {
46 | if(!textField)
47 | return;
48 | textField->selectAll();
49 | APP->event->setSelectedWidget(textField);
50 | }
51 | };
52 |
53 |
54 | MTextField::MTextField() {
55 | fontPath=asset::plugin(pluginInstance,"res/FreeMonoBold.ttf");
56 | }
57 |
58 | int MTextField::_bndIconLabelTextPosition(NVGcontext *ctx,float x,float y,float w,float h,float fontsize,const char *label,int px,int py) {
59 | std::shared_ptr font=APP->window->loadFont(fontPath);
60 | float bounds[4];
61 | float pleft=BND_TEXT_RADIUS;
62 | if(!label)
63 | return -1;
64 |
65 | if(font&&font->handle>=0) {
66 |
67 | x+=pleft;
68 | y+=BND_WIDGET_HEIGHT-BND_TEXT_PAD_DOWN;
69 |
70 | nvgFontFaceId(ctx,font->handle);
71 | nvgFontSize(ctx,fontsize);
72 | nvgTextAlign(ctx,NVG_ALIGN_LEFT|NVG_ALIGN_BASELINE);
73 |
74 | w-=BND_TEXT_RADIUS+pleft;
75 |
76 | float asc,desc,lh;
77 |
78 | int nrows=nvgTextBreakLines(ctx,label,NULL,w,rows,_BND_MAX_ROWS);
79 | if(nrows==0)
80 | return 0;
81 | nvgTextBoxBounds(ctx,x,y,w,label,NULL,bounds);
82 | nvgTextMetrics(ctx,&asc,&desc,&lh);
83 |
84 | // calculate vertical position
85 | int row=_clamp((int)((float)(py-bounds[1])/lh),0,nrows-1);
86 | // search horizontal position
87 | int nglyphs=nvgTextGlyphPositions(ctx,x,y,rows[row].start,rows[row].end+1,glyphs,_BND_MAX_GLYPHS);
88 | int col,p=0;
89 | for(col=0;col0&&col font=APP->window->loadFont(fontPath);
119 | float pleft=BND_TEXT_RADIUS;
120 | int cbegin=std::min(cursor,selection);
121 | int cend=std::max(cursor,selection);
122 | float x=0; float y=0; float w=box.size.x;
123 | NVGcontext *ctx = APP->window->vg;
124 | if(font&&font->handle>=0) {
125 |
126 | x+=pleft;
127 | y+=BND_WIDGET_HEIGHT-BND_TEXT_PAD_DOWN;
128 |
129 | nvgFontFaceId(ctx,font->handle);
130 | nvgFontSize(ctx,fontSize);
131 | nvgTextAlign(ctx,NVG_ALIGN_LEFT|NVG_ALIGN_BASELINE);
132 |
133 | w-=BND_TEXT_RADIUS+pleft;
134 |
135 | if(cend>=cbegin) {
136 | int c0r,c1r;
137 | float c0x,c0y,c1x,c1y;
138 | float desc,lh;
139 | static NVGtextRow rows[_BND_MAX_ROWS];
140 | int nrows=nvgTextBreakLines(ctx,text.c_str(),text.c_str()+cend+1,w,rows,_BND_MAX_ROWS);
141 | nvgTextMetrics(ctx,NULL,&desc,&lh);
142 |
143 | _bndCaretPosition(ctx,x,y,desc,lh,text.c_str()+cbegin,rows,nrows,&c0r,&c0x,&c0y);
144 | px=c0x;
145 | py=c0y;
146 | }
147 | }
148 | }
149 | void MTextField::_bndIconLabelCaret(NVGcontext *ctx,float x,float y,float w,float h,NVGcolor color,float fontsize,const char *label,NVGcolor caretcolor,int cbegin,int cend) {
150 | std::shared_ptr font=APP->window->loadFont(fontPath);
151 | float pleft=BND_TEXT_RADIUS;
152 | if(!label)
153 | return;
154 |
155 | if(font&&font->handle>=0) {
156 |
157 | x+=pleft;
158 | y+=BND_WIDGET_HEIGHT-BND_TEXT_PAD_DOWN;
159 |
160 | nvgFontFaceId(ctx,font->handle);
161 | nvgFontSize(ctx,fontsize);
162 | nvgTextAlign(ctx,NVG_ALIGN_LEFT|NVG_ALIGN_BASELINE);
163 |
164 | w-=BND_TEXT_RADIUS+pleft;
165 |
166 | if(cend>=cbegin) {
167 | int c0r,c1r;
168 | float c0x,c0y,c1x,c1y;
169 | float desc,lh;
170 | static NVGtextRow rows[_BND_MAX_ROWS];
171 | int nrows=nvgTextBreakLines(ctx,label,label+cend+1,w,rows,_BND_MAX_ROWS);
172 | nvgTextMetrics(ctx,NULL,&desc,&lh);
173 |
174 | _bndCaretPosition(ctx,x,y,desc,lh,label+cbegin,rows,nrows,&c0r,&c0x,&c0y);
175 | _bndCaretPosition(ctx,x,y,desc,lh,label+cend,rows,nrows,&c1r,&c1x,&c1y);
176 |
177 | nvgBeginPath(ctx);
178 | if(cbegin==cend) {
179 | nvgFillColor(ctx,nvgRGBf(0.337,0.502,0.761));
180 | nvgRect(ctx,c0x-1,c0y,2,lh+1);
181 | } else {
182 | nvgFillColor(ctx,caretcolor);
183 | if(c0r==c1r) {
184 | nvgRect(ctx,c0x-1,c0y,c1x-c0x+1,lh+1);
185 | } else {
186 | int blk=c1r-c0r-1;
187 | nvgRect(ctx,c0x-1,c0y,x+w-c0x+1,lh+1);
188 | nvgRect(ctx,x,c1y,c1x-x+1,lh+1);
189 | if(blk)
190 | nvgRect(ctx,x,c0y+lh,w,blk*lh+1);
191 | }
192 | }
193 | nvgFill(ctx);
194 | }
195 |
196 | nvgBeginPath(ctx);
197 | nvgFillColor(ctx,color);
198 | nvgTextBox(ctx,x,y,w,label,NULL);
199 | }
200 | }
201 |
202 |
203 | void MTextField::draw(const DrawArgs &args) {
204 | nvgScissor(args.vg,RECT_ARGS(args.clipBox));
205 |
206 | int begin=std::min(cursor,selection);
207 | int end=std::max(cursor,selection);
208 |
209 | _bndIconLabelCaret(args.vg,0.0,0.0,box.size.x,box.size.y,nvgRGB(128,255,128),fontSize,text.c_str(),nvgRGBA(128,128,128,128),begin,end);
210 |
211 | // Draw placeholder text
212 | if(text.empty()) {
213 | _bndIconLabelCaret(args.vg,0.0,0.0,box.size.x,box.size.y,bndGetTheme()->textFieldTheme.itemColor,fontSize,placeholder.c_str(),bndGetTheme()->textFieldTheme.itemColor,0,-1);
214 | }
215 |
216 | nvgResetScissor(args.vg);
217 | }
218 |
219 | void MTextField::onDragHover(const DragHoverEvent &e) {
220 | OpaqueWidget::onDragHover(e);
221 |
222 | if(e.origin==this) {
223 | int pos=getTextPosition(e.pos);
224 | cursor=pos;
225 | }
226 | }
227 |
228 | void MTextField::onButton(const ButtonEvent &e) {
229 | OpaqueWidget::onButton(e);
230 |
231 | if(e.action==GLFW_PRESS&&e.button==GLFW_MOUSE_BUTTON_LEFT) {
232 | cursor=selection=getTextPosition(e.pos);
233 | }
234 |
235 | if(e.action==GLFW_PRESS&&e.button==GLFW_MOUSE_BUTTON_RIGHT) {
236 | createContextMenu();
237 | e.consume(this);
238 | }
239 | }
240 |
241 | void MTextField::onSelectText(const SelectTextEvent &e) {
242 | if(e.codepoint<128) {
243 | std::string newText(1,(char)e.codepoint);
244 | insertText(newText);
245 | }
246 | e.consume(this);
247 | }
248 |
249 | void MTextField::split() {
250 | char prev=0;
251 | int rk=0;
252 | int start=0;
253 | for(int k=0;k0) {
266 | textRows[rk].begin=text.size();
267 | textRows[rk++].end=text.size();
268 | }
269 | currentMax=rk;
270 | }
271 |
272 | void MTextField::onSelectKey(const SelectKeyEvent &e) {
273 | if(e.action==GLFW_PRESS||e.action==GLFW_REPEAT) {
274 | // Backspace
275 | if(e.key==GLFW_KEY_BACKSPACE&&(e.mods&RACK_MOD_MASK)==0) {
276 | if(cursor==selection) {
277 | cursor=std::max(cursor-1,0);
278 | }
279 | insertText("");
280 | e.consume(this);
281 | }
282 | // Ctrl+Backspace
283 | if(e.key==GLFW_KEY_BACKSPACE&&(e.mods&RACK_MOD_MASK)==RACK_MOD_CTRL) {
284 | if(cursor==selection) {
285 | cursorToPrevWord();
286 | }
287 | insertText("");
288 | e.consume(this);
289 | }
290 | // Delete
291 | if(e.key==GLFW_KEY_DELETE&&(e.mods&RACK_MOD_MASK)==0) {
292 | if(cursor==selection) {
293 | cursor=std::min(cursor+1,(int)text.size());
294 | }
295 | insertText("");
296 | e.consume(this);
297 | }
298 | // Ctrl+Delete
299 | if(e.key==GLFW_KEY_DELETE&&(e.mods&RACK_MOD_MASK)==RACK_MOD_CTRL) {
300 | if(cursor==selection) {
301 | cursorToNextWord();
302 | }
303 | insertText("");
304 | e.consume(this);
305 | }
306 | // Left
307 | if(e.key==GLFW_KEY_LEFT) {
308 | if((e.mods&RACK_MOD_MASK)==RACK_MOD_CTRL) {
309 | cursorToPrevWord();
310 | } else {
311 | cursor=std::max(cursor-1,0);
312 | }
313 | if(!(e.mods&GLFW_MOD_SHIFT)) {
314 | selection=cursor;
315 | }
316 | e.consume(this);
317 | }
318 | // Right
319 | if(e.key==GLFW_KEY_RIGHT) {
320 | if((e.mods&RACK_MOD_MASK)==RACK_MOD_CTRL) {
321 | cursorToNextWord();
322 | } else {
323 | cursor=std::min(cursor+1,(int)text.size());
324 | }
325 | if(!(e.mods&GLFW_MOD_SHIFT)) {
326 | selection=cursor;
327 | }
328 | e.consume(this);
329 | }
330 |
331 | if(e.key==GLFW_KEY_UP) {
332 | if(cursor>0) {
333 | //INFO("cursor=%d",cursor);
334 | split();
335 | for(int k=0;k=textRows[k].begin&&cursor<=textRows[k].end) {
340 | //INFO("row=%d",k);
341 | if(k>0) {
342 | int pos=std::min(cursor-textRows[k].begin,textRows[k-1].end-textRows[k-1].begin);
343 | cursor=textRows[k-1].begin+pos;
344 | }
345 | break;
346 | }
347 | }
348 |
349 | if(!(e.mods&GLFW_MOD_SHIFT)) {
350 | selection=cursor;
351 | }
352 | }
353 | e.consume(this);
354 | }
355 |
356 | if(e.key==GLFW_KEY_DOWN) {
357 | if(cursor=textRows[k].begin&&cursor<=textRows[k].end) {
364 | //INFO("row=%d",k);
365 | if(k=textRows[k].begin&&cursor<=textRows[k].end) {
390 | selection=cursor=textRows[k].begin;
391 | }
392 | }
393 | } else if((e.mods&RACK_MOD_MASK)==GLFW_MOD_SHIFT) {
394 | split();
395 | for(int k=0;k=textRows[k].begin&&cursor<=textRows[k].end) {
397 | cursor=textRows[k].begin;
398 | }
399 | }
400 | }
401 | e.consume(this);
402 | }
403 |
404 | if(e.key==GLFW_KEY_END) {
405 | if((e.mods&RACK_MOD_MASK)==RACK_MOD_CTRL) {
406 | selection=text.size();
407 | cursor=text.size();
408 | } else if((e.mods&RACK_MOD_MASK)==(GLFW_MOD_SHIFT|RACK_MOD_CTRL)) {
409 | cursor=text.size();
410 | } else if((e.mods&RACK_MOD_MASK)==0) {
411 | split();
412 | for(int k=0;k=textRows[k].begin&&cursor<=textRows[k].end) {
414 | selection=cursor=textRows[k].end;
415 | }
416 | }
417 | } else if((e.mods&RACK_MOD_MASK)==GLFW_MOD_SHIFT) {
418 | split();
419 | for(int k=0;k=textRows[k].begin&&cursor<=textRows[k].end) {
421 | cursor=textRows[k].end;
422 | }
423 | }
424 | }
425 | e.consume(this);
426 | }
427 |
428 |
429 | /*
430 | // Home
431 | if(e.key==GLFW_KEY_HOME&&(e.mods&RACK_MOD_MASK)==0) {
432 | selection=cursor=0;
433 | e.consume(this);
434 | }
435 | // Shift+Home
436 | if(e.key==GLFW_KEY_HOME&&(e.mods&RACK_MOD_MASK)==GLFW_MOD_SHIFT) {
437 | cursor=0;
438 | e.consume(this);
439 | }
440 | // End
441 | if(e.key==GLFW_KEY_END&&(e.mods&RACK_MOD_MASK)==0) {
442 | selection=cursor=text.size();
443 | e.consume(this);
444 | }
445 | // Shift+End
446 | if(e.key==GLFW_KEY_END&&(e.mods&RACK_MOD_MASK)==GLFW_MOD_SHIFT) {
447 | cursor=text.size();
448 | e.consume(this);
449 | }
450 | */
451 | // Ctrl+V
452 | if(e.keyName=="v"&&(e.mods&RACK_MOD_MASK)==RACK_MOD_CTRL) {
453 | pasteClipboard(false);
454 | e.consume(this);
455 | }
456 | // Ctrl+X
457 | if(e.keyName=="x"&&(e.mods&RACK_MOD_MASK)==RACK_MOD_CTRL) {
458 | cutClipboard(false);
459 | e.consume(this);
460 | }
461 | // Ctrl+C
462 | if(e.keyName=="c"&&(e.mods&RACK_MOD_MASK)==RACK_MOD_CTRL) {
463 | copyClipboard(false);
464 | e.consume(this);
465 | }
466 | // Ctrl+A
467 | if(e.keyName=="a"&&(e.mods&RACK_MOD_MASK)==RACK_MOD_CTRL) {
468 | selectAll();
469 | e.consume(this);
470 | }
471 | // Enter
472 | if((e.key==GLFW_KEY_ENTER||e.key==GLFW_KEY_KP_ENTER)&&(e.mods&RACK_MOD_MASK)==0) {
473 | if(multiline) {
474 | insertText("\n");
475 | } else {
476 | ActionEvent eAction;
477 | onAction(eAction);
478 | }
479 | e.consume(this);
480 | }
481 | // Tab
482 | if(e.key==GLFW_KEY_TAB&&(e.mods&RACK_MOD_MASK)==0) {
483 | if(nextField)
484 | APP->event->setSelectedWidget(nextField);
485 | e.consume(this);
486 | }
487 | // Shift-Tab
488 | if(e.key==GLFW_KEY_TAB&&(e.mods&RACK_MOD_MASK)==GLFW_MOD_SHIFT) {
489 | if(prevField)
490 | APP->event->setSelectedWidget(prevField);
491 | e.consume(this);
492 | }
493 | // Consume all printable keys
494 | if(e.keyName!="") {
495 | e.consume(this);
496 | }
497 | float px,py;
498 | getCursorPosition(px,py);
499 | //INFO("%f %f %f %f %f",px,py,scroll->offset.x,scroll->offset.y,scroll->box.size.y);
500 | while(py>scroll->box.size.y-24+scroll->offset.y) scroll->offset.y+=10;
501 | while(pyoffset.y) scroll->offset.y-=10;
502 | if(scroll->offset.y<0) scroll->offset.y = 0;
503 |
504 | while(px>scroll->box.size.x-24+scroll->offset.x) scroll->offset.x+=10;
505 | while(pxoffset.x) scroll->offset.x-=10;
506 | if(scroll->offset.x<0) scroll->offset.x = 0;
507 |
508 | assert(0<=cursor);
509 | assert(cursor<=(int)text.size());
510 | assert(0<=selection);
511 | assert(selection<=(int)text.size());
512 | }
513 | }
514 |
515 | int MTextField::getTextPosition(math::Vec mousePos) {
516 | return _bndIconLabelTextPosition(APP->window->vg,0.0,0.0,box.size.x,box.size.y,fontSize,text.c_str(),mousePos.x,mousePos.y);
517 | }
518 |
519 | std::string MTextField::getText() {
520 | return text;
521 | }
522 |
523 | void MTextField::setText(std::string text) {
524 | if(this->text!=text) {
525 | this->text=text;
526 | // ChangeEvent
527 | ChangeEvent eChange;
528 | onChange(eChange);
529 | }
530 | selection=cursor=text.size();
531 | }
532 |
533 | void MTextField::selectAll() {
534 | cursor=text.size();
535 | selection=0;
536 | }
537 |
538 | std::string MTextField::getSelectedText() {
539 | int begin=std::min(cursor,selection);
540 | int len=std::abs(selection-cursor);
541 | return text.substr(begin,len);
542 | }
543 |
544 | void MTextField::insertText(std::string text) {
545 | bool changed=false;
546 | if(cursor!=selection) {
547 | // Delete selected text
548 | int begin=std::min(cursor,selection);
549 | int len=std::abs(selection-cursor);
550 | this->text.erase(begin,len);
551 | cursor=selection=begin;
552 | changed=true;
553 | }
554 | if(!text.empty()) {
555 | this->text.insert(cursor,text);
556 | cursor+=text.size();
557 | selection=cursor;
558 | changed=true;
559 | }
560 | if(changed) {
561 | ChangeEvent eChange;
562 | onChange(eChange);
563 | }
564 | }
565 |
566 | void MTextField::copyClipboard(bool menu) {
567 | if(cursor==selection)
568 | return;
569 | glfwSetClipboardString(APP->window->win,getSelectedText().c_str());
570 | }
571 |
572 | void MTextField::cutClipboard(bool menu) {
573 | copyClipboard();
574 | insertText("");
575 | }
576 |
577 | void MTextField::pasteClipboard(bool menu) {
578 | const char *newText=glfwGetClipboardString(APP->window->win);
579 | if(!newText)
580 | return;
581 | std::string strText(newText);
582 | strText.erase(std::remove(strText.begin(), strText.end(), 0x0d), strText.end());
583 | insertText(strText);
584 | }
585 |
586 | void MTextField::cursorToPrevWord() {
587 | size_t pos=text.rfind(' ',std::max(cursor-2,0));
588 | if(pos==std::string::npos)
589 | cursor=0;
590 | else
591 | cursor=std::min((int)pos+1,(int)text.size());
592 | }
593 |
594 | void MTextField::cursorToNextWord() {
595 | size_t pos=text.find(' ',std::min(cursor+1,(int)text.size()));
596 | if(pos==std::string::npos)
597 | pos=text.size();
598 | cursor=pos;
599 | }
600 |
601 | void MTextField::createContextMenu() {
602 | menu=createMenu();
603 |
604 | MTextFieldCutItem *cutItem=new MTextFieldCutItem;
605 | cutItem->text="Cut";
606 | cutItem->rightText=RACK_MOD_CTRL_NAME
607 | "+X";
608 | cutItem->textField=this;
609 | menu->addChild(cutItem);
610 |
611 | MTextFieldCopyItem *copyItem=new MTextFieldCopyItem;
612 | copyItem->text="Copy";
613 | copyItem->rightText=RACK_MOD_CTRL_NAME
614 | "+C";
615 | copyItem->textField=this;
616 | menu->addChild(copyItem);
617 |
618 | MTextFieldPasteItem *pasteItem=new MTextFieldPasteItem;
619 | pasteItem->text="Paste";
620 | pasteItem->rightText=RACK_MOD_CTRL_NAME
621 | "+V";
622 | pasteItem->textField=this;
623 | menu->addChild(pasteItem);
624 |
625 | MTextFieldSelectAllItem *selectAllItem=new MTextFieldSelectAllItem;
626 | selectAllItem->text="Select all";
627 | selectAllItem->rightText=RACK_MOD_CTRL_NAME
628 | "+A";
629 | selectAllItem->textField=this;
630 | menu->addChild(selectAllItem);
631 | }
632 |
--------------------------------------------------------------------------------
/src/textfield.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | // From Rack ui but modified to have more possibilities to customize.
4 | // Uses also some functions of the blender utilities directly to provide more lines in the text.
5 | // Adds the feature to move up and down using the arrow keys.
6 |
7 | //#include "plugin.hpp"
8 | #define _BND_MAX_ROWS 100
9 | #define _BND_MAX_GLYPHS 10240
10 | struct TextRow {
11 | int begin;
12 | int end;
13 | };
14 | struct MTextField : OpaqueWidget {
15 | ScrollWidget *scroll;
16 | std::string text;
17 | std::string placeholder;
18 | /** Masks text with "*". */
19 | bool password=false;
20 | bool multiline=false;
21 | int fontSize = 10;
22 | /** The index of the text cursor */
23 | int cursor=0;
24 | /** The index of the other end of the selection.
25 | If nothing is selected, this is equal to `cursor`.
26 | */
27 | int selection=0;
28 |
29 | int _bndIconLabelTextPosition(NVGcontext *ctx,float x,float y,float w,float h,float fontsize,const char *label,int px,int py);
30 |
31 | void _bndCaretPosition(NVGcontext *ctx,float x,float y,float desc,float lineHeight,const char *caret,NVGtextRow *rows,int nrows,int *cr,float *cx,float *cy);
32 |
33 | void _bndIconLabelCaret(NVGcontext *ctx,float x,float y,float w,float h,NVGcolor color,float fontsize,const char *label,NVGcolor caretcolor,int cbegin,int cend);
34 |
35 | /** For Tab and Shift-Tab focusing.
36 | */
37 | widget::Widget *prevField=NULL;
38 | widget::Widget *nextField=NULL;
39 | ui::Menu *menu;
40 | std::basic_string fontPath;
41 | NVGtextRow rows[_BND_MAX_ROWS];
42 | TextRow textRows[_BND_MAX_ROWS];
43 | NVGglyphPosition glyphs[_BND_MAX_GLYPHS];
44 | float _clamp(float v,float mn,float mx) {
45 | return (v>mx)?mx:(v