Added CALF LADSPA plugins
As per popular demand I added CALF LADSPA plugins to be shipped with LMMS. After some minor modifications the plugins compile and work on win32 platform too.
This commit is contained in:
@@ -49,6 +49,7 @@ ELSE(LMMS_HOST_X86_64)
|
||||
ENDIF(LMMS_HOST_X86_64)
|
||||
|
||||
OPTION(WANT_ALSA "Include ALSA (Advanced Linux Sound Architecture) support" ON)
|
||||
OPTION(WANT_CALF "Include CALF LADSPA plugins" ON)
|
||||
OPTION(WANT_CAPS "Include C* Audio Plugin Suite (LADSPA plugins)" ON)
|
||||
OPTION(WANT_CMT "Include Computer Music Toolkit LADSPA plugins" ON)
|
||||
OPTION(WANT_FFTW3F "Include SpectrumAnalyzer and ZynAddSubFX plugin" ON)
|
||||
@@ -130,6 +131,13 @@ IF(NOT SNDFILE_FOUND)
|
||||
MESSAGE(FATAL_ERROR "LMMS requires libsndfile1 and libsndfile1-dev >= 1.0.11 - please install, remove CMakeCache.txt and try again!")
|
||||
ENDIF(NOT SNDFILE_FOUND)
|
||||
|
||||
IF(WANT_CALF)
|
||||
SET(LMMS_HAVE_CALF TRUE)
|
||||
SET(STATUS_CALF "OK")
|
||||
ELSE(WANT_CALF)
|
||||
SET(STATUS_CALF "not built as requested")
|
||||
ENDIF(WANT_CALF)
|
||||
|
||||
IF(WANT_CAPS)
|
||||
SET(LMMS_HAVE_CAPS TRUE)
|
||||
SET(STATUS_CAPS "OK")
|
||||
@@ -701,6 +709,7 @@ MESSAGE(
|
||||
"* VST-effect hoster : ${STATUS_VST}\n"
|
||||
"* LV2 hoster : ${STATUS_LV2}\n"
|
||||
"* SpectrumAnalyzer : ${STATUS_FFTW3F}\n"
|
||||
"* CALF LADSPA plugins : ${STATUS_CALF}\n"
|
||||
"* CAPS LADSPA plugins : ${STATUS_CAPS}\n"
|
||||
"* CMT LADSPA plugins : ${STATUS_CMT}\n"
|
||||
"* TAP LADSPA plugins : ${STATUS_TAP}\n"
|
||||
|
||||
@@ -14,6 +14,10 @@ IF(WANT_CMT)
|
||||
ADD_SUBDIRECTORY(cmt)
|
||||
ENDIF(WANT_CMT)
|
||||
|
||||
IF(WANT_CALF)
|
||||
ADD_SUBDIRECTORY(calf)
|
||||
ENDIF(WANT_CALF)
|
||||
|
||||
|
||||
INCLUDE(BuildPlugin)
|
||||
|
||||
|
||||
9
plugins/ladspa_effect/calf/AUTHORS
Normal file
9
plugins/ladspa_effect/calf/AUTHORS
Normal file
@@ -0,0 +1,9 @@
|
||||
Krzysztof Foltman <wdev@foltman.com>
|
||||
Hermann Meyer <brummer-@web.de>
|
||||
Thor Harald Johansen <thj@thj.no>
|
||||
Thorsten Wilms <t_w_@freenet.de>
|
||||
Hans Baier <hansfbaier@googlemail.com>
|
||||
Torben Hohn <torbenh@gmx.de>
|
||||
|
||||
Additional bugfixes/enhancement patches:
|
||||
David Täht <d@teklibre.com>
|
||||
15
plugins/ladspa_effect/calf/CMakeLists.txt
Normal file
15
plugins/ladspa_effect/calf/CMakeLists.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
FILE(GLOB SOURCES *.cpp)
|
||||
ADD_LIBRARY(calf MODULE ${SOURCES})
|
||||
INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/calf)
|
||||
INSTALL(TARGETS calf LIBRARY DESTINATION ${PLUGIN_DIR}/ladspa)
|
||||
ADD_DEFINITIONS(-DUSE_LADSPA=1)
|
||||
SET_TARGET_PROPERTIES(calf PROPERTIES PREFIX "")
|
||||
SET_TARGET_PROPERTIES(calf PROPERTIES COMPILE_FLAGS "-O2 -finline-limit=80 -funroll-loops")
|
||||
|
||||
IF(LMMS_BUILD_WIN32)
|
||||
ADD_CUSTOM_COMMAND(TARGET calf POST_BUILD COMMAND ${STRIP} ${CMAKE_CURRENT_BINARY_DIR}/calf.dll)
|
||||
ENDIF(LMMS_BUILD_WIN32)
|
||||
IF(NOT LMMS_BUILD_APPLE)
|
||||
SET_TARGET_PROPERTIES(calf PROPERTIES LINK_FLAGS "${LINK_FLAGS} -shared -Wl,-no-undefined")
|
||||
ENDIF(NOT LMMS_BUILD_APPLE)
|
||||
|
||||
504
plugins/ladspa_effect/calf/COPYING
Normal file
504
plugins/ladspa_effect/calf/COPYING
Normal file
@@ -0,0 +1,504 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
|
||||
|
||||
340
plugins/ladspa_effect/calf/COPYING.GPL
Normal file
340
plugins/ladspa_effect/calf/COPYING.GPL
Normal file
@@ -0,0 +1,340 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
||||
170
plugins/ladspa_effect/calf/ChangeLog
Normal file
170
plugins/ladspa_effect/calf/ChangeLog
Normal file
@@ -0,0 +1,170 @@
|
||||
Version 0.0.18.2
|
||||
|
||||
+ Organ: fix voice stealing of released notes, sort out GUI, add quadratic
|
||||
mode for amplitude envelope (enabled by default) - sounds more natural
|
||||
+ Monosynth: fix the bug that caused JACK to kick the client out due
|
||||
to precalculating waves in a completely wrong place, fix portamento
|
||||
for off-stack notes
|
||||
+ Presets: 3 new presets for Organ, 4 for Monosynth, 2 for Reverb
|
||||
|
||||
Version 0.0.18.1
|
||||
|
||||
+ Filter: fixed subtle redraw bugs
|
||||
+ Icons: fixed packaging-incompatible paths
|
||||
|
||||
Version 0.0.18
|
||||
|
||||
+ Filterclavier: new plugin (a MIDI controlled filter) by Hans Baier
|
||||
+ DSSI: added a basic implementation of live graphs. The graphs have a
|
||||
limited resolution (128 data points), and are rather inefficient
|
||||
(as the graph data need to be transmitted via OSC to a different
|
||||
process), but it's better than nothing
|
||||
+ GUI: Torben Hohn's drawing optimizations (critical for Intel graphics
|
||||
cards, but should also reduce CPU usage on other hardware)
|
||||
+ Phaser: added frequency response graph
|
||||
+ JACK host: discontinue the broken option -p; allow giving preset names
|
||||
after a colon sign (reverb:DiscoVerb instead of -p DiscoVerb reverb)
|
||||
+ Reverb: less modulation; tone controls; 2 more room types
|
||||
+ MultiChorus: add double bandpass filter on input
|
||||
+ GUI: added frequency grid
|
||||
+ Organ: added progress reporting on load (works with JACK host and LV2)
|
||||
+ JACK host: use sensible port names (possibly breaking new LASH sessions)
|
||||
+ Organ: added polyphony limit
|
||||
+ Small plugins: added support for polymorphic port extension to allow
|
||||
the same plugins to be used for control and audio signals
|
||||
+ DSSI: renamed all the plugins from "plugin LADSPA" to "plugin DSSI"
|
||||
+ LADSPA: more reasonable default value hints, fixed locale issue in LRDF
|
||||
+ JACK host: added icons by Thorsten Wilms (thanks!)
|
||||
+ Organ, Monosynth: better memory usage
|
||||
+ LV2: attempt at supporting configure-like parameters (key mapping curve
|
||||
in Organ) by the new String Port extension
|
||||
+ AutoHell: header files are not installed anymore (they are of little
|
||||
use anyway)
|
||||
+ AutoHell: configure script prints if --enable-experimental was specified
|
||||
|
||||
Version 0.0.17
|
||||
|
||||
+ Compressor: new plugin by Thor Harald Johansen
|
||||
+ GUI: control improvements (new LED control, improved VU meter, XML
|
||||
improvements, line graph with dots and grid lines - no legend yet), move
|
||||
autolayout code from the plugin libraries to makerdf executable,
|
||||
+ Most plugins: use custom GUI layouts instead of autogenerated ones
|
||||
+ Most plugins: add dry amount (for aux bus type uses)
|
||||
+ Flanger, Filter, MultiChorus: added live graphs displaying frequency
|
||||
response and (in case of MultiChorus) LFO positions
|
||||
+ LV2 GUI: added a way to display live graphs in Ardour and Zynjacku/LV2Rack
|
||||
(only works when the plugin and the GUI are in the same process)
|
||||
+ Framework: general improvements/cleanups to reduce the chance of the
|
||||
kind of errors that were introduced in 0.0.16 and reduce dependencies
|
||||
+ Monosynth: removed soft clipper on output
|
||||
|
||||
Version 0.0.16.3
|
||||
|
||||
+ Fixed compilation without LV2 core installed
|
||||
|
||||
Version 0.0.16.2
|
||||
|
||||
+ Fixed DSSI GUI for MultiChorus
|
||||
+ Fixed LV2 GUI for MultiChorus
|
||||
+ Make knob control mouse wheel handling work better in Ingen
|
||||
|
||||
Version 0.0.16
|
||||
|
||||
+ New MultiChorus plugin (stereo multitap chorus with maximum of 8 voices)
|
||||
+ Experimental set of plugins for modular synthesizers like Ingen by
|
||||
Dave Robillard (enabled using --enable-experimental option in configure
|
||||
script)
|
||||
+ Minor improvements to other plugins (like Rotary Speaker)
|
||||
+ More work on API documentation
|
||||
|
||||
Version 0.0.15
|
||||
|
||||
+ Organ: new percussive section, using 2-operator FM synthesis for
|
||||
monophonic or polyphonic percussive attack; added global transpose and
|
||||
detune; rearrangement of controls between sections
|
||||
+ Rotary Speaker: another attempt at making it useful (thanks FishB8)
|
||||
+ JACK host: eliminate deadlock on exit
|
||||
+ GUI: bipolar knobs now have a "dead zone" (magnet) in the middle point
|
||||
+ GUI: dragging a knob with SHIFT held allows for fine adjustments
|
||||
+ GUI: new controls - curve editor and keyboard
|
||||
+ LV2: improved extension support (supports my "extended port properties"
|
||||
extension now)
|
||||
+ Added some API documentation
|
||||
|
||||
Version 0.0.14
|
||||
+ OSC: totally new OSC wrapper, to allow for realtime-safe parsing (doesn't
|
||||
matter as far as functionality goes, will probably be rewritten again
|
||||
anyway)
|
||||
+ Everything: memory management fixes (should improve stability and
|
||||
compatibility)
|
||||
+ Organ: improved memory usage
|
||||
+ GUI: improved bipolar knobs, added endless knobs
|
||||
+ Presets: separate 'built-in' and 'user' presets (so that built-in presets
|
||||
can be upgraded without affecting user's own presets)
|
||||
+ Monosynth: new presets
|
||||
|
||||
Version 0.0.13
|
||||
+ Fixed several problems related to 64-bit environments and OpenSUSE (thanks
|
||||
oc2pus!)
|
||||
+ Added NOCONFIGURE environment variable support to autogen.sh
|
||||
|
||||
Version 0.0.12
|
||||
+ RotarySpeaker: work in progress; enabled by default just in case it's
|
||||
useful for anyone
|
||||
+ Organ: reworked to add a complete subtractive synth section, a selection
|
||||
of waveform (settable on a per-drawbar basis), individual settings of
|
||||
phase, detune, panning, routing for each drawbar, as well as improved(?)
|
||||
percussive section and vibrato/phaser section. It is usable (and sounds
|
||||
good!), but some parameters, waveform set etc. may change in future. May
|
||||
take up to 100 MB of RAM due to pre-calculated bandlimited waveforms.
|
||||
+ Added half-complete implementation of LV2 (including GUI and events).
|
||||
+ Lots of small "polishing" kind of fixes in many places (like proper
|
||||
rounding of values in the GUIs, another set of hold/sostenuto fixes etc)
|
||||
|
||||
Version 0.0.11
|
||||
|
||||
+ Fixed x86-64 bugs
|
||||
+ JackHost: implemented LASH support
|
||||
+ RotarySpeaker: fixed panning bug, implemented acceleration/decceleration
|
||||
for "off" state
|
||||
|
||||
Version 0.0.10
|
||||
|
||||
+ First attempt at DSSI GUI, does not support some features from JACK host,
|
||||
but that's inevitable because of API limitations
|
||||
+ Reverb: improvements (more parameters, fixed denormals)
|
||||
+ Knob: added custom support for scroll wheel (instead of one inherited from
|
||||
GtkRange)
|
||||
|
||||
Version 0.0.9
|
||||
|
||||
+ started creating an XML-based GUI
|
||||
+ LineGraph: new GTK+ control for displaying waveforms and filter response
|
||||
graphs in Monosynth (and maybe others in future)
|
||||
+ Monosynth: notch filter changes (made notch bandwidth proportional to Q,
|
||||
just for fun, might be a bad idea)
|
||||
+ Monosynth: more waveforms (these might be final?)
|
||||
+ Monosynth: capped Sustain level to 0.999 so that decay time actually means
|
||||
something with Sustain = 100% (not a great way to do it, but acceptable in
|
||||
this case)
|
||||
+ Monosynth: GUI refreshes less often (which means less CPU use)
|
||||
+ Monosynth: less clicks on sounds using LP filter with very low cutoff
|
||||
(using ramp of 256 samples instead of 64 samples as before)
|
||||
+ Knob: new GTK+ control based on GtkRange, with my primitive bitmap set
|
||||
(generated with Python and Cairo)
|
||||
+ Organ: added a GUI too, very provisional
|
||||
+ Organ: fixed Hold pedal (doesn't release the notes which are still depressed)
|
||||
+ RotarySpeaker: new effect (split off Organ)
|
||||
+ all: denormal fixes (still some denormals present in reverb)
|
||||
+ Reverb: better time setting (decay time somewhat corresponds to -60dB
|
||||
attenuation time)
|
||||
+ JackHost: -M switch allows for automatic connection to JACK MIDI event source
|
||||
(use -M system:midi_capture_2 or -M 2 for autoconnection to
|
||||
system:midi_capture_2; of course, the short numeric form only work for
|
||||
system:midi_capture_ ports)
|
||||
+ JackHost: -p switch selects a preset automatically
|
||||
+ JackHost: better size setting algorithm
|
||||
+ JackHost: duplicate client name (causing JACK to rename the client) doesn't
|
||||
break autoconnecting functionality
|
||||
+ autotools configuration update (detect Cairo and require newer GTK+)
|
||||
+ more presets
|
||||
254
plugins/ladspa_effect/calf/INSTALL
Normal file
254
plugins/ladspa_effect/calf/INSTALL
Normal file
@@ -0,0 +1,254 @@
|
||||
Installation Instructions
|
||||
*************************
|
||||
|
||||
Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free
|
||||
Software Foundation, Inc.
|
||||
Copyright (C) 2007-2008 Krzysztof Foltman
|
||||
|
||||
This file is free documentation; the Free Software Foundation gives
|
||||
unlimited permission to copy, distribute and modify it.
|
||||
|
||||
Prerequisites
|
||||
=============
|
||||
|
||||
To compile and install Calf, you need:
|
||||
|
||||
- POSIX-compliant operating system
|
||||
- G++ version 4.0 or higher (tested with 4.1.3)
|
||||
- GTK+2 headers and libraries (glib 2.10, gtk+ 2.12)
|
||||
- Cairo headers and libraries
|
||||
- Glade 2 headers and libraries
|
||||
|
||||
Optional but recommended:
|
||||
- JACK header and libraries (tested with 0.109.0)
|
||||
- LADSPA header
|
||||
- DSSI header
|
||||
- LV2 core
|
||||
|
||||
Basic Installation
|
||||
==================
|
||||
|
||||
These are generic installation instructions.
|
||||
|
||||
The `configure' shell script attempts to guess correct values for
|
||||
various system-dependent variables used during compilation. It uses
|
||||
those values to create a `Makefile' in each directory of the package.
|
||||
It may also create one or more `.h' files containing system-dependent
|
||||
definitions. Finally, it creates a shell script `config.status' that
|
||||
you can run in the future to recreate the current configuration, and a
|
||||
file `config.log' containing compiler output (useful mainly for
|
||||
debugging `configure').
|
||||
|
||||
It can also use an optional file (typically called `config.cache'
|
||||
and enabled with `--cache-file=config.cache' or simply `-C') that saves
|
||||
the results of its tests to speed up reconfiguring. (Caching is
|
||||
disabled by default to prevent problems with accidental use of stale
|
||||
cache files.)
|
||||
|
||||
If you need to do unusual things to compile the package, please try
|
||||
to figure out how `configure' could check whether to do them, and mail
|
||||
diffs or instructions to the address given in the `README' so they can
|
||||
be considered for the next release. If you are using the cache, and at
|
||||
some point `config.cache' contains results you don't want to keep, you
|
||||
may remove or edit it.
|
||||
|
||||
The file `configure.ac' (or `configure.in') is used to create
|
||||
`configure' by a program called `autoconf'. You only need
|
||||
`configure.ac' if you want to change it or regenerate `configure' using
|
||||
a newer version of `autoconf'.
|
||||
|
||||
The simplest way to compile this package is:
|
||||
|
||||
1. `cd' to the directory containing the package's source code and type
|
||||
`./configure' to configure the package for your system. If you're
|
||||
using `csh' on an old version of System V, you might need to type
|
||||
`sh ./configure' instead to prevent `csh' from trying to execute
|
||||
`configure' itself.
|
||||
|
||||
Running `configure' takes awhile. While running, it prints some
|
||||
messages telling which features it is checking for.
|
||||
|
||||
2. Type `make' to compile the package.
|
||||
|
||||
3. Optionally, type `make check' to run any self-tests that come with
|
||||
the package.
|
||||
|
||||
4. Type `make install' to install the programs and any data files and
|
||||
documentation.
|
||||
|
||||
5. You can remove the program binaries and object files from the
|
||||
source code directory by typing `make clean'. To also remove the
|
||||
files that `configure' created (so you can compile the package for
|
||||
a different kind of computer), type `make distclean'. There is
|
||||
also a `make maintainer-clean' target, but that is intended mainly
|
||||
for the package's developers. If you use it, you may have to get
|
||||
all sorts of other programs in order to regenerate files that came
|
||||
with the distribution.
|
||||
|
||||
Compilers and Options
|
||||
=====================
|
||||
|
||||
Some systems require unusual options for compilation or linking that the
|
||||
`configure' script does not know about. Run `./configure --help' for
|
||||
details on some of the pertinent environment variables.
|
||||
|
||||
You can give `configure' initial values for configuration parameters
|
||||
by setting variables in the command line or in the environment. Here
|
||||
is an example:
|
||||
|
||||
./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
|
||||
|
||||
*Note Defining Variables::, for more details.
|
||||
|
||||
Compiling For Multiple Architectures
|
||||
====================================
|
||||
|
||||
You can compile the package for more than one kind of computer at the
|
||||
same time, by placing the object files for each architecture in their
|
||||
own directory. To do this, you must use a version of `make' that
|
||||
supports the `VPATH' variable, such as GNU `make'. `cd' to the
|
||||
directory where you want the object files and executables to go and run
|
||||
the `configure' script. `configure' automatically checks for the
|
||||
source code in the directory that `configure' is in and in `..'.
|
||||
|
||||
If you have to use a `make' that does not support the `VPATH'
|
||||
variable, you have to compile the package for one architecture at a
|
||||
time in the source code directory. After you have installed the
|
||||
package for one architecture, use `make distclean' before reconfiguring
|
||||
for another architecture.
|
||||
|
||||
Installation Names
|
||||
==================
|
||||
|
||||
By default, `make install' installs the package's commands under
|
||||
`/usr/local/bin', include files under `/usr/local/include', etc. You
|
||||
can specify an installation prefix other than `/usr/local' by giving
|
||||
`configure' the option `--prefix=PREFIX'.
|
||||
|
||||
You can specify separate installation prefixes for
|
||||
architecture-specific files and architecture-independent files. If you
|
||||
pass the option `--exec-prefix=PREFIX' to `configure', the package uses
|
||||
PREFIX as the prefix for installing programs and libraries.
|
||||
Documentation and other data files still use the regular prefix.
|
||||
|
||||
In addition, if you use an unusual directory layout you can give
|
||||
options like `--bindir=DIR' to specify different values for particular
|
||||
kinds of files. Run `configure --help' for a list of the directories
|
||||
you can set and what kinds of files go in them.
|
||||
|
||||
If the package supports it, you can cause programs to be installed
|
||||
with an extra prefix or suffix on their names by giving `configure' the
|
||||
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
|
||||
|
||||
Optional Features
|
||||
=================
|
||||
|
||||
Some packages pay attention to `--enable-FEATURE' options to
|
||||
`configure', where FEATURE indicates an optional part of the package.
|
||||
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
|
||||
is something like `gnu-as' or `x' (for the X Window System). The
|
||||
`README' should mention any `--enable-' and `--with-' options that the
|
||||
package recognizes.
|
||||
|
||||
For packages that use the X Window System, `configure' can usually
|
||||
find the X include and library files automatically, but if it doesn't,
|
||||
you can use the `configure' options `--x-includes=DIR' and
|
||||
`--x-libraries=DIR' to specify their locations.
|
||||
|
||||
Specifying the System Type
|
||||
==========================
|
||||
|
||||
There may be some features `configure' cannot figure out automatically,
|
||||
but needs to determine by the type of machine the package will run on.
|
||||
Usually, assuming the package is built to be run on the _same_
|
||||
architectures, `configure' can figure that out, but if it prints a
|
||||
message saying it cannot guess the machine type, give it the
|
||||
`--build=TYPE' option. TYPE can either be a short name for the system
|
||||
type, such as `sun4', or a canonical name which has the form:
|
||||
|
||||
CPU-COMPANY-SYSTEM
|
||||
|
||||
where SYSTEM can have one of these forms:
|
||||
|
||||
OS KERNEL-OS
|
||||
|
||||
See the file `config.sub' for the possible values of each field. If
|
||||
`config.sub' isn't included in this package, then this package doesn't
|
||||
need to know the machine type.
|
||||
|
||||
If you are _building_ compiler tools for cross-compiling, you should
|
||||
use the option `--target=TYPE' to select the type of system they will
|
||||
produce code for.
|
||||
|
||||
If you want to _use_ a cross compiler, that generates code for a
|
||||
platform different from the build platform, you should specify the
|
||||
"host" platform (i.e., that on which the generated programs will
|
||||
eventually be run) with `--host=TYPE'.
|
||||
|
||||
Sharing Defaults
|
||||
================
|
||||
|
||||
If you want to set default values for `configure' scripts to share, you
|
||||
can create a site shell script called `config.site' that gives default
|
||||
values for variables like `CC', `cache_file', and `prefix'.
|
||||
`configure' looks for `PREFIX/share/config.site' if it exists, then
|
||||
`PREFIX/etc/config.site' if it exists. Or, you can set the
|
||||
`CONFIG_SITE' environment variable to the location of the site script.
|
||||
A warning: not all `configure' scripts look for a site script.
|
||||
|
||||
Defining Variables
|
||||
==================
|
||||
|
||||
Variables not defined in a site shell script can be set in the
|
||||
environment passed to `configure'. However, some packages may run
|
||||
configure again during the build, and the customized values of these
|
||||
variables may be lost. In order to avoid this problem, you should set
|
||||
them in the `configure' command line, using `VAR=value'. For example:
|
||||
|
||||
./configure CC=/usr/local2/bin/gcc
|
||||
|
||||
causes the specified `gcc' to be used as the C compiler (unless it is
|
||||
overridden in the site shell script). Here is a another example:
|
||||
|
||||
/bin/bash ./configure CONFIG_SHELL=/bin/bash
|
||||
|
||||
Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent
|
||||
configuration-related scripts to be executed by `/bin/bash'.
|
||||
|
||||
`configure' Invocation
|
||||
======================
|
||||
|
||||
`configure' recognizes the following options to control how it operates.
|
||||
|
||||
`--help'
|
||||
`-h'
|
||||
Print a summary of the options to `configure', and exit.
|
||||
|
||||
`--version'
|
||||
`-V'
|
||||
Print the version of Autoconf used to generate the `configure'
|
||||
script, and exit.
|
||||
|
||||
`--cache-file=FILE'
|
||||
Enable the cache: use and save the results of the tests in FILE,
|
||||
traditionally `config.cache'. FILE defaults to `/dev/null' to
|
||||
disable caching.
|
||||
|
||||
`--config-cache'
|
||||
`-C'
|
||||
Alias for `--cache-file=config.cache'.
|
||||
|
||||
`--quiet'
|
||||
`--silent'
|
||||
`-q'
|
||||
Do not print messages saying which checks are being made. To
|
||||
suppress all normal output, redirect it to `/dev/null' (any error
|
||||
messages will still be shown).
|
||||
|
||||
`--srcdir=DIR'
|
||||
Look for the package's source code in directory DIR. Usually
|
||||
`configure' can determine that directory automatically.
|
||||
|
||||
`configure' also accepts some other, not widely useful, options. Run
|
||||
`configure --help' for more details.
|
||||
|
||||
0
plugins/ladspa_effect/calf/NEWS
Normal file
0
plugins/ladspa_effect/calf/NEWS
Normal file
49
plugins/ladspa_effect/calf/README
Normal file
49
plugins/ladspa_effect/calf/README
Normal file
@@ -0,0 +1,49 @@
|
||||
Calf is a pack of audio plugins - effects and instruments, currently in
|
||||
development. The goal is to create a set of plugins using decent algorithms
|
||||
and parameter settings, available in a form which is compatible with as many
|
||||
open source applications as possible.
|
||||
|
||||
How to use Calf plugins:
|
||||
|
||||
* LADSPA plugins
|
||||
|
||||
Calf is installed as calf.so library in your LADSPA directory (typically
|
||||
/usr/lib/ladspa). It means that typical LADSPA host should be able to find
|
||||
Calf's plugins.
|
||||
|
||||
* DSSI plugins
|
||||
|
||||
Calf .so module is also installed in your DSSI plugin directory, which means
|
||||
your DSSI host (like jack-dssi-host or rosegarden) should find it and
|
||||
include its plugins in the plugin list.
|
||||
|
||||
* JACK client application
|
||||
|
||||
You can also use Calf plugins as separate applications, connecting to other
|
||||
applications using JACK Audio Connection Kit (version 0.103 or newer is
|
||||
required). To run the client, type:
|
||||
|
||||
calfjackhost monosynth !
|
||||
|
||||
(! means "connect", last "!" means "connect to output")
|
||||
|
||||
Other examples:
|
||||
|
||||
calfjackhost monosynth ! vintagedelay ! flanger !
|
||||
|
||||
(runs monosynth into vintagedelay and vintagedelay into flanger, then to
|
||||
output)
|
||||
|
||||
calfjackhost ! reverb !
|
||||
|
||||
(takes signal from system:capture_1 and _2, puts it through reverb, and then
|
||||
sends to system:playback_1 and _2)
|
||||
|
||||
You can also change client name or input/output port names with command-line
|
||||
options (type calfjackhost --help). Use qjackctl, patchage or jack_connect
|
||||
to connect the Calf JACK client to your sound card or other applications, if
|
||||
"!" is inadequate for any reason (if I didn't explain it properly, or if it
|
||||
doesn't provide the connectivity options needed).
|
||||
|
||||
Keep in mind this project is in the early development phase. It is usable
|
||||
for certain purposes, but drop me a note if you need something.
|
||||
40
plugins/ladspa_effect/calf/TODO
Normal file
40
plugins/ladspa_effect/calf/TODO
Normal file
@@ -0,0 +1,40 @@
|
||||
1. More effects
|
||||
|
||||
- auto-wah (might be integrated into filter)
|
||||
- envelope follower
|
||||
- better reverb (more features, use nested allpasses, use 1-pole
|
||||
1-zero allpass instead of fractional delays)
|
||||
- dynamics processing (Thor already did the compressor)
|
||||
- distortion?
|
||||
- windy rotary speakery stuff
|
||||
- filter: more types
|
||||
|
||||
2. Some instruments
|
||||
|
||||
- some virtual analogue thing (something larger than Monosynth)
|
||||
- FM (by reusing my MMX code, or something)
|
||||
|
||||
3. DSP library
|
||||
|
||||
- profiling framework
|
||||
- optimized code (the one I have now only pretends to be optimized :) )
|
||||
- underflow handling
|
||||
|
||||
4. Wrappers
|
||||
|
||||
- LADSPA: proper rdf (get clearance from drobilla ;) )
|
||||
- better jack host (controls etc)
|
||||
- BSE
|
||||
- buzztard
|
||||
- Linux VST
|
||||
- LV2
|
||||
Message Context (for Organ)
|
||||
EPP (the rest of them)
|
||||
Mixing Controls
|
||||
|
||||
5. Organization stuff (autotools etc)
|
||||
|
||||
- correct compilation and installation of LADSPA plugins (current version is a hack!)
|
||||
- switch to -O3
|
||||
- get to work on 64-bit architectures
|
||||
- i18n (gettext or whatever)
|
||||
790
plugins/ladspa_effect/calf/calf/audio_fx.h
Normal file
790
plugins/ladspa_effect/calf/calf/audio_fx.h
Normal file
@@ -0,0 +1,790 @@
|
||||
/* Calf DSP Library
|
||||
* Reusable audio effect classes.
|
||||
*
|
||||
* Copyright (C) 2001-2007 Krzysztof Foltman
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef __CALF_AUDIOFX_H
|
||||
#define __CALF_AUDIOFX_H
|
||||
|
||||
#include <complex>
|
||||
#include <iostream>
|
||||
#include <calf/biquad.h>
|
||||
#include <calf/onepole.h>
|
||||
#include "primitives.h"
|
||||
#include "delay.h"
|
||||
#include "fixed_point.h"
|
||||
#include "inertia.h"
|
||||
|
||||
namespace dsp {
|
||||
#if 0
|
||||
}; to keep editor happy
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Audio effect base class. Not really useful until it gets more developed.
|
||||
*/
|
||||
class audio_effect
|
||||
{
|
||||
public:
|
||||
virtual void setup(int sample_rate)=0;
|
||||
virtual ~audio_effect() {}
|
||||
};
|
||||
|
||||
class modulation_effect: public audio_effect
|
||||
{
|
||||
protected:
|
||||
int sample_rate;
|
||||
float rate, wet, dry, odsr;
|
||||
gain_smoothing gs_wet, gs_dry;
|
||||
public:
|
||||
fixed_point<unsigned int, 20> phase, dphase;
|
||||
float get_rate() {
|
||||
return rate;
|
||||
}
|
||||
void set_rate(float rate) {
|
||||
this->rate = rate;
|
||||
dphase = rate/sample_rate*4096;
|
||||
}
|
||||
float get_wet() {
|
||||
return wet;
|
||||
}
|
||||
void set_wet(float wet) {
|
||||
this->wet = wet;
|
||||
gs_wet.set_inertia(wet);
|
||||
}
|
||||
float get_dry() {
|
||||
return dry;
|
||||
}
|
||||
void set_dry(float dry) {
|
||||
this->dry = dry;
|
||||
gs_dry.set_inertia(dry);
|
||||
}
|
||||
void reset_phase(float req_phase)
|
||||
{
|
||||
phase = req_phase * 4096.0;
|
||||
}
|
||||
void inc_phase(float req_phase)
|
||||
{
|
||||
phase += fixed_point<unsigned int, 20>(req_phase * 4096.0);
|
||||
}
|
||||
void setup(int sample_rate)
|
||||
{
|
||||
this->sample_rate = sample_rate;
|
||||
this->odsr = 1.0 / sample_rate;
|
||||
phase = 0;
|
||||
set_rate(get_rate());
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A monophonic phaser. If you want stereo, combine two :)
|
||||
* Also, gave up on using template args for signal type.
|
||||
*/
|
||||
template<int MaxStages>
|
||||
class simple_phaser: public modulation_effect
|
||||
{
|
||||
protected:
|
||||
float base_frq, mod_depth, fb;
|
||||
float state;
|
||||
int cnt, stages;
|
||||
dsp::onepole<float, float> stage1;
|
||||
float x1[MaxStages], y1[MaxStages];
|
||||
public:
|
||||
simple_phaser()
|
||||
{
|
||||
set_base_frq(1000);
|
||||
set_mod_depth(1000);
|
||||
set_fb(0);
|
||||
state = 0;
|
||||
cnt = 0;
|
||||
stages = 0;
|
||||
set_stages(6);
|
||||
}
|
||||
float get_base_frq() {
|
||||
return base_frq;
|
||||
}
|
||||
void set_base_frq(float _base_frq) {
|
||||
base_frq = _base_frq;
|
||||
}
|
||||
int get_stages() {
|
||||
return stages;
|
||||
}
|
||||
void set_stages(int _stages) {
|
||||
if (_stages > stages)
|
||||
{
|
||||
for (int i = stages; i < _stages; i++)
|
||||
{
|
||||
x1[i] = x1[stages-1];
|
||||
y1[i] = y1[stages-1];
|
||||
}
|
||||
}
|
||||
stages = _stages;
|
||||
}
|
||||
float get_mod_depth() {
|
||||
return mod_depth;
|
||||
}
|
||||
void set_mod_depth(float _mod_depth) {
|
||||
mod_depth = _mod_depth;
|
||||
}
|
||||
float get_fb() {
|
||||
return fb;
|
||||
}
|
||||
void set_fb(float fb) {
|
||||
this->fb = fb;
|
||||
}
|
||||
virtual void setup(int sample_rate) {
|
||||
modulation_effect::setup(sample_rate);
|
||||
reset();
|
||||
}
|
||||
void reset()
|
||||
{
|
||||
cnt = 0;
|
||||
state = 0;
|
||||
phase.set(0);
|
||||
for (int i = 0; i < MaxStages; i++)
|
||||
x1[i] = y1[i] = 0;
|
||||
control_step();
|
||||
}
|
||||
inline void control_step()
|
||||
{
|
||||
cnt = 0;
|
||||
int v = phase.get() + 0x40000000;
|
||||
int sign = v >> 31;
|
||||
v ^= sign;
|
||||
// triangle wave, range from 0 to INT_MAX
|
||||
double vf = (double)((v >> 16) * (1.0 / 16384.0) - 1);
|
||||
|
||||
float freq = base_frq * pow(2.0, vf * mod_depth / 1200.0);
|
||||
freq = dsp::clip<float>(freq, 10.0, 0.49 * sample_rate);
|
||||
stage1.set_ap_w(freq * (M_PI / 2.0) * odsr);
|
||||
phase += dphase * 32;
|
||||
for (int i = 0; i < stages; i++)
|
||||
{
|
||||
dsp::sanitize(x1[i]);
|
||||
dsp::sanitize(y1[i]);
|
||||
}
|
||||
dsp::sanitize(state);
|
||||
}
|
||||
void process(float *buf_out, float *buf_in, int nsamples) {
|
||||
for (int i=0; i<nsamples; i++) {
|
||||
cnt++;
|
||||
if (cnt == 32)
|
||||
control_step();
|
||||
float in = *buf_in++;
|
||||
float fd = in + state * fb;
|
||||
for (int j = 0; j < stages; j++)
|
||||
fd = stage1.process_ap(fd, x1[j], y1[j]);
|
||||
state = fd;
|
||||
|
||||
float sdry = in * gs_dry.get();
|
||||
float swet = fd * gs_wet.get();
|
||||
*buf_out++ = sdry + swet;
|
||||
}
|
||||
}
|
||||
float freq_gain(float freq, float sr)
|
||||
{
|
||||
typedef std::complex<double> cfloat;
|
||||
freq *= 2.0 * M_PI / sr;
|
||||
cfloat z = 1.0 / exp(cfloat(0.0, freq)); // z^-1
|
||||
|
||||
cfloat p = cfloat(1.0);
|
||||
cfloat stg = stage1.h_z(z);
|
||||
|
||||
for (int i = 0; i < stages; i++)
|
||||
p = p * stg;
|
||||
|
||||
p = p / (cfloat(1.0) - cfloat(fb) * p);
|
||||
return std::abs(cfloat(gs_dry.get_last()) + cfloat(gs_wet.get_last()) * p);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Base class for chorus and flanger. Wouldn't be needed if it wasn't
|
||||
* for odd behaviour of GCC when deriving templates from template
|
||||
* base classes (not seeing fields from base classes!).
|
||||
*/
|
||||
class chorus_base: public modulation_effect
|
||||
{
|
||||
protected:
|
||||
int min_delay_samples, mod_depth_samples;
|
||||
float min_delay, mod_depth;
|
||||
sine_table<int, 4096, 65536> sine;
|
||||
public:
|
||||
float get_min_delay() {
|
||||
return min_delay;
|
||||
}
|
||||
void set_min_delay(float min_delay) {
|
||||
this->min_delay = min_delay;
|
||||
this->min_delay_samples = (int)(min_delay * 65536.0 * sample_rate);
|
||||
}
|
||||
float get_mod_depth() {
|
||||
return mod_depth;
|
||||
}
|
||||
void set_mod_depth(float mod_depth) {
|
||||
this->mod_depth = mod_depth;
|
||||
// 128 because it's then multiplied by (hopefully) a value of 32768..-32767
|
||||
this->mod_depth_samples = (int)(mod_depth * 32.0 * sample_rate);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Single-tap chorus without feedback.
|
||||
* Perhaps MaxDelay should be a bit longer!
|
||||
*/
|
||||
template<class T, int MaxDelay=512>
|
||||
class simple_chorus: public chorus_base
|
||||
{
|
||||
protected:
|
||||
simple_delay<MaxDelay,T> delay;
|
||||
public:
|
||||
simple_chorus() {
|
||||
rate = 0.63f;
|
||||
dry = 0.5f;
|
||||
wet = 0.5f;
|
||||
min_delay = 0.005f;
|
||||
mod_depth = 0.0025f;
|
||||
setup(44100);
|
||||
}
|
||||
void reset() {
|
||||
delay.reset();
|
||||
}
|
||||
virtual void setup(int sample_rate) {
|
||||
modulation_effect::setup(sample_rate);
|
||||
delay.reset();
|
||||
set_min_delay(get_min_delay());
|
||||
set_mod_depth(get_mod_depth());
|
||||
}
|
||||
template<class OutIter, class InIter>
|
||||
void process(OutIter buf_out, InIter buf_in, int nsamples) {
|
||||
int mds = min_delay_samples + mod_depth_samples * 1024 + 2*65536;
|
||||
int mdepth = mod_depth_samples;
|
||||
for (int i=0; i<nsamples; i++) {
|
||||
phase += dphase;
|
||||
unsigned int ipart = phase.ipart();
|
||||
|
||||
float in = *buf_in++;
|
||||
int lfo = phase.lerp_by_fract_int<int, 14, int>(sine.data[ipart], sine.data[ipart+1]);
|
||||
int v = mds + (mdepth * lfo >> 6);
|
||||
// if (!(i & 7)) printf("%d\n", v);
|
||||
int ifv = v >> 16;
|
||||
delay.put(in);
|
||||
T fd; // signal from delay's output
|
||||
delay.get_interp(fd, ifv, (v & 0xFFFF)*(1.0/65536.0));
|
||||
T sdry = in * gs_dry.get();
|
||||
T swet = fd * gs_wet.get();
|
||||
*buf_out++ = sdry + swet;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Single-tap flanger (chorus plus feedback).
|
||||
*/
|
||||
template<class T, int MaxDelay=1024>
|
||||
class simple_flanger: public chorus_base
|
||||
{
|
||||
protected:
|
||||
simple_delay<MaxDelay,T> delay;
|
||||
float fb;
|
||||
int last_delay_pos, last_actual_delay_pos;
|
||||
int ramp_pos, ramp_delay_pos;
|
||||
public:
|
||||
simple_flanger()
|
||||
: fb(0) {}
|
||||
void reset() {
|
||||
delay.reset();
|
||||
last_delay_pos = last_actual_delay_pos = ramp_delay_pos = 0;
|
||||
ramp_pos = 1024;
|
||||
}
|
||||
virtual void setup(int sample_rate) {
|
||||
this->sample_rate = sample_rate;
|
||||
this->odsr = 1.0 / sample_rate;
|
||||
delay.reset();
|
||||
phase = 0;
|
||||
set_rate(get_rate());
|
||||
set_min_delay(get_min_delay());
|
||||
}
|
||||
float get_fb() {
|
||||
return fb;
|
||||
}
|
||||
void set_fb(float fb) {
|
||||
this->fb = fb;
|
||||
}
|
||||
template<class OutIter, class InIter>
|
||||
void process(OutIter buf_out, InIter buf_in, int nsamples) {
|
||||
if (!nsamples)
|
||||
return;
|
||||
int mds = this->min_delay_samples + this->mod_depth_samples * 1024 + 2 * 65536;
|
||||
int mdepth = this->mod_depth_samples;
|
||||
int delay_pos;
|
||||
unsigned int ipart = this->phase.ipart();
|
||||
int lfo = phase.lerp_by_fract_int<int, 14, int>(this->sine.data[ipart], this->sine.data[ipart+1]);
|
||||
delay_pos = mds + (mdepth * lfo >> 6);
|
||||
|
||||
if (delay_pos != last_delay_pos || ramp_pos < 1024)
|
||||
{
|
||||
if (delay_pos != last_delay_pos) {
|
||||
// we need to ramp from what the delay tap length actually was,
|
||||
// not from old (ramp_delay_pos) or desired (delay_pos) tap length
|
||||
ramp_delay_pos = last_actual_delay_pos;
|
||||
ramp_pos = 0;
|
||||
}
|
||||
|
||||
int64_t dp = 0;
|
||||
for (int i=0; i<nsamples; i++) {
|
||||
float in = *buf_in++;
|
||||
T fd; // signal from delay's output
|
||||
dp = (((int64_t)ramp_delay_pos) * (1024 - ramp_pos) + ((int64_t)delay_pos) * ramp_pos) >> 10;
|
||||
ramp_pos++;
|
||||
if (ramp_pos > 1024) ramp_pos = 1024;
|
||||
this->delay.get_interp(fd, dp >> 16, (dp & 0xFFFF)*(1.0/65536.0));
|
||||
sanitize(fd);
|
||||
T sdry = in * this->dry;
|
||||
T swet = fd * this->wet;
|
||||
*buf_out++ = sdry + swet;
|
||||
this->delay.put(in+fb*fd);
|
||||
|
||||
this->phase += this->dphase;
|
||||
ipart = this->phase.ipart();
|
||||
lfo = phase.lerp_by_fract_int<int, 14, int>(this->sine.data[ipart], this->sine.data[ipart+1]);
|
||||
delay_pos = mds + (mdepth * lfo >> 6);
|
||||
}
|
||||
last_actual_delay_pos = dp;
|
||||
}
|
||||
else {
|
||||
for (int i=0; i<nsamples; i++) {
|
||||
float in = *buf_in++;
|
||||
T fd; // signal from delay's output
|
||||
this->delay.get_interp(fd, delay_pos >> 16, (delay_pos & 0xFFFF)*(1.0/65536.0));
|
||||
sanitize(fd);
|
||||
T sdry = in * this->gs_dry.get();
|
||||
T swet = fd * this->gs_wet.get();
|
||||
*buf_out++ = sdry + swet;
|
||||
this->delay.put(in+fb*fd);
|
||||
|
||||
this->phase += this->dphase;
|
||||
ipart = this->phase.ipart();
|
||||
lfo = phase.lerp_by_fract_int<int, 14, int>(this->sine.data[ipart], this->sine.data[ipart+1]);
|
||||
delay_pos = mds + (mdepth * lfo >> 6);
|
||||
}
|
||||
last_actual_delay_pos = delay_pos;
|
||||
}
|
||||
last_delay_pos = delay_pos;
|
||||
}
|
||||
float freq_gain(float freq, float sr)
|
||||
{
|
||||
typedef std::complex<double> cfloat;
|
||||
freq *= 2.0 * M_PI / sr;
|
||||
cfloat z = 1.0 / exp(cfloat(0.0, freq)); // z^-1
|
||||
|
||||
float ldp = last_delay_pos / 65536.0;
|
||||
float fldp = floor(ldp);
|
||||
cfloat zn = std::pow(z, fldp); // z^-N
|
||||
cfloat zn1 = zn * z; // z^-(N+1)
|
||||
// simulate a lerped comb filter - H(z) = 1 / (1 + fb * (lerp(z^-N, z^-(N+1), fracpos))), N = int(pos), fracpos = pos - int(pos)
|
||||
cfloat delayed = zn + (zn1 - zn) * cfloat(ldp - fldp);
|
||||
cfloat h = cfloat(delayed) / (cfloat(1.0) - cfloat(fb) * delayed);
|
||||
// mix with dry signal
|
||||
float v = std::abs(cfloat(gs_dry.get_last()) + cfloat(gs_wet.get_last()) * h);
|
||||
return v;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A classic allpass loop reverb with modulated allpass filter.
|
||||
* Just started implementing it, so there is no control over many
|
||||
* parameters.
|
||||
*/
|
||||
template<class T>
|
||||
class reverb: public audio_effect
|
||||
{
|
||||
simple_delay<2048, T> apL1, apL2, apL3, apL4, apL5, apL6;
|
||||
simple_delay<2048, T> apR1, apR2, apR3, apR4, apR5, apR6;
|
||||
fixed_point<unsigned int, 25> phase, dphase;
|
||||
sine_table<int, 128, 10000> sine;
|
||||
onepole<T> lp_left, lp_right;
|
||||
T old_left, old_right;
|
||||
int type;
|
||||
float time, fb, cutoff, diffusion;
|
||||
int tl[6], tr[6];
|
||||
float ldec[6], rdec[6];
|
||||
|
||||
int sr;
|
||||
public:
|
||||
reverb()
|
||||
{
|
||||
phase = 0.0;
|
||||
time = 1.0;
|
||||
cutoff = 9000;
|
||||
type = 2;
|
||||
diffusion = 1.f;
|
||||
setup(44100);
|
||||
}
|
||||
virtual void setup(int sample_rate) {
|
||||
sr = sample_rate;
|
||||
set_time(time);
|
||||
set_cutoff(cutoff);
|
||||
phase = 0.0;
|
||||
dphase = 0.5*128/sr;
|
||||
update_times();
|
||||
}
|
||||
void update_times()
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case 0:
|
||||
tl[0] = 397 << 16, tr[0] = 383 << 16;
|
||||
tl[1] = 457 << 16, tr[1] = 429 << 16;
|
||||
tl[2] = 549 << 16, tr[2] = 631 << 16;
|
||||
tl[3] = 649 << 16, tr[3] = 756 << 16;
|
||||
tl[4] = 773 << 16, tr[4] = 803 << 16;
|
||||
tl[5] = 877 << 16, tr[5] = 901 << 16;
|
||||
break;
|
||||
case 1:
|
||||
tl[0] = 697 << 16, tr[0] = 783 << 16;
|
||||
tl[1] = 957 << 16, tr[1] = 929 << 16;
|
||||
tl[2] = 649 << 16, tr[2] = 531 << 16;
|
||||
tl[3] = 1049 << 16, tr[3] = 1177 << 16;
|
||||
tl[4] = 473 << 16, tr[4] = 501 << 16;
|
||||
tl[5] = 587 << 16, tr[5] = 681 << 16;
|
||||
break;
|
||||
case 2:
|
||||
default:
|
||||
tl[0] = 697 << 16, tr[0] = 783 << 16;
|
||||
tl[1] = 957 << 16, tr[1] = 929 << 16;
|
||||
tl[2] = 649 << 16, tr[2] = 531 << 16;
|
||||
tl[3] = 1249 << 16, tr[3] = 1377 << 16;
|
||||
tl[4] = 1573 << 16, tr[4] = 1671 << 16;
|
||||
tl[5] = 1877 << 16, tr[5] = 1781 << 16;
|
||||
break;
|
||||
case 3:
|
||||
tl[0] = 1097 << 16, tr[0] = 1087 << 16;
|
||||
tl[1] = 1057 << 16, tr[1] = 1031 << 16;
|
||||
tl[2] = 1049 << 16, tr[2] = 1039 << 16;
|
||||
tl[3] = 1083 << 16, tr[3] = 1055 << 16;
|
||||
tl[4] = 1075 << 16, tr[4] = 1099 << 16;
|
||||
tl[5] = 1003 << 16, tr[5] = 1073 << 16;
|
||||
break;
|
||||
case 4:
|
||||
tl[0] = 197 << 16, tr[0] = 133 << 16;
|
||||
tl[1] = 357 << 16, tr[1] = 229 << 16;
|
||||
tl[2] = 549 << 16, tr[2] = 431 << 16;
|
||||
tl[3] = 949 << 16, tr[3] = 1277 << 16;
|
||||
tl[4] = 1173 << 16, tr[4] = 1671 << 16;
|
||||
tl[5] = 1477 << 16, tr[5] = 1881 << 16;
|
||||
break;
|
||||
case 5:
|
||||
tl[0] = 197 << 16, tr[0] = 133 << 16;
|
||||
tl[1] = 257 << 16, tr[1] = 179 << 16;
|
||||
tl[2] = 549 << 16, tr[2] = 431 << 16;
|
||||
tl[3] = 619 << 16, tr[3] = 497 << 16;
|
||||
tl[4] = 1173 << 16, tr[4] = 1371 << 16;
|
||||
tl[5] = 1577 << 16, tr[5] = 1881 << 16;
|
||||
break;
|
||||
}
|
||||
|
||||
float fDec=1000 + 2400.f * diffusion;
|
||||
for (int i = 0 ; i < 6; i++) {
|
||||
ldec[i]=exp(-float(tl[i] >> 16) / fDec),
|
||||
rdec[i]=exp(-float(tr[i] >> 16) / fDec);
|
||||
}
|
||||
}
|
||||
float get_time() {
|
||||
return time;
|
||||
}
|
||||
void set_time(float time) {
|
||||
this->time = time;
|
||||
// fb = pow(1.0f/4096.0f, (float)(1700/(time*sr)));
|
||||
fb = 1.0 - 0.3 / (time * sr / 44100.0);
|
||||
}
|
||||
float get_type() {
|
||||
return type;
|
||||
}
|
||||
void set_type(int type) {
|
||||
this->type = type;
|
||||
update_times();
|
||||
}
|
||||
float get_diffusion() {
|
||||
return diffusion;
|
||||
}
|
||||
void set_diffusion(float diffusion) {
|
||||
this->diffusion = diffusion;
|
||||
update_times();
|
||||
}
|
||||
void set_type_and_diffusion(int type, float diffusion) {
|
||||
this->type = type;
|
||||
this->diffusion = diffusion;
|
||||
update_times();
|
||||
}
|
||||
float get_fb()
|
||||
{
|
||||
return this->fb;
|
||||
}
|
||||
void set_fb(float fb)
|
||||
{
|
||||
this->fb = fb;
|
||||
}
|
||||
float get_cutoff() {
|
||||
return cutoff;
|
||||
}
|
||||
void set_cutoff(float cutoff) {
|
||||
this->cutoff = cutoff;
|
||||
lp_left.set_lp(cutoff,sr);
|
||||
lp_right.set_lp(cutoff,sr);
|
||||
}
|
||||
void reset()
|
||||
{
|
||||
apL1.reset();apR1.reset();
|
||||
apL2.reset();apR2.reset();
|
||||
apL3.reset();apR3.reset();
|
||||
apL4.reset();apR4.reset();
|
||||
apL5.reset();apR5.reset();
|
||||
apL6.reset();apR6.reset();
|
||||
lp_left.reset();lp_right.reset();
|
||||
old_left = 0; old_right = 0;
|
||||
}
|
||||
void process(T &left, T &right)
|
||||
{
|
||||
unsigned int ipart = phase.ipart();
|
||||
|
||||
// the interpolated LFO might be an overkill here
|
||||
int lfo = phase.lerp_by_fract_int<int, 14, int>(sine.data[ipart], sine.data[ipart+1]) >> 2;
|
||||
phase += dphase;
|
||||
|
||||
left += old_right;
|
||||
left = apL1.process_allpass_comb_lerp16(left, tl[0] - 45*lfo, ldec[0]);
|
||||
left = apL2.process_allpass_comb_lerp16(left, tl[1] + 47*lfo, ldec[1]);
|
||||
float out_left = left;
|
||||
left = apL3.process_allpass_comb_lerp16(left, tl[2] + 54*lfo, ldec[2]);
|
||||
left = apL4.process_allpass_comb_lerp16(left, tl[3] - 69*lfo, ldec[3]);
|
||||
left = apL5.process_allpass_comb_lerp16(left, tl[4] + 69*lfo, ldec[4]);
|
||||
left = apL6.process_allpass_comb_lerp16(left, tl[5] - 46*lfo, ldec[5]);
|
||||
old_left = lp_left.process(left * fb);
|
||||
sanitize(old_left);
|
||||
|
||||
right += old_left;
|
||||
right = apR1.process_allpass_comb_lerp16(right, tr[0] - 45*lfo, rdec[0]);
|
||||
right = apR2.process_allpass_comb_lerp16(right, tr[1] + 47*lfo, rdec[1]);
|
||||
float out_right = right;
|
||||
right = apR3.process_allpass_comb_lerp16(right, tr[2] + 54*lfo, rdec[2]);
|
||||
right = apR4.process_allpass_comb_lerp16(right, tr[3] - 69*lfo, rdec[3]);
|
||||
right = apR5.process_allpass_comb_lerp16(right, tr[4] + 69*lfo, rdec[4]);
|
||||
right = apR6.process_allpass_comb_lerp16(right, tr[5] - 46*lfo, rdec[5]);
|
||||
old_right = lp_right.process(right * fb);
|
||||
sanitize(old_right);
|
||||
|
||||
left = out_left, right = out_right;
|
||||
}
|
||||
void extra_sanitize()
|
||||
{
|
||||
lp_left.sanitize();
|
||||
lp_right.sanitize();
|
||||
}
|
||||
};
|
||||
|
||||
class filter_module_iface
|
||||
{
|
||||
public:
|
||||
virtual void calculate_filter(float freq, float q, int mode, float gain = 1.0) = 0;
|
||||
virtual void filter_activate() = 0;
|
||||
virtual void sanitize() = 0;
|
||||
virtual int process_channel(uint16_t channel_no, float *in, float *out, uint32_t numsamples, int inmask) = 0;
|
||||
virtual float freq_gain(int subindex, float freq, float srate) = 0;
|
||||
|
||||
virtual ~filter_module_iface() {}
|
||||
};
|
||||
|
||||
|
||||
class biquad_filter_module: public filter_module_iface
|
||||
{
|
||||
private:
|
||||
dsp::biquad_d1<float> left[3], right[3];
|
||||
int order;
|
||||
|
||||
public:
|
||||
uint32_t srate;
|
||||
|
||||
enum { mode_12db_lp = 0, mode_24db_lp = 1, mode_36db_lp = 2,
|
||||
mode_12db_hp = 3, mode_24db_hp = 4, mode_36db_hp = 5,
|
||||
mode_6db_bp = 6, mode_12db_bp = 7, mode_18db_bp = 8,
|
||||
mode_6db_br = 9, mode_12db_br = 10, mode_18db_br = 11,
|
||||
mode_count
|
||||
};
|
||||
|
||||
public:
|
||||
biquad_filter_module() : order(0) {}
|
||||
|
||||
void calculate_filter(float freq, float q, int mode, float gain = 1.0)
|
||||
{
|
||||
if (mode <= mode_36db_lp) {
|
||||
order = mode + 1;
|
||||
left[0].set_lp_rbj(freq, pow(q, 1.0 / order), srate, gain);
|
||||
} else if ( mode_12db_hp <= mode && mode <= mode_36db_hp ) {
|
||||
order = mode - mode_12db_hp + 1;
|
||||
left[0].set_hp_rbj(freq, pow(q, 1.0 / order), srate, gain);
|
||||
} else if ( mode_6db_bp <= mode && mode <= mode_18db_bp ) {
|
||||
order = mode - mode_6db_bp + 1;
|
||||
left[0].set_bp_rbj(freq, pow(q, 1.0 / order), srate, gain);
|
||||
} else { // mode_6db_br <= mode <= mode_18db_br
|
||||
order = mode - mode_6db_br + 1;
|
||||
left[0].set_br_rbj(freq, order * 0.1 * q, srate, gain);
|
||||
}
|
||||
|
||||
right[0].copy_coeffs(left[0]);
|
||||
for (int i = 1; i < order; i++) {
|
||||
left[i].copy_coeffs(left[0]);
|
||||
right[i].copy_coeffs(left[0]);
|
||||
}
|
||||
}
|
||||
|
||||
void filter_activate()
|
||||
{
|
||||
for (int i=0; i < order; i++) {
|
||||
left[i].reset();
|
||||
right[i].reset();
|
||||
}
|
||||
}
|
||||
|
||||
void sanitize()
|
||||
{
|
||||
for (int i=0; i < order; i++) {
|
||||
left[i].sanitize();
|
||||
right[i].sanitize();
|
||||
}
|
||||
}
|
||||
|
||||
inline int process_channel(uint16_t channel_no, float *in, float *out, uint32_t numsamples, int inmask) {
|
||||
dsp::biquad_d1<float> *filter;
|
||||
switch (channel_no) {
|
||||
case 0:
|
||||
filter = left;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
filter = right;
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (inmask) {
|
||||
switch(order) {
|
||||
case 1:
|
||||
for (uint32_t i = 0; i < numsamples; i++)
|
||||
out[i] = filter[0].process(in[i]);
|
||||
break;
|
||||
case 2:
|
||||
for (uint32_t i = 0; i < numsamples; i++)
|
||||
out[i] = filter[1].process(filter[0].process(in[i]));
|
||||
break;
|
||||
case 3:
|
||||
for (uint32_t i = 0; i < numsamples; i++)
|
||||
out[i] = filter[2].process(filter[1].process(filter[0].process(in[i])));
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (filter[order - 1].empty())
|
||||
return 0;
|
||||
switch(order) {
|
||||
case 1:
|
||||
for (uint32_t i = 0; i < numsamples; i++)
|
||||
out[i] = filter[0].process_zeroin();
|
||||
break;
|
||||
case 2:
|
||||
if (filter[0].empty())
|
||||
for (uint32_t i = 0; i < numsamples; i++)
|
||||
out[i] = filter[1].process_zeroin();
|
||||
else
|
||||
for (uint32_t i = 0; i < numsamples; i++)
|
||||
out[i] = filter[1].process(filter[0].process_zeroin());
|
||||
break;
|
||||
case 3:
|
||||
if (filter[1].empty())
|
||||
for (uint32_t i = 0; i < numsamples; i++)
|
||||
out[i] = filter[2].process_zeroin();
|
||||
else
|
||||
for (uint32_t i = 0; i < numsamples; i++)
|
||||
out[i] = filter[2].process(filter[1].process(filter[0].process_zeroin()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < order; i++)
|
||||
filter[i].sanitize();
|
||||
return filter[order - 1].empty() ? 0 : inmask;
|
||||
}
|
||||
|
||||
float freq_gain(int subindex, float freq, float srate)
|
||||
{
|
||||
float level = 1.0;
|
||||
for (int j = 0; j < order; j++)
|
||||
level *= left[j].freq_gain(freq, srate);
|
||||
return level;
|
||||
}
|
||||
};
|
||||
|
||||
class two_band_eq
|
||||
{
|
||||
private:
|
||||
dsp::onepole<float> lowcut, highcut;
|
||||
float low_gain, high_gain;
|
||||
|
||||
public:
|
||||
void reset()
|
||||
{
|
||||
lowcut.reset();
|
||||
highcut.reset();
|
||||
}
|
||||
|
||||
inline float process(float v)
|
||||
{
|
||||
v = dsp::lerp(lowcut.process_hp(v), v, low_gain);
|
||||
v = dsp::lerp(highcut.process_lp(v), v, high_gain);
|
||||
return v;
|
||||
}
|
||||
|
||||
inline void copy_coeffs(const two_band_eq &src)
|
||||
{
|
||||
lowcut.copy_coeffs(src.lowcut);
|
||||
highcut.copy_coeffs(src.highcut);
|
||||
low_gain = src.low_gain;
|
||||
high_gain = src.high_gain;
|
||||
}
|
||||
|
||||
void sanitize()
|
||||
{
|
||||
lowcut.sanitize();
|
||||
highcut.sanitize();
|
||||
}
|
||||
|
||||
void set(float _low_freq, float _low_gain, float _high_freq, float _high_gain, float sr)
|
||||
{
|
||||
lowcut.set_hp(_low_freq, sr);
|
||||
highcut.set_lp(_high_freq, sr);
|
||||
low_gain = _low_gain;
|
||||
high_gain = _high_gain;
|
||||
}
|
||||
};
|
||||
|
||||
#if 0
|
||||
{ to keep editor happy
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
580
plugins/ladspa_effect/calf/calf/biquad.h
Normal file
580
plugins/ladspa_effect/calf/calf/biquad.h
Normal file
@@ -0,0 +1,580 @@
|
||||
/* Calf DSP Library
|
||||
* Biquad filters
|
||||
* Copyright (C) 2001-2007 Krzysztof Foltman
|
||||
*
|
||||
* Most of code in this file is based on freely
|
||||
* available other work of other people (filter equations).
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef __CALF_BIQUAD_H
|
||||
#define __CALF_BIQUAD_H
|
||||
|
||||
#include <complex>
|
||||
#include "primitives.h"
|
||||
|
||||
namespace dsp {
|
||||
|
||||
/**
|
||||
* Coefficients for two-pole two-zero filter, for floating point values,
|
||||
* plus a bunch of functions to set them to typical values.
|
||||
*
|
||||
* Coefficient calculation is based on famous Robert Bristow-Johnson's equations,
|
||||
* except where it's not.
|
||||
* The coefficient calculation is NOT mine, the only exception is the lossy
|
||||
* optimization in Zoelzer and rbj HP filter code.
|
||||
*
|
||||
* See http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt for reference.
|
||||
*
|
||||
* don't use this for integers because it won't work
|
||||
*/
|
||||
template<class Coeff = float>
|
||||
class biquad_coeffs
|
||||
{
|
||||
public:
|
||||
// filter coefficients
|
||||
Coeff a0, a1, a2, b1, b2;
|
||||
typedef std::complex<double> cfloat;
|
||||
|
||||
biquad_coeffs()
|
||||
{
|
||||
set_null();
|
||||
}
|
||||
|
||||
inline void set_null()
|
||||
{
|
||||
a0 = 1.0;
|
||||
b1 = b2 = a1 = a2 = 0.f;
|
||||
}
|
||||
|
||||
/** Lowpass filter based on Robert Bristow-Johnson's equations
|
||||
* Perhaps every synth code that doesn't use SVF uses these
|
||||
* equations :)
|
||||
* @param fc resonant frequency
|
||||
* @param q resonance (gain at fc)
|
||||
* @param sr sample rate
|
||||
* @param gain amplification (gain at 0Hz)
|
||||
*/
|
||||
inline void set_lp_rbj(float fc, float q, float sr, float gain = 1.0)
|
||||
{
|
||||
float omega=(float)(2*M_PI*fc/sr);
|
||||
float sn=sin(omega);
|
||||
float cs=cos(omega);
|
||||
float alpha=(float)(sn/(2*q));
|
||||
float inv=(float)(1.0/(1.0+alpha));
|
||||
|
||||
a2 = a0 = (float)(gain*inv*(1 - cs)*0.5f);
|
||||
a1 = a0 + a0;
|
||||
b1 = (float)(-2*cs*inv);
|
||||
b2 = (float)((1 - alpha)*inv);
|
||||
}
|
||||
|
||||
// different lowpass filter, based on Zoelzer's equations, modified by
|
||||
// me (kfoltman) to use polynomials to approximate tangent function
|
||||
// not very accurate, but perhaps good enough for synth work :)
|
||||
// odsr is "one divided by samplerate"
|
||||
// from how it looks, it perhaps uses bilinear transform - but who knows :)
|
||||
inline void set_lp_zoelzer(float fc, float q, float odsr, float gain=1.0)
|
||||
{
|
||||
Coeff omega=(Coeff)(M_PI*fc*odsr);
|
||||
Coeff omega2=omega*omega;
|
||||
Coeff K=omega*(1+omega2*omega2*Coeff(1.0/1.45));
|
||||
Coeff KK=K*K;
|
||||
Coeff QK=q*(KK+1.f);
|
||||
Coeff iQK=1.0f/(QK+K);
|
||||
Coeff inv=q*iQK;
|
||||
b2 = (Coeff)(iQK*(QK-K));
|
||||
b1 = (Coeff)(2.f*(KK-1.f)*inv);
|
||||
a2 = a0 = (Coeff)(inv*gain*KK);
|
||||
a1 = a0 + a0;
|
||||
}
|
||||
|
||||
/** Highpass filter based on Robert Bristow-Johnson's equations
|
||||
* @param fc resonant frequency
|
||||
* @param q resonance (gain at fc)
|
||||
* @param sr sample rate
|
||||
* @param gain amplification (gain at sr/2)
|
||||
*/
|
||||
inline void set_hp_rbj(float fc, float q, float esr, float gain=1.0)
|
||||
{
|
||||
Coeff omega=(float)(2*M_PI*fc/esr);
|
||||
Coeff sn=sin(omega);
|
||||
Coeff cs=cos(omega);
|
||||
Coeff alpha=(float)(sn/(2*q));
|
||||
|
||||
float inv=(float)(1.0/(1.0+alpha));
|
||||
|
||||
a0 = (Coeff)(gain*inv*(1 + cs)/2);
|
||||
a1 = -2.f * a0;
|
||||
a2 = a0;
|
||||
b1 = (Coeff)(-2*cs*inv);
|
||||
b2 = (Coeff)((1 - alpha)*inv);
|
||||
}
|
||||
|
||||
// this replaces sin/cos with polynomial approximation
|
||||
inline void set_hp_rbj_optimized(float fc, float q, float esr, float gain=1.0)
|
||||
{
|
||||
Coeff omega=(float)(2*M_PI*fc/esr);
|
||||
Coeff sn=omega+omega*omega*omega*(1.0/6.0)+omega*omega*omega*omega*omega*(1.0/120);
|
||||
Coeff cs=1-omega*omega*(1.0/2.0)+omega*omega*omega*omega*(1.0/24);
|
||||
Coeff alpha=(float)(sn/(2*q));
|
||||
|
||||
float inv=(float)(1.0/(1.0+alpha));
|
||||
|
||||
a0 = (Coeff)(gain*inv*(1 + cs)*(1.0/2.0));
|
||||
a1 = -2.f * a0;
|
||||
a2 = a0;
|
||||
b1 = (Coeff)(-2*cs*inv);
|
||||
b2 = (Coeff)((1 - alpha)*inv);
|
||||
}
|
||||
|
||||
/** Bandpass filter based on Robert Bristow-Johnson's equations (normalized to 1.0 at center frequency)
|
||||
* @param fc center frequency (gain at fc = 1.0)
|
||||
* @param q =~ fc/bandwidth (not quite, but close) - 1/Q = 2*sinh(ln(2)/2*BW*w0/sin(w0))
|
||||
* @param sr sample rate
|
||||
* @param gain amplification (gain at sr/2)
|
||||
*/
|
||||
inline void set_bp_rbj(double fc, double q, double esr, double gain=1.0)
|
||||
{
|
||||
float omega=(float)(2*M_PI*fc/esr);
|
||||
float sn=sin(omega);
|
||||
float cs=cos(omega);
|
||||
float alpha=(float)(sn/(2*q));
|
||||
|
||||
float inv=(float)(1.0/(1.0+alpha));
|
||||
|
||||
a0 = (float)(gain*inv*alpha);
|
||||
a1 = 0.f;
|
||||
a2 = (float)(-gain*inv*alpha);
|
||||
b1 = (float)(-2*cs*inv);
|
||||
b2 = (float)((1 - alpha)*inv);
|
||||
}
|
||||
|
||||
// rbj's bandreject
|
||||
inline void set_br_rbj(double fc, double q, double esr, double gain=1.0)
|
||||
{
|
||||
float omega=(float)(2*M_PI*fc/esr);
|
||||
float sn=sin(omega);
|
||||
float cs=cos(omega);
|
||||
float alpha=(float)(sn/(2*q));
|
||||
|
||||
float inv=(float)(1.0/(1.0+alpha));
|
||||
|
||||
a0 = (Coeff)(gain*inv);
|
||||
a1 = (Coeff)(-gain*inv*2*cs);
|
||||
a2 = (Coeff)(gain*inv);
|
||||
b1 = (Coeff)(-2*cs*inv);
|
||||
b2 = (Coeff)((1 - alpha)*inv);
|
||||
}
|
||||
// this is mine (and, I guess, it sucks/doesn't work)
|
||||
void set_allpass(float freq, float pole_r, float sr)
|
||||
{
|
||||
float a=prewarp(freq, sr);
|
||||
float q=pole_r;
|
||||
set_bilinear(a*a+q*q, -2.0f*a, 1, a*a+q*q, 2.0f*a, 1);
|
||||
}
|
||||
/// prewarping for bilinear transform, maps given digital frequency to analog counterpart for analog filter design
|
||||
static inline float prewarp(float freq, float sr)
|
||||
{
|
||||
if (freq>sr*0.49) freq=(float)(sr*0.49);
|
||||
return (float)(tan(M_PI*freq/sr));
|
||||
}
|
||||
/// convert analog angular frequency value to digital
|
||||
static inline float unwarp(float omega, float sr)
|
||||
{
|
||||
float T = 1.0 / sr;
|
||||
return (2 / T) * atan(omega * T / 2);
|
||||
}
|
||||
/// convert analog filter time constant to digital counterpart
|
||||
static inline float unwarpf(float t, float sr)
|
||||
{
|
||||
// this is most likely broken and works by pure accident!
|
||||
float omega = 1.0 / t;
|
||||
omega = unwarp(omega, sr);
|
||||
// I really don't know why does it have to be M_PI and not 2 * M_PI!
|
||||
float f = M_PI / omega;
|
||||
return f / sr;
|
||||
}
|
||||
/// set digital filter parameters based on given analog filter parameters
|
||||
void set_bilinear(float aa0, float aa1, float aa2, float ab0, float ab1, float ab2)
|
||||
{
|
||||
float q=(float)(1.0/(ab0+ab1+ab2));
|
||||
a0 = (aa0+aa1+aa2)*q;
|
||||
a1 = 2*(aa0-aa2)*q;
|
||||
a2 = (aa0-aa1+aa2)*q;
|
||||
b1 = 2*(ab0-ab2)*q;
|
||||
b2 = (ab0-ab1+ab2)*q;
|
||||
}
|
||||
|
||||
/// RBJ peaking EQ
|
||||
/// @param freq peak frequency
|
||||
/// @param q q (correlated to freq/bandwidth, @see set_bp_rbj)
|
||||
/// @param peak peak gain (1.0 means no peak, >1.0 means a peak, less than 1.0 is a dip)
|
||||
inline void set_peakeq_rbj(float freq, float q, float peak, float sr)
|
||||
{
|
||||
float A = sqrt(peak);
|
||||
float w0 = freq * 2 * M_PI * (1.0 / sr);
|
||||
float alpha = sin(w0) / (2 * q);
|
||||
float ib0 = 1.0 / (1 + alpha/A);
|
||||
a1 = b1 = -2*cos(w0) * ib0;
|
||||
a0 = ib0 * (1 + alpha*A);
|
||||
a2 = ib0 * (1 - alpha*A);
|
||||
b2 = ib0 * (1 - alpha/A);
|
||||
}
|
||||
|
||||
/// RBJ low shelf EQ - amplitication of 'peak' at 0 Hz and of 1.0 (0dB) at sr/2 Hz
|
||||
/// @param freq corner frequency (gain at freq is sqrt(peak))
|
||||
/// @param q q (relates bandwidth and peak frequency), the higher q, the louder the resonant peak (situated below fc) is
|
||||
/// @param peak shelf gain (1.0 means no peak, >1.0 means a peak, less than 1.0 is a dip)
|
||||
inline void set_lowshelf_rbj(float freq, float q, float peak, float sr)
|
||||
{
|
||||
float A = sqrt(peak);
|
||||
float w0 = freq * 2 * M_PI * (1.0 / sr);
|
||||
float alpha = sin(w0) / (2 * q);
|
||||
float cw0 = cos(w0);
|
||||
float tmp = 2 * sqrt(A) * alpha;
|
||||
float b0 = 0.f, ib0 = 0.f;
|
||||
|
||||
a0 = A*( (A+1) - (A-1)*cw0 + tmp);
|
||||
a1 = 2*A*( (A-1) - (A+1)*cw0);
|
||||
a2 = A*( (A+1) - (A-1)*cw0 - tmp);
|
||||
b0 = (A+1) + (A-1)*cw0 + tmp;
|
||||
b1 = -2*( (A-1) + (A+1)*cw0);
|
||||
b2 = (A+1) + (A-1)*cw0 - tmp;
|
||||
|
||||
ib0 = 1.0 / b0;
|
||||
b1 *= ib0;
|
||||
b2 *= ib0;
|
||||
a0 *= ib0;
|
||||
a1 *= ib0;
|
||||
a2 *= ib0;
|
||||
}
|
||||
|
||||
/// RBJ high shelf EQ - amplitication of 0dB at 0 Hz and of peak at sr/2 Hz
|
||||
/// @param freq corner frequency (gain at freq is sqrt(peak))
|
||||
/// @param q q (relates bandwidth and peak frequency), the higher q, the louder the resonant peak (situated above fc) is
|
||||
/// @param peak shelf gain (1.0 means no peak, >1.0 means a peak, less than 1.0 is a dip)
|
||||
inline void set_highshelf_rbj(float freq, float q, float peak, float sr)
|
||||
{
|
||||
float A = sqrt(peak);
|
||||
float w0 = freq * 2 * M_PI * (1.0 / sr);
|
||||
float alpha = sin(w0) / (2 * q);
|
||||
float cw0 = cos(w0);
|
||||
float tmp = 2 * sqrt(A) * alpha;
|
||||
float b0 = 0.f, ib0 = 0.f;
|
||||
|
||||
a0 = A*( (A+1) + (A-1)*cw0 + tmp);
|
||||
a1 = -2*A*( (A-1) + (A+1)*cw0);
|
||||
a2 = A*( (A+1) + (A-1)*cw0 - tmp);
|
||||
b0 = (A+1) - (A-1)*cw0 + tmp;
|
||||
b1 = 2*( (A-1) - (A+1)*cw0);
|
||||
b2 = (A+1) - (A-1)*cw0 - tmp;
|
||||
|
||||
ib0 = 1.0 / b0;
|
||||
b1 *= ib0;
|
||||
b2 *= ib0;
|
||||
a0 *= ib0;
|
||||
a1 *= ib0;
|
||||
a2 *= ib0;
|
||||
}
|
||||
|
||||
/// copy coefficients from another biquad
|
||||
template<class U>
|
||||
inline void copy_coeffs(const biquad_coeffs<U> &src)
|
||||
{
|
||||
a0 = src.a0;
|
||||
a1 = src.a1;
|
||||
a2 = src.a2;
|
||||
b1 = src.b1;
|
||||
b2 = src.b2;
|
||||
}
|
||||
|
||||
/// Return the filter's gain at frequency freq
|
||||
/// @param freq Frequency to look up
|
||||
/// @param sr Filter sample rate (used to convert frequency to angular frequency)
|
||||
float freq_gain(float freq, float sr)
|
||||
{
|
||||
typedef std::complex<double> cfloat;
|
||||
freq *= 2.0 * M_PI / sr;
|
||||
cfloat z = 1.0 / exp(cfloat(0.0, freq));
|
||||
|
||||
return std::abs(h_z(z));
|
||||
}
|
||||
|
||||
/// Return H(z) the filter's gain at frequency freq
|
||||
/// @param z Z variable (e^jw)
|
||||
cfloat h_z(const cfloat &z)
|
||||
{
|
||||
|
||||
return (cfloat(a0) + double(a1) * z + double(a2) * z*z) / (cfloat(1.0) + double(b1) * z + double(b2) * z*z);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Two-pole two-zero filter, for floating point values.
|
||||
* Uses "traditional" Direct I form (separate FIR and IIR halves).
|
||||
* don't use this for integers because it won't work
|
||||
*/
|
||||
template<class Coeff = float, class T = float>
|
||||
struct biquad_d1: public biquad_coeffs<Coeff>
|
||||
{
|
||||
using biquad_coeffs<Coeff>::a0;
|
||||
using biquad_coeffs<Coeff>::a1;
|
||||
using biquad_coeffs<Coeff>::a2;
|
||||
using biquad_coeffs<Coeff>::b1;
|
||||
using biquad_coeffs<Coeff>::b2;
|
||||
/// input[n-1]
|
||||
T x1;
|
||||
/// input[n-2]
|
||||
T x2;
|
||||
/// output[n-1]
|
||||
T y1;
|
||||
/// output[n-2]
|
||||
T y2;
|
||||
/// Constructor (initializes state to all zeros)
|
||||
biquad_d1()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
/// direct I form with four state variables
|
||||
inline T process(T in)
|
||||
{
|
||||
T out = in * a0 + x1 * a1 + x2 * a2 - y1 * b1 - y2 * b2;
|
||||
x2 = x1;
|
||||
y2 = y1;
|
||||
x1 = in;
|
||||
y1 = out;
|
||||
return out;
|
||||
}
|
||||
|
||||
/// direct I form with zero input
|
||||
inline T process_zeroin()
|
||||
{
|
||||
T out = - y1 * b1 - y2 * b2;
|
||||
y2 = y1;
|
||||
y1 = out;
|
||||
return out;
|
||||
}
|
||||
|
||||
/// simplified version for lowpass case with two zeros at -1
|
||||
inline T process_lp(T in)
|
||||
{
|
||||
T out = a0*(in + x1 + x1 + x2) - y1 * b1 - y2 * b2;
|
||||
x2 = x1;
|
||||
y2 = y1;
|
||||
x1 = in;
|
||||
y1 = out;
|
||||
return out;
|
||||
}
|
||||
/// Sanitize (set to 0 if potentially denormal) filter state
|
||||
inline void sanitize()
|
||||
{
|
||||
dsp::sanitize(x1);
|
||||
dsp::sanitize(y1);
|
||||
dsp::sanitize(x2);
|
||||
dsp::sanitize(y2);
|
||||
}
|
||||
/// Reset state variables
|
||||
inline void reset()
|
||||
{
|
||||
dsp::zero(x1);
|
||||
dsp::zero(y1);
|
||||
dsp::zero(x2);
|
||||
dsp::zero(y2);
|
||||
}
|
||||
inline bool empty() {
|
||||
return (y1 == 0.f && y2 == 0.f);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Two-pole two-zero filter, for floating point values.
|
||||
* Uses slightly faster Direct II form (combined FIR and IIR halves).
|
||||
* However, when used with wildly varying coefficients, it may
|
||||
* make more zipper noise than Direct I form, so it's better to
|
||||
* use it when filter coefficients are not changed mid-stream.
|
||||
*/
|
||||
template<class Coeff = float, class T = float>
|
||||
struct biquad_d2: public biquad_coeffs<Coeff>
|
||||
{
|
||||
using biquad_coeffs<Coeff>::a0;
|
||||
using biquad_coeffs<Coeff>::a1;
|
||||
using biquad_coeffs<Coeff>::a2;
|
||||
using biquad_coeffs<Coeff>::b1;
|
||||
using biquad_coeffs<Coeff>::b2;
|
||||
/// state[n-1]
|
||||
float w1;
|
||||
/// state[n-2]
|
||||
float w2;
|
||||
/// Constructor (initializes state to all zeros)
|
||||
biquad_d2()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
/// direct II form with two state variables
|
||||
inline T process(T in)
|
||||
{
|
||||
T tmp = in - w1 * b1 - w2 * b2;
|
||||
T out = tmp * a0 + w1 * a1 + w2 * a2;
|
||||
w2 = w1;
|
||||
w1 = tmp;
|
||||
return out;
|
||||
}
|
||||
|
||||
// direct II form with two state variables, lowpass version
|
||||
// interesting fact: this is actually slower than the general version!
|
||||
inline T process_lp(T in)
|
||||
{
|
||||
T tmp = in - w1 * b1 - w2 * b2;
|
||||
T out = (tmp + w2 + w1* 2) * a0;
|
||||
w2 = w1;
|
||||
w1 = tmp;
|
||||
return out;
|
||||
}
|
||||
|
||||
/// Is the filter state completely silent? (i.e. set to 0 by sanitize function)
|
||||
inline bool empty() {
|
||||
return (w1 == 0.f && w2 == 0.f);
|
||||
}
|
||||
|
||||
|
||||
/// Sanitize (set to 0 if potentially denormal) filter state
|
||||
inline void sanitize()
|
||||
{
|
||||
dsp::sanitize(w1);
|
||||
dsp::sanitize(w2);
|
||||
}
|
||||
|
||||
/// Reset state variables
|
||||
inline void reset()
|
||||
{
|
||||
dsp::zero(w1);
|
||||
dsp::zero(w2);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Two-pole two-zero filter, for floating point values.
|
||||
* Uses "traditional" Direct I form (separate FIR and IIR halves).
|
||||
* don't use this for integers because it won't work
|
||||
*/
|
||||
template<class Coeff = float, class T = float>
|
||||
struct biquad_d1_lerp: public biquad_coeffs<Coeff>
|
||||
{
|
||||
using biquad_coeffs<Coeff>::a0;
|
||||
using biquad_coeffs<Coeff>::a1;
|
||||
using biquad_coeffs<Coeff>::a2;
|
||||
using biquad_coeffs<Coeff>::b1;
|
||||
using biquad_coeffs<Coeff>::b2;
|
||||
Coeff a0cur, a1cur, a2cur, b1cur, b2cur;
|
||||
Coeff a0delta, a1delta, a2delta, b1delta, b2delta;
|
||||
/// input[n-1]
|
||||
T x1;
|
||||
/// input[n-2]
|
||||
T x2;
|
||||
/// output[n-1]
|
||||
T y1;
|
||||
/// output[n-2]
|
||||
T y2;
|
||||
/// Constructor (initializes state to all zeros)
|
||||
biquad_d1_lerp()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
#define _DO_COEFF(coeff) coeff##delta = (coeff - coeff##cur) * (frac)
|
||||
void big_step(Coeff frac)
|
||||
{
|
||||
_DO_COEFF(a0);
|
||||
_DO_COEFF(a1);
|
||||
_DO_COEFF(a2);
|
||||
_DO_COEFF(b1);
|
||||
_DO_COEFF(b2);
|
||||
}
|
||||
#undef _DO_COEFF
|
||||
/// direct I form with four state variables
|
||||
inline T process(T in)
|
||||
{
|
||||
T out = in * a0cur + x1 * a1cur + x2 * a2cur - y1 * b1cur - y2 * b2cur;
|
||||
x2 = x1;
|
||||
y2 = y1;
|
||||
x1 = in;
|
||||
y1 = out;
|
||||
a0cur += a0delta;
|
||||
a1cur += a1delta;
|
||||
a2cur += a2delta;
|
||||
b1cur += b1delta;
|
||||
b2cur += b2delta;
|
||||
return out;
|
||||
}
|
||||
|
||||
/// direct I form with zero input
|
||||
inline T process_zeroin()
|
||||
{
|
||||
T out = - y1 * b1 - y2 * b2;
|
||||
y2 = y1;
|
||||
y1 = out;
|
||||
b1cur += b1delta;
|
||||
b2cur += b2delta;
|
||||
return out;
|
||||
}
|
||||
|
||||
/// simplified version for lowpass case with two zeros at -1
|
||||
inline T process_lp(T in)
|
||||
{
|
||||
T out = a0*(in + x1 + x1 + x2) - y1 * b1 - y2 * b2;
|
||||
x2 = x1;
|
||||
y2 = y1;
|
||||
x1 = in;
|
||||
y1 = out;
|
||||
return out;
|
||||
}
|
||||
/// Sanitize (set to 0 if potentially denormal) filter state
|
||||
inline void sanitize()
|
||||
{
|
||||
dsp::sanitize(x1);
|
||||
dsp::sanitize(y1);
|
||||
dsp::sanitize(x2);
|
||||
dsp::sanitize(y2);
|
||||
dsp::sanitize(a0cur);
|
||||
dsp::sanitize(a1cur);
|
||||
dsp::sanitize(a2cur);
|
||||
dsp::sanitize(b1cur);
|
||||
dsp::sanitize(b2cur);
|
||||
}
|
||||
/// Reset state variables
|
||||
inline void reset()
|
||||
{
|
||||
dsp::zero(x1);
|
||||
dsp::zero(y1);
|
||||
dsp::zero(x2);
|
||||
dsp::zero(y2);
|
||||
dsp::zero(a0cur);
|
||||
dsp::zero(a1cur);
|
||||
dsp::zero(a2cur);
|
||||
dsp::zero(b1cur);
|
||||
dsp::zero(b2cur);
|
||||
}
|
||||
inline bool empty() {
|
||||
return (y1 == 0.f && y2 == 0.f);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
229
plugins/ladspa_effect/calf/calf/buffer.h
Normal file
229
plugins/ladspa_effect/calf/calf/buffer.h
Normal file
@@ -0,0 +1,229 @@
|
||||
/* Calf DSP Library
|
||||
* Buffer abstractions.
|
||||
*
|
||||
* Copyright (C) 2007 Krzysztof Foltman
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef __BUFFER_H
|
||||
#define __BUFFER_H
|
||||
|
||||
namespace dsp {
|
||||
|
||||
/// decrease by N if >= N (useful for circular buffers)
|
||||
template<int N> inline int wrap_around(int a) {
|
||||
return (a >= N) ? a - N : a;
|
||||
}
|
||||
|
||||
// provide fast specializations for powers of 2
|
||||
template<> inline int wrap_around<2>(int a) { return a & 1; }
|
||||
template<> inline int wrap_around<4>(int a) { return a & 3; }
|
||||
template<> inline int wrap_around<8>(int a) { return a & 7; }
|
||||
template<> inline int wrap_around<16>(int a) { return a & 15; }
|
||||
template<> inline int wrap_around<32>(int a) { return a & 31; }
|
||||
template<> inline int wrap_around<64>(int a) { return a & 63; }
|
||||
template<> inline int wrap_around<128>(int a) { return a & 127; }
|
||||
template<> inline int wrap_around<256>(int a) { return a & 255; }
|
||||
template<> inline int wrap_around<512>(int a) { return a & 511; }
|
||||
template<> inline int wrap_around<1024>(int a) { return a & 1023; }
|
||||
template<> inline int wrap_around<2048>(int a) { return a & 2047; }
|
||||
template<> inline int wrap_around<4096>(int a) { return a & 4095; }
|
||||
template<> inline int wrap_around<8192>(int a) { return a & 8191; }
|
||||
template<> inline int wrap_around<16384>(int a) { return a & 16383; }
|
||||
template<> inline int wrap_around<32768>(int a) { return a & 32767; }
|
||||
template<> inline int wrap_around<65536>(int a) { return a & 65535; }
|
||||
|
||||
template<class Buf, class T>
|
||||
void fill(Buf &buf, T value) {
|
||||
T* data = buf.data();
|
||||
int size = buf.size();
|
||||
for (int i=0; i<size; i++)
|
||||
*data++ = value;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void fill(T *data, int size, T value) {
|
||||
for (int i=0; i<size; i++)
|
||||
*data++ = value;
|
||||
}
|
||||
|
||||
template<class T, class U>
|
||||
void copy(T *dest, U *src, int size, T scale = 1, T add = 0) {
|
||||
for (int i=0; i<size; i++)
|
||||
*dest++ = (*src++) * scale + add;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
struct sample_traits {
|
||||
enum {
|
||||
channels = 1,
|
||||
bps = sizeof(T)*8
|
||||
};
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct sample_traits<stereo_sample<T> > {
|
||||
enum {
|
||||
channels = 2,
|
||||
bps = sizeof(T)*8
|
||||
};
|
||||
};
|
||||
|
||||
template<int N, class T = float>
|
||||
class fixed_size_buffer {
|
||||
public:
|
||||
typedef T data_type;
|
||||
enum { buffer_size = N };
|
||||
inline int size() { return N; }
|
||||
};
|
||||
|
||||
template<int N, class T = float>
|
||||
class mem_fixed_size_buffer: public fixed_size_buffer<N, T> {
|
||||
T *buf;
|
||||
public:
|
||||
mem_fixed_size_buffer(T ubuf[N]) { buf = ubuf; }
|
||||
void set_data(T buf[N]) { this->buf = buf; }
|
||||
inline T* data() { return buf; }
|
||||
inline const T* data() const { return buf; }
|
||||
inline T& operator[](int pos) { return buf[pos]; }
|
||||
inline const T& operator[](int pos) const { return buf[pos]; }
|
||||
};
|
||||
|
||||
template<int N, class T = float>
|
||||
class auto_buffer: public fixed_size_buffer<N, T> {
|
||||
T buf[N];
|
||||
public:
|
||||
T* data() const { return buf; }
|
||||
inline T& operator[](int pos) { return buf[pos]; }
|
||||
inline const T& operator[](int pos) const { return buf[pos]; }
|
||||
};
|
||||
|
||||
template<class T = float>
|
||||
class dynamic_buffer {
|
||||
T *buf;
|
||||
int buf_size;
|
||||
bool owns;
|
||||
public:
|
||||
dynamic_buffer() { owns = false; }
|
||||
dynamic_buffer(T *_buf, int _buf_size, bool _own)
|
||||
: buf(_buf), buf_size(_buf_size), owns(_own) {
|
||||
}
|
||||
dynamic_buffer(int _size) {
|
||||
buf = new T[_size];
|
||||
buf_size = _size;
|
||||
owns = true;
|
||||
}
|
||||
inline T* data() { return buf; }
|
||||
inline const T* data() const { return buf; }
|
||||
inline int size() { return buf_size; }
|
||||
void resize(int new_size, bool fill_with_zeros = false) {
|
||||
T *new_buf = new T[new_size];
|
||||
memcpy(new_buf, buf, std::min(buf_size, new_size));
|
||||
if (fill_with_zeros && buf_size < new_size)
|
||||
dsp::zero(new_buf + buf_size, new_size - buf_size);
|
||||
if (owns)
|
||||
delete []buf;
|
||||
buf = new_buf;
|
||||
buf_size = new_size;
|
||||
owns = true;
|
||||
}
|
||||
inline T& operator[](int pos) { return buf[pos]; }
|
||||
inline const T& operator[](int pos) const { return buf[pos]; }
|
||||
~dynamic_buffer() {
|
||||
if (owns)
|
||||
delete []buf;
|
||||
}
|
||||
};
|
||||
|
||||
template<class T, class U>
|
||||
void copy_buf(T &dest_buf, const U &src_buf, T scale = 1, T add = 0) {
|
||||
typedef typename T::data_type data_type;
|
||||
data_type *dest = dest_buf.data();
|
||||
const data_type *src = src_buf.data();
|
||||
int size = src.size();
|
||||
for (int i=0; i<size; i++)
|
||||
*dest++ = (*src++) * scale + add;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
struct buffer_traits {
|
||||
};
|
||||
|
||||
/// this class template defines some basic position operations for fixed_size_buffers
|
||||
template<int N, class T>
|
||||
struct buffer_traits<fixed_size_buffer<N, T> > {
|
||||
int inc_wrap(int pos) const {
|
||||
return wrap_around<T::size>(pos+1);
|
||||
}
|
||||
|
||||
int pos_diff(int pos1, int pos2) const {
|
||||
int pos = pos1 - pos2;
|
||||
if (pos < 0) pos += T::size;
|
||||
return pos;
|
||||
}
|
||||
};
|
||||
|
||||
/// this is useless for now (and untested too)
|
||||
template<class B>
|
||||
class circular_buffer: public B {
|
||||
typedef typename B::data_type data_type;
|
||||
typedef class buffer_traits<B> traits;
|
||||
B buffer;
|
||||
int rpos, wpos;
|
||||
circular_buffer() {
|
||||
clear();
|
||||
}
|
||||
void clear() {
|
||||
rpos = 0;
|
||||
wpos = 0;
|
||||
}
|
||||
inline void put(data_type data) {
|
||||
buffer[wpos] = data;
|
||||
wpos = traits::inc_wrap(wpos);
|
||||
}
|
||||
inline bool empty() {
|
||||
return rpos == wpos;
|
||||
}
|
||||
inline bool full() {
|
||||
return rpos == traits::inc_wrap(wpos);
|
||||
}
|
||||
inline const data_type& get() {
|
||||
int oldrpos = rpos;
|
||||
rpos = traits::inc_wrap(rpos);
|
||||
return buffer[oldrpos];
|
||||
}
|
||||
inline int get_rbytes() {
|
||||
return traits::pos_diff(wpos, rpos);
|
||||
}
|
||||
inline int get_wbytes() {
|
||||
if (full()) return 0;
|
||||
return traits::pos_diff(rpos, wpos);
|
||||
}
|
||||
};
|
||||
|
||||
/// this is useless for now
|
||||
template<int N, class T = float>
|
||||
class mono_auto_buffer: public auto_buffer<N, T> {
|
||||
};
|
||||
|
||||
/// this is useless for now
|
||||
template<int N, class T = float>
|
||||
class stereo_auto_buffer: public auto_buffer<N, stereo_sample<T> > {
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
185
plugins/ladspa_effect/calf/calf/delay.h
Normal file
185
plugins/ladspa_effect/calf/calf/delay.h
Normal file
@@ -0,0 +1,185 @@
|
||||
/* Calf DSP Library
|
||||
* Reusable audio effect classes.
|
||||
*
|
||||
* Copyright (C) 2001-2007 Krzysztof Foltman
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef __CALF_DELAY_H
|
||||
#define __CALF_DELAY_H
|
||||
|
||||
#include "primitives.h"
|
||||
#include "buffer.h"
|
||||
#include "onepole.h"
|
||||
|
||||
namespace dsp {
|
||||
|
||||
/**
|
||||
* Delay primitive. Can be used for most delay stuff, including
|
||||
* variable (modulated) delays like chorus/flanger. Note that
|
||||
* for modulated delay effects use of GetInterp is preferred,
|
||||
* because it handles fractional positions and uses linear
|
||||
* interpolation, which sounds better most of the time.
|
||||
*
|
||||
* @param N maximum length
|
||||
* @param C number of channels read/written for each sample (1 mono, 2 stereo etc)
|
||||
*/
|
||||
template<int N, class T>
|
||||
struct simple_delay {
|
||||
auto_buffer<N, T> data;
|
||||
int pos;
|
||||
|
||||
simple_delay() {
|
||||
reset();
|
||||
}
|
||||
void reset() {
|
||||
pos = 0;
|
||||
for (int i=0; i<N; i++)
|
||||
zero(data[i]);
|
||||
}
|
||||
/** Write one C-channel sample from idata[0], idata[1] etc into buffer */
|
||||
inline void put(T idata) {
|
||||
data[pos] = idata;
|
||||
pos = wrap_around<N>(pos+1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read one C-channel sample into odata[0], odata[1] etc into buffer.
|
||||
* Don't use for modulated delays (chorus/flanger etc) unless you
|
||||
* want them to crackle and generally sound ugly
|
||||
* @param odata pointer to write into
|
||||
* @param delay delay relative to current writing pos
|
||||
*/
|
||||
template<class U>
|
||||
inline void get(U &odata, int delay) {
|
||||
assert(delay >= 0 && delay < N);
|
||||
int ppos = wrap_around<N>(pos + N - delay);
|
||||
odata = data[ppos];
|
||||
}
|
||||
|
||||
/**
|
||||
* Read and write during the same function call
|
||||
*/
|
||||
inline T process(T idata, int delay)
|
||||
{
|
||||
assert(delay >= 0 && delay < N);
|
||||
int ppos = wrap_around<N>(pos + N - delay);
|
||||
T odata = data[ppos];
|
||||
data[pos] = idata;
|
||||
pos = wrap_around<N>(pos+1);
|
||||
return odata;
|
||||
}
|
||||
|
||||
/** Read one C-channel sample at fractional position.
|
||||
* This version can be used for modulated delays, because
|
||||
* it uses linear interpolation.
|
||||
* @param odata value to write into
|
||||
* @param delay delay relative to current writing pos
|
||||
* @param udelay fractional delay (0..1)
|
||||
*/
|
||||
template<class U>
|
||||
inline void get_interp(U &odata, int delay, float udelay) {
|
||||
// assert(delay >= 0 && delay < N-1);
|
||||
int ppos = wrap_around<N>(pos + N - delay);
|
||||
int pppos = wrap_around<N>(ppos + N - 1);
|
||||
odata = lerp(data[ppos], data[pppos], udelay);
|
||||
}
|
||||
|
||||
/** Read one C-channel sample at fractional position.
|
||||
* This version can be used for modulated delays, because
|
||||
* it uses linear interpolation.
|
||||
* @param odata value to write into
|
||||
* @param delay delay relative to current writing pos
|
||||
* @param udelay fractional delay (0..1)
|
||||
*/
|
||||
inline T get_interp_1616(unsigned int delay) {
|
||||
float udelay = (float)((delay & 0xFFFF) * (1.0 / 65536.0));
|
||||
delay = delay >> 16;
|
||||
// assert(delay >= 0 && delay < N-1);
|
||||
int ppos = wrap_around<N>(pos + N - delay);
|
||||
int pppos = wrap_around<N>(ppos + N - 1);
|
||||
return lerp(data[ppos], data[pppos], udelay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Comb filter. Feedback delay line with given delay and feedback values
|
||||
* @param in input signal
|
||||
* @param delay delay length (must be <N and integer)
|
||||
* @param fb feedback (must be <1 or it will be unstable)
|
||||
*/
|
||||
inline T process_comb(T in, unsigned int delay, float fb)
|
||||
{
|
||||
T old, cur;
|
||||
get(old, delay);
|
||||
cur = in + fb*old;
|
||||
sanitize(cur);
|
||||
put(cur);
|
||||
return old;
|
||||
}
|
||||
|
||||
/**
|
||||
* Comb filter with linear interpolation. Feedback delay line with given delay and feedback values
|
||||
* Note that linear interpolation introduces some weird effects in frequency response.
|
||||
* @param in input signal
|
||||
* @param delay fractional delay length (must be < 65536 * N)
|
||||
* @param fb feedback (must be <1 or it will be unstable)
|
||||
*/
|
||||
inline T process_comb_lerp16(T in, unsigned int delay, float udelay, float fb)
|
||||
{
|
||||
T old, cur;
|
||||
get_interp(old, delay>>16, dsp::fract16(delay));
|
||||
cur = in + fb*old;
|
||||
sanitize(cur);
|
||||
put(cur);
|
||||
return old;
|
||||
}
|
||||
|
||||
/**
|
||||
* Comb allpass filter. The comb filter with additional direct path, which is supposed to cancel the coloration.
|
||||
* @param in input signal
|
||||
* @param delay delay length (must be <N and integer)
|
||||
* @param fb feedback (must be <1 or it will be unstable)
|
||||
*/
|
||||
inline T process_allpass_comb(T in, unsigned int delay, float fb)
|
||||
{
|
||||
T old, cur;
|
||||
get(old, delay);
|
||||
cur = in + fb*old;
|
||||
sanitize(cur);
|
||||
put(cur);
|
||||
return old - fb * cur;
|
||||
}
|
||||
|
||||
/**
|
||||
* Comb allpass filter. The comb filter with additional direct path, which is supposed to cancel the coloration.
|
||||
* @param in input signal
|
||||
* @param delay fractional delay length (must be < 65536 * N)
|
||||
* @param fb feedback (must be <1 or it will be unstable)
|
||||
*/
|
||||
inline T process_allpass_comb_lerp16(T in, unsigned int delay, float fb)
|
||||
{
|
||||
T old, cur;
|
||||
get_interp(old, delay>>16, dsp::fract16(delay));
|
||||
cur = in + fb*old;
|
||||
sanitize(cur);
|
||||
put(cur);
|
||||
return old - fb * cur;
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
206
plugins/ladspa_effect/calf/calf/envelope.h
Normal file
206
plugins/ladspa_effect/calf/calf/envelope.h
Normal file
@@ -0,0 +1,206 @@
|
||||
/* Calf DSP Library
|
||||
* ADSR envelope class (and other envelopes in future)
|
||||
*
|
||||
* Copyright (C) 2007-2008 Krzysztof Foltman
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef __CALF_ENVELOPE_H
|
||||
#define __CALF_ENVELOPE_H
|
||||
|
||||
#include "primitives.h"
|
||||
|
||||
namespace dsp {
|
||||
|
||||
/// Rate-based ADSFR envelope class. Note that if release rate is slower than decay
|
||||
/// rate, this envelope won't use release rate until output level falls below sustain level
|
||||
/// it's different to what certain hardware synth companies did, but it prevents the very
|
||||
/// un-musical (IMHO) behaviour known from (for example) SoundFont 2.
|
||||
class adsr
|
||||
{
|
||||
public:
|
||||
enum env_state {
|
||||
STOP, ///< envelope is stopped
|
||||
ATTACK, ///< attack - rise from 0 to 1
|
||||
DECAY, ///< decay - fall from 1 to sustain level
|
||||
SUSTAIN, ///< sustain - remain at sustain level (unless sustain is 0 - then it gets stopped); with fade != 0 it goes towards 0% (positive fade) or 100% (negative fade)
|
||||
RELEASE, ///< release - fall from sustain (or pre-sustain) level to 0
|
||||
LOCKDECAY, ///< locked decay
|
||||
};
|
||||
|
||||
/// Current envelope stage
|
||||
env_state state;
|
||||
/// @note these are *rates*, not times
|
||||
double attack, decay, sustain, release, fade;
|
||||
/// Requested release time (not the rate!) in frames, used for recalculating the rate if sustain is changed
|
||||
double release_time;
|
||||
/// Current envelope (output) level
|
||||
double value;
|
||||
/// Release rate used for the current note (calculated from this note's sustain level, and not the current sustain level,
|
||||
/// which may have changed after note has been released)
|
||||
double thisrelease;
|
||||
/// Sustain level used for the current note (used to calculate release rate if sustain changed during release stage
|
||||
/// of the current note)
|
||||
double thiss;
|
||||
/// Value from the time before advance() was called last time
|
||||
double old_value;
|
||||
|
||||
adsr()
|
||||
{
|
||||
attack = decay = sustain = release = thisrelease = thiss = 0.f;
|
||||
reset();
|
||||
}
|
||||
/// Stop (reset) the envelope
|
||||
inline void reset()
|
||||
{
|
||||
old_value = value = 0.0;
|
||||
thiss = 0.0;
|
||||
state = STOP;
|
||||
}
|
||||
/// Set the envelope parameters (updates rate member variables based on values passed)
|
||||
/// @param a attack time
|
||||
/// @param d decay time
|
||||
/// @param s sustain level
|
||||
/// @param r release time
|
||||
/// @param er Envelope (update) rate
|
||||
/// @param f fade time (if applicable)
|
||||
inline void set(float a, float d, float s, float r, float er, float f = 0.f)
|
||||
{
|
||||
attack = 1.0 / (a * er);
|
||||
decay = (1 - s) / (d * er);
|
||||
sustain = s;
|
||||
release_time = r * er;
|
||||
release = s / release_time;
|
||||
if (fabs(f) > small_value<float>())
|
||||
fade = 1.0 / (f * er);
|
||||
else
|
||||
fade = 0.0;
|
||||
// in release:
|
||||
// lock thiss setting (start of release for current note) and unlock thisrelease setting (current note's release rate)
|
||||
if (state != RELEASE)
|
||||
thiss = s;
|
||||
else
|
||||
thisrelease = thiss / release_time;
|
||||
}
|
||||
/// @retval true if envelope is in released state (forced decay, release or stopped)
|
||||
inline bool released() const
|
||||
{
|
||||
return state == LOCKDECAY || state == RELEASE || state == STOP;
|
||||
}
|
||||
/// @retval true if envelope is stopped (has not been started or has run till its end)
|
||||
inline bool stopped() const
|
||||
{
|
||||
return state == STOP;
|
||||
}
|
||||
/// Start the envelope
|
||||
inline void note_on()
|
||||
{
|
||||
state = ATTACK;
|
||||
thiss = sustain;
|
||||
}
|
||||
/// Release the envelope
|
||||
inline void note_off()
|
||||
{
|
||||
// Do nothing if envelope is already stopped
|
||||
if (state == STOP)
|
||||
return;
|
||||
// XXXKF what if envelope is already released? (doesn't happen in any current synth, but who knows?)
|
||||
// Raise sustain value if it has been changed... I'm not sure if it's needed
|
||||
thiss = std::max(sustain, value);
|
||||
// Calculate release rate from sustain level
|
||||
thisrelease = thiss / release_time;
|
||||
// we're in attack or decay, and if decay is faster than release
|
||||
if (value > sustain && decay > thisrelease) {
|
||||
// use standard release time later (because we'll be switching at sustain point)
|
||||
thisrelease = release;
|
||||
state = LOCKDECAY;
|
||||
} else {
|
||||
// in attack/decay, but use fixed release time
|
||||
// in case value fell below sustain, assume it didn't (for the purpose of calculating release rate only)
|
||||
state = RELEASE;
|
||||
}
|
||||
}
|
||||
/// Calculate next envelope value
|
||||
inline void advance()
|
||||
{
|
||||
old_value = value;
|
||||
// XXXKF This may use a state array instead of a switch some day (at least for phases other than attack and possibly sustain)
|
||||
switch(state)
|
||||
{
|
||||
case ATTACK:
|
||||
value += attack;
|
||||
if (value >= 1.0) {
|
||||
value = 1.0;
|
||||
state = DECAY;
|
||||
}
|
||||
break;
|
||||
case DECAY:
|
||||
value -= decay;
|
||||
if (value < sustain)
|
||||
{
|
||||
value = sustain;
|
||||
state = SUSTAIN;
|
||||
}
|
||||
break;
|
||||
case LOCKDECAY:
|
||||
value -= decay;
|
||||
if (value < sustain)
|
||||
{
|
||||
if (value < 0.f)
|
||||
value = 0.f;
|
||||
state = RELEASE;
|
||||
thisrelease = release;
|
||||
}
|
||||
break;
|
||||
case SUSTAIN:
|
||||
if (fade != 0.f)
|
||||
{
|
||||
value -= fade;
|
||||
if (value > 1.f)
|
||||
value = 1.f;
|
||||
}
|
||||
else
|
||||
value = sustain;
|
||||
if (value < 0.00001f) {
|
||||
value = 0;
|
||||
state = STOP;
|
||||
}
|
||||
break;
|
||||
case RELEASE:
|
||||
value -= thisrelease;
|
||||
if (value <= 0.f) {
|
||||
value = 0.f;
|
||||
state = STOP;
|
||||
}
|
||||
break;
|
||||
case STOP:
|
||||
value = 0.f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/// Return a value between old_value (previous step) and value (current step)
|
||||
/// @param pos between 0 and 1
|
||||
inline double interpolate(double pos)
|
||||
{
|
||||
return old_value + (value - old_value) * pos;
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
113
plugins/ladspa_effect/calf/calf/fft.h
Normal file
113
plugins/ladspa_effect/calf/calf/fft.h
Normal file
@@ -0,0 +1,113 @@
|
||||
/* Calf DSP Library
|
||||
* FFT class
|
||||
*
|
||||
* Copyright (C) 2007 Krzysztof Foltman
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __CALF_FFT_H
|
||||
#define __CALF_FFT_H
|
||||
|
||||
#include <complex>
|
||||
|
||||
namespace dsp {
|
||||
|
||||
/// FFT routine copied from my old OneSignal library, modified to
|
||||
/// match Calf's style. It's not fast at all, just a straightforward
|
||||
/// implementation.
|
||||
template<class T, int O>
|
||||
class fft
|
||||
{
|
||||
typedef typename std::complex<T> complex;
|
||||
int scramble[1<<O];
|
||||
complex sines[1<<O];
|
||||
public:
|
||||
fft()
|
||||
{
|
||||
int N=1<<O;
|
||||
assert(N >= 4);
|
||||
for (int i=0; i<N; i++)
|
||||
{
|
||||
int v=0;
|
||||
for (int j=0; j<O; j++)
|
||||
if (i&(1<<j))
|
||||
v+=(N>>(j+1));
|
||||
scramble[i]=v;
|
||||
}
|
||||
int N90 = N >> 2;
|
||||
T divN = 2 * M_PI / N;
|
||||
// use symmetry
|
||||
for (int i=0; i<N90; i++)
|
||||
{
|
||||
T angle = divN * i;
|
||||
T c = cos(angle), s = sin(angle);
|
||||
sines[i + 3 * N90] = -(sines[i + N90] = complex(-s, c));
|
||||
sines[i + 2 * N90] = -(sines[i] = complex(c, s));
|
||||
}
|
||||
}
|
||||
void calculate(complex *input, complex *output, bool inverse)
|
||||
{
|
||||
int N=1<<O;
|
||||
int N1=N-1;
|
||||
int i;
|
||||
// Scramble the input data
|
||||
if (inverse)
|
||||
{
|
||||
float mf=1.0/N;
|
||||
for (i=0; i<N; i++)
|
||||
{
|
||||
complex &c=input[scramble[i]];
|
||||
output[i]=mf*complex(c.imag(),c.real());
|
||||
}
|
||||
}
|
||||
else
|
||||
for (i=0; i<N; i++)
|
||||
output[i]=input[scramble[i]];
|
||||
|
||||
// O butterfiles
|
||||
for (i=0; i<O; i++)
|
||||
{
|
||||
int PO=1<<i, PNO=1<<(O-i-1);
|
||||
int j,k;
|
||||
for (j=0; j<PNO; j++)
|
||||
{
|
||||
int base=j<<(i+1);
|
||||
for (k=0; k<PO; k++)
|
||||
{
|
||||
int B1=base+k;
|
||||
int B2=base+k+(1<<i);
|
||||
complex r1=output[B1];
|
||||
complex r2=output[B2];
|
||||
output[B1]=r1+r2*sines[(B1<<(O-i-1))&N1];
|
||||
output[B2]=r1+r2*sines[(B2<<(O-i-1))&N1];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (inverse)
|
||||
{
|
||||
for (i=0; i<N; i++)
|
||||
{
|
||||
const complex &c=output[i];
|
||||
output[i]=complex(c.imag(),c.real());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
269
plugins/ladspa_effect/calf/calf/fixed_point.h
Normal file
269
plugins/ladspa_effect/calf/calf/fixed_point.h
Normal file
@@ -0,0 +1,269 @@
|
||||
/* Calf DSP Library
|
||||
* DSP primitives.
|
||||
*
|
||||
* Copyright (C) 2001-2007 Krzysztof Foltman
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __CALF_FIXED_POINT_H
|
||||
#define __CALF_FIXED_POINT_H
|
||||
|
||||
namespace dsp {
|
||||
|
||||
inline uint32_t shr(uint32_t v, int bits = 1) { return v>>bits; };
|
||||
inline int32_t shr(int32_t v, int bits = 1) { return v>>bits; };
|
||||
inline uint64_t shr(uint64_t v, int bits = 1) { return v>>bits; };
|
||||
inline int64_t shr(int64_t v, int bits = 1) { return v>>bits; };
|
||||
inline float shr(float v, int bits = 1) { return v*(1.0/(1<<bits)); };
|
||||
inline double shr(double v, int bits = 1) { return v*(1.0/(1<<bits)); };
|
||||
template<class T, int FracBits>
|
||||
inline T shr(T v, int bits = 1) {
|
||||
v.set(v >> bits);
|
||||
return v;
|
||||
}
|
||||
|
||||
template<class T, int FracBits> class fixed_point {
|
||||
T value;
|
||||
enum { IntBits = (sizeof(T)/8) - FracBits };
|
||||
|
||||
public:
|
||||
/// default constructor, does not initialize the value, just like - say - float doesn't
|
||||
inline fixed_point() {
|
||||
}
|
||||
|
||||
/// copy constructor from any other fixed_point value
|
||||
template<class U, int FracBits2> inline fixed_point(const fixed_point<U, FracBits2> &v) {
|
||||
if (FracBits == FracBits2) value = v.get();
|
||||
else if (FracBits > FracBits2) value = v.get() << abs(FracBits - FracBits2);
|
||||
else value = v.get() >> abs(FracBits - FracBits2);
|
||||
}
|
||||
|
||||
/* this would be way too confusing, it wouldn't be obvious if it expects a whole fixed point or an integer part
|
||||
explicit inline fixed_point(T v) {
|
||||
this->value = v;
|
||||
}
|
||||
*/
|
||||
explicit inline fixed_point(double v) {
|
||||
value = (T)(v*one());
|
||||
}
|
||||
|
||||
/// Makes an instance from a representation value (ie. same type of value as is used for internal storage and get/set)
|
||||
static inline fixed_point from_base(const T &v)
|
||||
{
|
||||
fixed_point result;
|
||||
result.value = v;
|
||||
return result;
|
||||
}
|
||||
|
||||
inline static T one() {
|
||||
return (T)(1) << FracBits;
|
||||
}
|
||||
|
||||
inline void set(T value) {
|
||||
this->value = value;
|
||||
}
|
||||
|
||||
inline T get() const {
|
||||
return value;
|
||||
}
|
||||
|
||||
inline operator double() const {
|
||||
return value * (1.0/one());
|
||||
}
|
||||
|
||||
inline fixed_point &operator=(double v) {
|
||||
value = (T)(v*one());
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class U, int FracBits2> static inline T rebase(const fixed_point<U, FracBits2> &v) {
|
||||
if (FracBits == FracBits2)
|
||||
return v.get();
|
||||
if (FracBits > FracBits2)
|
||||
return v.get() << abs(FracBits - FracBits2);
|
||||
return v.get() >> abs(FracBits2 - FracBits);
|
||||
}
|
||||
|
||||
template<class U, int FracBits2> inline fixed_point &operator=(const fixed_point<U, FracBits2> &v) {
|
||||
value = rebase<U, FracBits2>(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class U, int FracBits2> inline fixed_point &operator+=(const fixed_point<U, FracBits2> &v) {
|
||||
value += rebase<U, FracBits2>(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class U, int FracBits2> inline fixed_point &operator-=(const fixed_point<U, FracBits2> &v) {
|
||||
value -= rebase<U, FracBits2>(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class U, int FracBits2> inline fixed_point operator+(const fixed_point<U, FracBits2> &v) const {
|
||||
fixed_point fpv;
|
||||
fpv.set(value + rebase<U, FracBits2>(v));
|
||||
return fpv;
|
||||
}
|
||||
|
||||
template<class U, int FracBits2> inline fixed_point operator-(const fixed_point<U, FracBits2> &v) const {
|
||||
fixed_point fpv;
|
||||
fpv.set(value - rebase<U, FracBits2>(v));
|
||||
return fpv;
|
||||
}
|
||||
|
||||
/// multiply two fixed point values, using long long int to store the temporary multiplication result
|
||||
template<class U, int FracBits2> inline fixed_point operator*(const fixed_point<U, FracBits2> &v) const {
|
||||
fixed_point tmp;
|
||||
tmp.set(((int64_t)value) * v.get() >> FracBits2);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/// multiply two fixed point values, using BigType (usually 64-bit int) to store the temporary multiplication result
|
||||
template<class U, int FracBits2, class BigType> inline fixed_point& operator*=(const fixed_point<U, FracBits2> &v) {
|
||||
value = (T)(((BigType)value) * v.get() >> FracBits2);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline fixed_point operator+(int v) const {
|
||||
fixed_point tmp;
|
||||
tmp.set(value + (v << FracBits));
|
||||
return tmp;
|
||||
}
|
||||
|
||||
inline fixed_point operator-(int v) const {
|
||||
fixed_point tmp;
|
||||
tmp.set(value - (v << FracBits));
|
||||
return tmp;
|
||||
}
|
||||
|
||||
inline fixed_point operator*(int v) const {
|
||||
fixed_point tmp;
|
||||
tmp.value = value*v;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
inline fixed_point& operator+=(int v) {
|
||||
value += (v << FracBits);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline fixed_point& operator-=(int v) {
|
||||
value -= (v << FracBits);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline fixed_point& operator*=(int v) {
|
||||
value *= v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// return integer part
|
||||
inline T ipart() const {
|
||||
return value >> FracBits;
|
||||
}
|
||||
|
||||
/// return integer part as unsigned int
|
||||
inline unsigned int uipart() const {
|
||||
return ((unsigned)value) >> FracBits;
|
||||
}
|
||||
|
||||
/// return integer part as unsigned int
|
||||
inline unsigned int ui64part() const {
|
||||
return ((uint64_t)value) >> FracBits;
|
||||
}
|
||||
|
||||
/// return fractional part as 0..(2^FracBits-1)
|
||||
inline T fpart() const {
|
||||
return value & ((1 << FracBits)-1);
|
||||
}
|
||||
|
||||
/// return fractional part as 0..(2^Bits-1)
|
||||
template<int Bits>
|
||||
inline T fpart() const {
|
||||
int fbits = value & ((1 << FracBits)-1);
|
||||
if (Bits == FracBits) return fbits;
|
||||
int shift = abs(Bits-FracBits);
|
||||
return (Bits < FracBits) ? (fbits >> shift) : (fbits << shift);
|
||||
}
|
||||
|
||||
/// return fractional part as 0..1
|
||||
inline double fpart_as_double() const {
|
||||
return (value & ((1 << FracBits)-1)) * (1.0 / (1 << FracBits));
|
||||
}
|
||||
|
||||
/// use fractional part (either whole or given number of most significant bits) for interpolating between two values
|
||||
/// note that it uses integer arithmetic only, and isn't suitable for floating point or fixed point U!
|
||||
/// @param UseBits can be used when there's a risk of exceeding range of U because max(fpart)*max(v1 or v2) > range of U
|
||||
template<class U, int UseBits, class MulType>
|
||||
inline U lerp_by_fract_int(U v1, U v2) const {
|
||||
int fp = fpart<UseBits>();
|
||||
assert ( fp >=0 && fp <= (1<<UseBits));
|
||||
// printf("diff =
|
||||
return v1 + shr(((MulType)(v2-v1) * fp), UseBits);
|
||||
}
|
||||
|
||||
template<class U, int UseBits>
|
||||
inline U lerp_table_lookup_int(U data[(1<<IntBits)+1]) const {
|
||||
unsigned int pos = uipart();
|
||||
return lerp_by_fract_int<U, UseBits>(data[pos], data[pos+1]);
|
||||
}
|
||||
|
||||
/// Untested... I've started it to get a sin/cos readout for rotaryorgan, but decided to use table-less solution instead
|
||||
/// Do not assume it works, because it most probably doesn't
|
||||
template<class U, int UseBits>
|
||||
inline U lerp_table_lookup_int_shift(U data[(1<<IntBits)+1], unsigned int shift) {
|
||||
unsigned int pos = (uipart() + shift) & ((1 << IntBits) - 1);
|
||||
return lerp_by_fract_int<U, UseBits>(data[pos], data[pos+1]);
|
||||
}
|
||||
|
||||
template<class U>
|
||||
inline U lerp_table_lookup_float(U data[(1<<IntBits)+1]) const {
|
||||
unsigned int pos = uipart();
|
||||
return data[pos] + (data[pos+1]-data[pos]) * fpart_as_double();
|
||||
}
|
||||
|
||||
template<class U>
|
||||
inline U lerp_table_lookup_float_mask(U data[(1<<IntBits)+1], unsigned int mask) const {
|
||||
unsigned int pos = ui64part() & mask;
|
||||
// printf("full = %lld pos = %d + %f\n", value, pos, fpart_as_double());
|
||||
return data[pos] + (data[pos+1]-data[pos]) * fpart_as_double();
|
||||
}
|
||||
|
||||
template<class U, int UseBits, class MulType>
|
||||
inline U lerp_ptr_lookup_int(U *data) const {
|
||||
unsigned int pos = ui64part();
|
||||
return lerp_by_fract_int<U, UseBits, MulType>(data[pos], data[pos+1]);
|
||||
}
|
||||
|
||||
template<class U>
|
||||
inline U lerp_ptr_lookup_float(U *data) const {
|
||||
unsigned int pos = ui64part();
|
||||
return data[pos] + (data[pos+1]-data[pos]) * fpart_as_double();
|
||||
}
|
||||
};
|
||||
|
||||
template<class T, int FractBits>
|
||||
inline fixed_point<T, FractBits> operator*(int v, fixed_point<T, FractBits> v2) {
|
||||
v2 *= v;
|
||||
return v2;
|
||||
}
|
||||
|
||||
/// wave position (unsigned 64-bit int including 24-bit fractional part)
|
||||
typedef fixed_point<unsigned long long int, 24> wpos;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
561
plugins/ladspa_effect/calf/calf/giface.h
Normal file
561
plugins/ladspa_effect/calf/calf/giface.h
Normal file
@@ -0,0 +1,561 @@
|
||||
/* Calf DSP Library
|
||||
* Common plugin interface definitions (shared between LADSPA/LV2/DSSI/standalone).
|
||||
*
|
||||
* Copyright (C) 2007 Krzysztof Foltman
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __CALF_GIFACE_H
|
||||
#define __CALF_GIFACE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include "primitives.h"
|
||||
#include "preset.h"
|
||||
|
||||
namespace osctl {
|
||||
struct osc_client;
|
||||
}
|
||||
|
||||
namespace calf_plugins {
|
||||
|
||||
enum {
|
||||
MAX_SAMPLE_RUN = 256
|
||||
};
|
||||
|
||||
/// Values ORed together for flags field in parameter_properties
|
||||
enum parameter_flags
|
||||
{
|
||||
PF_TYPEMASK = 0x000F, ///< bit mask for type
|
||||
PF_FLOAT = 0x0000, ///< any float value
|
||||
PF_INT = 0x0001, ///< integer value (still represented as float)
|
||||
PF_BOOL = 0x0002, ///< bool value (usually >=0.5f is treated as TRUE, which is inconsistent with LV2 etc. which treats anything >0 as TRUE)
|
||||
PF_ENUM = 0x0003, ///< enum value (min, min+1, ..., max, only guaranteed to work when min = 0)
|
||||
PF_ENUM_MULTI = 0x0004, ///< SET / multiple-choice
|
||||
PF_STRING = 0x0005, ///< see: http://lv2plug.in/docs/index.php?title=String_port
|
||||
|
||||
PF_SCALEMASK = 0xF0, ///< bit mask for scale
|
||||
PF_SCALE_DEFAULT = 0x00, ///< no scale given
|
||||
PF_SCALE_LINEAR = 0x10, ///< linear scale
|
||||
PF_SCALE_LOG = 0x20, ///< log scale
|
||||
PF_SCALE_GAIN = 0x30, ///< gain = -96dB..0 or -inf dB
|
||||
PF_SCALE_PERC = 0x40, ///< percent
|
||||
PF_SCALE_QUAD = 0x50, ///< quadratic scale (decent for some gain/amplitude values)
|
||||
PF_SCALE_LOG_INF = 0x60, ///< log scale + +inf (FAKE_INFINITY)
|
||||
|
||||
PF_CTLMASK = 0x0F00, ///< bit mask for control type
|
||||
PF_CTL_DEFAULT = 0x0000, ///< try to figure out automatically
|
||||
PF_CTL_KNOB = 0x0100, ///< knob
|
||||
PF_CTL_FADER = 0x0200, ///< fader (slider)
|
||||
PF_CTL_TOGGLE = 0x0300, ///< toggle button
|
||||
PF_CTL_COMBO = 0x0400, ///< combo box
|
||||
PF_CTL_RADIO = 0x0500, ///< radio button
|
||||
PF_CTL_BUTTON = 0x0600, ///< push button
|
||||
PF_CTL_METER = 0x0700, ///< volume meter
|
||||
PF_CTL_LED = 0x0800, ///< light emitting diode
|
||||
|
||||
PF_CTLOPTIONS = 0x00F000, ///< bit mask for control (widget) options
|
||||
PF_CTLO_HORIZ = 0x001000, ///< horizontal version of the control (unused)
|
||||
PF_CTLO_VERT = 0x002000, ///< vertical version of the control (unused)
|
||||
PF_CTLO_LABEL = 0x004000, ///< add a text display to the control (meters only)
|
||||
PF_CTLO_REVERSE = 0x008000, ///< use VU_MONOCHROME_REVERSE mode (meters only)
|
||||
|
||||
PF_PROP_NOBOUNDS = 0x010000, ///< no epp:hasStrictBounds
|
||||
PF_PROP_EXPENSIVE = 0x020000, ///< epp:expensive, may trigger expensive calculation
|
||||
PF_PROP_OUTPUT_GAIN=0x050000, ///< epp:outputGain + skip epp:hasStrictBounds
|
||||
PF_PROP_OUTPUT = 0x080000, ///< output port
|
||||
PF_PROP_OPTIONAL = 0x100000, ///< connection optional
|
||||
PF_PROP_GRAPH = 0x200000, ///< add graph
|
||||
PF_PROP_MSGCONTEXT= 0x400000, ///< message context
|
||||
|
||||
PF_UNITMASK = 0xFF000000, ///< bit mask for units \todo reduce to use only 5 bits
|
||||
PF_UNIT_DB = 0x01000000, ///< decibels
|
||||
PF_UNIT_COEF = 0x02000000, ///< multiply-by factor
|
||||
PF_UNIT_HZ = 0x03000000, ///< Hertz
|
||||
PF_UNIT_SEC = 0x04000000, ///< second
|
||||
PF_UNIT_MSEC = 0x05000000, ///< millisecond
|
||||
PF_UNIT_CENTS = 0x06000000, ///< cents (1/100 of a semitone, 1/1200 of an octave)
|
||||
PF_UNIT_SEMITONES = 0x07000000,///< semitones
|
||||
PF_UNIT_BPM = 0x08000000, ///< beats per minute
|
||||
PF_UNIT_DEG = 0x09000000, ///< degrees
|
||||
PF_UNIT_NOTE = 0x0A000000, ///< MIDI note number
|
||||
PF_UNIT_RPM = 0x0B000000, ///< revolutions per minute
|
||||
};
|
||||
|
||||
/// A fake infinity value (because real infinity may break some hosts)
|
||||
#define FAKE_INFINITY (65536.0 * 65536.0)
|
||||
/// Check for infinity (with appropriate-ish tolerance)
|
||||
#define IS_FAKE_INFINITY(value) (fabs(value-FAKE_INFINITY) < 1.0)
|
||||
|
||||
/// Information record about plugin's menu command
|
||||
struct plugin_command_info
|
||||
{
|
||||
const char *label; ///< short command name / label
|
||||
const char *name; ///< human-readable command name
|
||||
const char *description; ///< description (for status line etc.)
|
||||
};
|
||||
|
||||
/// Range, default value, flags and names for a parameter
|
||||
struct parameter_properties
|
||||
{
|
||||
/// default value
|
||||
float def_value;
|
||||
/// minimum value
|
||||
float min;
|
||||
/// maximum value
|
||||
float max;
|
||||
/// number of steps (for an integer value from 0 to 100 this will be 101; for 0/90/180/270/360 this will be 5), or 0 for continuous
|
||||
float step;
|
||||
/// logical OR of parameter_flags
|
||||
uint32_t flags;
|
||||
/// for PF_ENUM: array of text values (from min to max step 1), otherwise NULL
|
||||
const char **choices;
|
||||
/// parameter label (for use in LV2 label field etc.)
|
||||
const char *short_name;
|
||||
/// parameter human-readable name
|
||||
const char *name;
|
||||
/// convert from [0, 1] range to [min, max] (applying scaling)
|
||||
float from_01(double value01) const;
|
||||
/// convert from [min, max] to [0, 1] range (applying reverse scaling)
|
||||
double to_01(float value) const;
|
||||
/// stringify (in sensible way)
|
||||
std::string to_string(float value) const;
|
||||
/// get required width (for reserving GUI space)
|
||||
int get_char_count() const;
|
||||
/// get increment step based on step value (if specified) and other factors
|
||||
float get_increment() const;
|
||||
};
|
||||
|
||||
struct cairo_iface
|
||||
{
|
||||
virtual void set_source_rgba(float r, float g, float b, float a = 1.f) = 0;
|
||||
virtual void set_line_width(float width) = 0;
|
||||
virtual ~cairo_iface() {}
|
||||
};
|
||||
|
||||
struct progress_report_iface
|
||||
{
|
||||
virtual void report_progress(float percentage, const std::string &message) = 0;
|
||||
virtual ~progress_report_iface() {}
|
||||
};
|
||||
|
||||
/// 'provides live line graph values' interface
|
||||
struct line_graph_iface
|
||||
{
|
||||
/// Obtain subindex'th graph of parameter 'index'
|
||||
/// @param index parameter/graph number (usually tied to particular plugin control port)
|
||||
/// @param subindex graph number (there may be multiple overlaid graphs for one parameter, eg. for monosynth 2x12dB filters)
|
||||
/// @param data buffer for normalized output values
|
||||
/// @param points number of points to fill
|
||||
/// @param context cairo context to adjust (for multicolour graphs etc.)
|
||||
/// @retval true graph data was returned; subindex+1 graph may or may not be available
|
||||
/// @retval false graph data was not returned; subindex+1 graph does not exist either
|
||||
virtual bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context) { return false; }
|
||||
|
||||
/// Obtain subindex'th dot of parameter 'index'
|
||||
/// @param index parameter/dot number (usually tied to particular plugin control port)
|
||||
/// @param subindex dot number (there may be multiple dots graphs for one parameter)
|
||||
virtual bool get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context) { return false; }
|
||||
|
||||
/// Obtain subindex'th dot of parameter 'index'
|
||||
/// @param index parameter/dot number (usually tied to particular plugin control port)
|
||||
/// @param subindex dot number (there may be multiple dots graphs for one parameter)
|
||||
virtual bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) { return false; }
|
||||
|
||||
/// Obtain subindex'th static graph of parameter index (static graphs are only dependent on parameter value, not plugin state)
|
||||
/// @param index parameter/graph number (usually tied to particular plugin control port)
|
||||
/// @param subindex graph number (there may be multiple overlaid graphs for one parameter, eg. for monosynth 2x12dB filters)
|
||||
/// @param value parameter value to pick the graph for
|
||||
/// @param data buffer for normalized output values
|
||||
/// @param points number of points to fill
|
||||
/// @param context cairo context to adjust (for multicolour graphs etc.)
|
||||
/// @retval true graph data was returned; subindex+1 graph may or may not be available
|
||||
/// @retval false graph data was not returned; subindex+1 graph does not exist either
|
||||
virtual bool get_static_graph(int index, int subindex, float value, float *data, int points, cairo_iface *context) { return false; }
|
||||
|
||||
/// Return which graphs need to be redrawn and which can be cached for later reuse
|
||||
/// @param generation 0 (at start) or the last value returned by the function (corresponds to a set of input values)
|
||||
/// @param subindex_graph First graph that has to be redrawn (because it depends on values that might have changed)
|
||||
/// @param subindex_dot First dot that has to be redrawn
|
||||
/// @param subindex_gridline First gridline/legend that has to be redrawn
|
||||
/// @retval Current generation (to pass when calling the function next time); if different than passed generation value, call the function again to retrieve which graph offsets should be put into cache
|
||||
virtual int get_changed_offsets(int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) { subindex_graph = subindex_dot = subindex_gridline = 0; return 0; }
|
||||
|
||||
/// Standard destructor to make compiler happy
|
||||
virtual ~line_graph_iface() {}
|
||||
};
|
||||
|
||||
enum table_column_type
|
||||
{
|
||||
TCT_UNKNOWN, ///< guard invalid type
|
||||
TCT_FLOAT, ///< float value (encoded as C locale string)
|
||||
TCT_ENUM, ///< enum value (see: 'values' array in table_column_info) - encoded as string base 10 representation of integer
|
||||
TCT_STRING, ///< string value (encoded as C-escaped string)
|
||||
TCT_OBJECT, ///< external object, encoded as string
|
||||
TCT_LABEL, ///< string value (encoded as C-escaped string)
|
||||
};
|
||||
|
||||
/// parameters of
|
||||
struct table_column_info
|
||||
{
|
||||
const char *name; ///< column label
|
||||
table_column_type type; ///< column data type
|
||||
float min; ///< minimum value (for float)
|
||||
float max; ///< maximum value (for float and enum)
|
||||
float def_value; ///< default value (for float and enum)
|
||||
const char **values; ///< NULL unless a TCT_ENUM, where it represents a NULL-terminated list of choices
|
||||
};
|
||||
|
||||
/// 'has string parameters containing tabular data' interface
|
||||
struct table_edit_iface
|
||||
{
|
||||
/// retrieve the table layout for specific parameter
|
||||
virtual const table_column_info *get_table_columns(int param) = 0;
|
||||
|
||||
/// return the current number of rows
|
||||
virtual uint32_t get_table_rows(int param) = 0;
|
||||
|
||||
/// retrieve data item from the plugin
|
||||
virtual std::string get_cell(int param, int row, int column) { return calf_utils::i2s(row)+":"+calf_utils::i2s(column); }
|
||||
|
||||
/// set data item to the plugin
|
||||
virtual void set_cell(int param, int row, int column, const std::string &src, std::string &error) { error.clear(); }
|
||||
|
||||
/// return a line graph interface for a specific parameter/column (unused for now)
|
||||
virtual line_graph_iface *get_graph_iface(int param, int column) { return NULL; }
|
||||
|
||||
/// return an editor name for a specific grid cell (unused for now - I don't even know how editors be implemented)
|
||||
virtual const char *get_cell_editor(int param, int column) { return NULL; }
|
||||
|
||||
virtual ~table_edit_iface() {}
|
||||
};
|
||||
|
||||
/// 'may receive configure variables' interface
|
||||
struct send_configure_iface
|
||||
{
|
||||
/// Called to set configure variable
|
||||
/// @param key variable name
|
||||
/// @param value variable content
|
||||
virtual void send_configure(const char *key, const char *value) = 0;
|
||||
|
||||
virtual ~send_configure_iface() {}
|
||||
};
|
||||
|
||||
/// 'may receive new status values' interface
|
||||
struct send_updates_iface
|
||||
{
|
||||
/// Called to set configure variable
|
||||
/// @param key variable name
|
||||
/// @param value variable content
|
||||
virtual void send_status(const char *key, const char *value) = 0;
|
||||
|
||||
virtual ~send_updates_iface() {}
|
||||
};
|
||||
|
||||
struct plugin_command_info;
|
||||
|
||||
/// General information about the plugin - @todo XXXKF lacks the "new" id-label-name triple
|
||||
struct ladspa_plugin_info
|
||||
{
|
||||
/// LADSPA ID
|
||||
uint32_t unique_id;
|
||||
/// plugin short name (camel case)
|
||||
const char *label;
|
||||
/// plugin human-readable name
|
||||
const char *name;
|
||||
/// maker (author)
|
||||
const char *maker;
|
||||
/// copyright notice
|
||||
const char *copyright;
|
||||
/// plugin type for LRDF/LV2
|
||||
const char *plugin_type;
|
||||
};
|
||||
|
||||
/// An interface returning metadata about a plugin
|
||||
struct plugin_metadata_iface
|
||||
{
|
||||
/// @return plugin long name
|
||||
virtual const char *get_name() = 0;
|
||||
/// @return plugin LV2 label
|
||||
virtual const char *get_id() = 0;
|
||||
/// @return plugin human-readable label
|
||||
virtual const char *get_label() = 0;
|
||||
/// @return total number of parameters
|
||||
virtual int get_param_count() = 0;
|
||||
/// Return custom XML
|
||||
virtual const char *get_gui_xml() = 0;
|
||||
/// @return number of audio inputs
|
||||
virtual int get_input_count()=0;
|
||||
/// @return number of audio outputs
|
||||
virtual int get_output_count()=0;
|
||||
/// @return true if plugin can work in hard-realtime conditions
|
||||
virtual bool is_rt_capable()=0;
|
||||
/// @return true if plugin has MIDI input
|
||||
virtual bool get_midi()=0;
|
||||
/// @return true if plugin has MIDI input
|
||||
virtual bool requires_midi()=0;
|
||||
/// @return port offset of first control (parameter) port (= number of audio inputs + number of audio outputs in all existing plugins as for 1 Aug 2008)
|
||||
virtual int get_param_port_offset() = 0;
|
||||
/// @return line_graph_iface if any
|
||||
virtual line_graph_iface *get_line_graph_iface() = 0;
|
||||
/// @return table_edit_iface if any
|
||||
virtual table_edit_iface *get_table_edit_iface() = 0;
|
||||
/// @return NULL-terminated list of menu commands
|
||||
virtual plugin_command_info *get_commands() { return NULL; }
|
||||
/// @return description structure for given parameter
|
||||
virtual parameter_properties *get_param_props(int param_no) = 0;
|
||||
/// @return retrieve names of audio ports (@note control ports are named in parameter_properties, not here)
|
||||
virtual const char **get_port_names() = 0;
|
||||
/// @return description structure for the plugin
|
||||
virtual const ladspa_plugin_info &get_plugin_info() = 0;
|
||||
/// is a given parameter a control voltage?
|
||||
virtual bool is_cv(int param_no) = 0;
|
||||
/// is the given parameter non-interpolated?
|
||||
virtual bool is_noisy(int param_no) = 0;
|
||||
/// does the plugin require message context? (or DSSI configure) may be slow
|
||||
virtual bool requires_message_context() = 0;
|
||||
/// does the plugin require string port extension? (or DSSI configure) may be slow
|
||||
virtual bool requires_string_ports() = 0;
|
||||
/// add all message context parameter numbers to the ports vector
|
||||
virtual void get_message_context_parameters(std::vector<int> &ports) = 0;
|
||||
|
||||
/// Do-nothing destructor to silence compiler warning
|
||||
virtual ~plugin_metadata_iface() {}
|
||||
};
|
||||
|
||||
/// Interface for host-GUI-plugin interaction (should be really split in two, but ... meh)
|
||||
struct plugin_ctl_iface: public virtual plugin_metadata_iface
|
||||
{
|
||||
/// @return value of given parameter
|
||||
virtual float get_param_value(int param_no) = 0;
|
||||
/// Set value of given parameter
|
||||
virtual void set_param_value(int param_no, float value) = 0;
|
||||
/// Load preset with given number
|
||||
virtual bool activate_preset(int bank, int program) = 0;
|
||||
/// @return volume level for port'th port (if supported by the implementation, currently only jack_host<Module> implements that by measuring signal level on plugin ports)
|
||||
virtual float get_level(unsigned int port)=0;
|
||||
/// Execute menu command with given number
|
||||
virtual void execute(int cmd_no)=0;
|
||||
/// Set a configure variable on a plugin
|
||||
virtual char *configure(const char *key, const char *value) { return NULL; }
|
||||
/// Send all configure variables set within a plugin to given destination (which may be limited to only those that plugin understands)
|
||||
virtual void send_configures(send_configure_iface *)=0;
|
||||
/// Restore all state (parameters and configure vars) to default values - implemented in giface.cpp
|
||||
virtual void clear_preset();
|
||||
/// Call a named function in a plugin - this will most likely be redesigned soon - and never used
|
||||
/// @retval false call has failed, result contains an error message
|
||||
virtual bool blobcall(const char *command, const std::string &request, std::string &result) { result = "Call not supported"; return false; }
|
||||
/// Update status variables changed since last_serial
|
||||
/// @return new last_serial
|
||||
virtual int send_status_updates(send_updates_iface *sui, int last_serial) { return last_serial; }
|
||||
/// Do-nothing destructor to silence compiler warning
|
||||
virtual ~plugin_ctl_iface() {}
|
||||
};
|
||||
|
||||
struct plugin_list_info_iface;
|
||||
|
||||
/// Get a list of all "large" (effect/synthesizer) plugins
|
||||
extern void get_all_plugins(std::vector<plugin_metadata_iface *> &plugins);
|
||||
/// Get a list of all "small" (module) plugins
|
||||
extern void get_all_small_plugins(plugin_list_info_iface *plii);
|
||||
/// Load and strdup a text file with GUI definition
|
||||
extern const char *load_gui_xml(const std::string &plugin_id);
|
||||
|
||||
/// Empty implementations for plugin functions. Note, that functions aren't virtual, because they're called via the particular
|
||||
/// subclass (flanger_audio_module etc) via template wrappers (ladspa_wrapper<> etc), not via base class pointer/reference
|
||||
template<class Metadata>
|
||||
class audio_module: public Metadata
|
||||
{
|
||||
public:
|
||||
typedef Metadata metadata_type;
|
||||
|
||||
progress_report_iface *progress_report;
|
||||
|
||||
audio_module() {
|
||||
progress_report = NULL;
|
||||
}
|
||||
|
||||
/// Handle MIDI Note On
|
||||
inline void note_on(int note, int velocity) {}
|
||||
/// Handle MIDI Note Off
|
||||
inline void note_off(int note, int velocity) {}
|
||||
/// Handle MIDI Program Change
|
||||
inline void program_change(int program) {}
|
||||
/// Handle MIDI Control Change
|
||||
inline void control_change(int controller, int value) {}
|
||||
/// Handle MIDI Pitch Bend
|
||||
/// @param value pitch bend value (-8192 to 8191, defined as in MIDI ie. 8191 = 200 ct by default)
|
||||
inline void pitch_bend(int value) {}
|
||||
/// Handle MIDI Channel Pressure
|
||||
/// @param value channel pressure (0 to 127)
|
||||
inline void channel_pressure(int value) {}
|
||||
/// Called when params are changed (before processing)
|
||||
inline void params_changed() {}
|
||||
/// LADSPA-esque activate function, except it is called after ports are connected, not before
|
||||
inline void activate() {}
|
||||
/// LADSPA-esque deactivate function
|
||||
inline void deactivate() {}
|
||||
/// Set sample rate for the plugin
|
||||
inline void set_sample_rate(uint32_t sr) { }
|
||||
/// Execute menu command with given number
|
||||
inline void execute(int cmd_no) {}
|
||||
/// DSSI configure call
|
||||
virtual char *configure(const char *key, const char *value) { return NULL; }
|
||||
/// Send all understood configure vars (none by default)
|
||||
inline void send_configures(send_configure_iface *sci) {}
|
||||
/// Send all supported status vars (none by default)
|
||||
inline int send_status_updates(send_updates_iface *sui, int last_serial) { return last_serial; }
|
||||
/// Reset parameter values for epp:trigger type parameters (ones activated by oneshot push button instead of check box)
|
||||
inline void params_reset() {}
|
||||
/// Called after instantiating (after all the feature pointers are set - including interfaces like progress_report_iface)
|
||||
inline void post_instantiate() {}
|
||||
/// Handle 'message context' port message
|
||||
/// @arg output_ports pointer to bit array of output port "changed" flags, note that 0 = first audio input, not first parameter (use input_count + output_count)
|
||||
inline uint32_t message_run(const void *valid_ports, void *output_ports) {
|
||||
fprintf(stderr, "ERROR: message run not implemented\n");
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
extern bool check_for_message_context_ports(parameter_properties *parameters, int count);
|
||||
extern bool check_for_string_ports(parameter_properties *parameters, int count);
|
||||
|
||||
#if USE_DSSI
|
||||
|
||||
enum line_graph_item
|
||||
{
|
||||
LGI_END = 0,
|
||||
LGI_GRAPH,
|
||||
LGI_SUBGRAPH,
|
||||
LGI_LEGEND,
|
||||
LGI_DOT,
|
||||
LGI_END_ITEM,
|
||||
LGI_SET_RGBA,
|
||||
LGI_SET_WIDTH,
|
||||
};
|
||||
|
||||
/// A class to send status updates via OSC
|
||||
struct dssi_feedback_sender
|
||||
{
|
||||
/// OSC client object used to send updates
|
||||
osctl::osc_client *client;
|
||||
/// Background thread handle
|
||||
pthread_t bg_thread;
|
||||
/// Quit flag (used to terminate the thread)
|
||||
bool quit;
|
||||
/// Indices of graphs to send
|
||||
std::vector<int> indices;
|
||||
/// Source for the graph data (interface to marshal)
|
||||
calf_plugins::line_graph_iface *graph;
|
||||
|
||||
dssi_feedback_sender(const char *URI, line_graph_iface *_graph, calf_plugins::parameter_properties *props, int num_params);
|
||||
void update();
|
||||
~dssi_feedback_sender();
|
||||
};
|
||||
#endif
|
||||
|
||||
/// Metadata base class template, to provide default versions of interface functions
|
||||
template<class Metadata>
|
||||
class plugin_metadata: public virtual plugin_metadata_iface
|
||||
{
|
||||
public:
|
||||
static const char *port_names[];
|
||||
static parameter_properties param_props[];
|
||||
static ladspa_plugin_info plugin_info;
|
||||
|
||||
// These below are stock implementations based on enums and static members in Metadata classes
|
||||
// they may be overridden to provide more interesting functionality
|
||||
|
||||
const char *get_name() { return Metadata::impl_get_name(); }
|
||||
const char *get_id() { return Metadata::impl_get_id(); }
|
||||
const char *get_label() { return Metadata::impl_get_label(); }
|
||||
int get_input_count() { return Metadata::in_count; }
|
||||
int get_output_count() { return Metadata::out_count; }
|
||||
int get_param_count() { return Metadata::param_count; }
|
||||
bool get_midi() { return Metadata::support_midi; }
|
||||
bool requires_midi() { return Metadata::require_midi; }
|
||||
bool is_rt_capable() { return Metadata::rt_capable; }
|
||||
line_graph_iface *get_line_graph_iface() { return dynamic_cast<line_graph_iface *>(this); }
|
||||
table_edit_iface *get_table_edit_iface() { return dynamic_cast<table_edit_iface *>(this); }
|
||||
int get_param_port_offset() { return Metadata::in_count + Metadata::out_count; }
|
||||
const char *get_gui_xml() { static const char *data_ptr = calf_plugins::load_gui_xml(get_id()); return data_ptr; }
|
||||
plugin_command_info *get_commands() { return NULL; }
|
||||
parameter_properties *get_param_props(int param_no) { return ¶m_props[param_no]; }
|
||||
const char **get_port_names() { return port_names; }
|
||||
bool is_cv(int param_no) { return true; }
|
||||
bool is_noisy(int param_no) { return false; }
|
||||
const ladspa_plugin_info &get_plugin_info() { return plugin_info; }
|
||||
bool requires_message_context() { return check_for_message_context_ports(param_props, Metadata::param_count); }
|
||||
bool requires_string_ports() { return check_for_string_ports(param_props, Metadata::param_count); }
|
||||
void get_message_context_parameters(std::vector<int> &ports) {
|
||||
for (int i = 0; i < get_param_count(); ++i) {
|
||||
if (get_param_props(i)->flags & PF_PROP_MSGCONTEXT)
|
||||
ports.push_back(i);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// A class for delegating metadata implementation to a "remote" metadata class.
|
||||
/// Used for GUI wrappers that cannot have a dependency on actual classes,
|
||||
/// and which instead take an "external" metadata object pointer, obtained
|
||||
/// through get_all_plugins.
|
||||
class plugin_metadata_proxy: public virtual plugin_metadata_iface
|
||||
{
|
||||
public:
|
||||
plugin_metadata_iface *impl;
|
||||
public:
|
||||
plugin_metadata_proxy(plugin_metadata_iface *_impl) { impl = _impl; }
|
||||
const char *get_name() { return impl->get_name(); }
|
||||
const char *get_id() { return impl->get_id(); }
|
||||
const char *get_label() { return impl->get_label(); }
|
||||
int get_input_count() { return impl->get_input_count(); }
|
||||
int get_output_count() { return impl->get_output_count(); }
|
||||
int get_param_count() { return impl->get_param_count(); }
|
||||
bool get_midi() { return impl->get_midi(); }
|
||||
bool requires_midi() { return impl->requires_midi(); }
|
||||
bool is_rt_capable() { return impl->is_rt_capable(); }
|
||||
line_graph_iface *get_line_graph_iface() { return impl->get_line_graph_iface(); }
|
||||
table_edit_iface *get_table_edit_iface() { return impl->get_table_edit_iface(); }
|
||||
int get_param_port_offset() { return impl->get_param_port_offset(); }
|
||||
const char *get_gui_xml() { return impl->get_gui_xml(); }
|
||||
plugin_command_info *get_commands() { return impl->get_commands(); }
|
||||
parameter_properties *get_param_props(int param_no) { return impl->get_param_props(param_no); }
|
||||
const char **get_port_names() { return impl->get_port_names(); }
|
||||
bool is_cv(int param_no) { return impl->is_cv(param_no); }
|
||||
bool is_noisy(int param_no) { return impl->is_noisy(param_no); }
|
||||
const ladspa_plugin_info &get_plugin_info() { return impl->get_plugin_info(); }
|
||||
bool requires_message_context() { return impl->requires_message_context(); }
|
||||
bool requires_string_ports() { return impl->requires_string_ports(); }
|
||||
void get_message_context_parameters(std::vector<int> &ports) { impl->get_message_context_parameters(ports); }
|
||||
};
|
||||
|
||||
#define CALF_PORT_NAMES(name) template<> const char *::plugin_metadata<name##_metadata>::port_names[]
|
||||
#define CALF_PORT_PROPS(name) template<> parameter_properties plugin_metadata<name##_metadata>::param_props[]
|
||||
#define CALF_PLUGIN_INFO(name) template<> calf_plugins::ladspa_plugin_info plugin_metadata<name##_metadata>::plugin_info
|
||||
#define PLUGIN_NAME_ID_LABEL(name, id, label) \
|
||||
static const char *impl_get_name() { return name; } \
|
||||
static const char *impl_get_id() { return id; } \
|
||||
static const char *impl_get_label() { return label; } \
|
||||
|
||||
|
||||
extern const char *calf_copyright_info;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
256
plugins/ladspa_effect/calf/calf/inertia.h
Normal file
256
plugins/ladspa_effect/calf/calf/inertia.h
Normal file
@@ -0,0 +1,256 @@
|
||||
/* Calf DSP Library
|
||||
* Basic "inertia" (parameter smoothing) classes.
|
||||
* Copyright (C) 2001-2007 Krzysztof Foltman
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __CALF_INERTIA_H
|
||||
#define __CALF_INERTIA_H
|
||||
|
||||
#include "primitives.h"
|
||||
|
||||
namespace dsp {
|
||||
|
||||
/// Algorithm for a constant time linear ramp
|
||||
class linear_ramp
|
||||
{
|
||||
public:
|
||||
int ramp_len;
|
||||
float mul, delta;
|
||||
public:
|
||||
/// Construct for given ramp length
|
||||
linear_ramp(int _ramp_len) {
|
||||
ramp_len = _ramp_len;
|
||||
mul = (float)(1.0f / ramp_len);
|
||||
}
|
||||
/// Change ramp length
|
||||
inline void set_length(int _ramp_len) {
|
||||
ramp_len = _ramp_len;
|
||||
mul = (float)(1.0f / ramp_len);
|
||||
}
|
||||
inline int length()
|
||||
{
|
||||
return ramp_len;
|
||||
}
|
||||
inline void start_ramp(float start, float end)
|
||||
{
|
||||
delta = mul * (end - start);
|
||||
}
|
||||
/// Return value after single step
|
||||
inline float ramp(float value)
|
||||
{
|
||||
return value + delta;
|
||||
}
|
||||
/// Return value after many steps
|
||||
inline float ramp_many(float value, int count)
|
||||
{
|
||||
return value + delta * count;
|
||||
}
|
||||
};
|
||||
|
||||
/// Algorithm for a constant time linear ramp
|
||||
class exponential_ramp
|
||||
{
|
||||
public:
|
||||
int ramp_len;
|
||||
float root, delta;
|
||||
public:
|
||||
exponential_ramp(int _ramp_len) {
|
||||
ramp_len = _ramp_len;
|
||||
root = (float)(1.0f / ramp_len);
|
||||
}
|
||||
inline void set_length(int _ramp_len) {
|
||||
ramp_len = _ramp_len;
|
||||
root = (float)(1.0f / ramp_len);
|
||||
}
|
||||
inline int length()
|
||||
{
|
||||
return ramp_len;
|
||||
}
|
||||
inline void start_ramp(float start, float end)
|
||||
{
|
||||
delta = pow(end / start, root);
|
||||
}
|
||||
/// Return value after single step
|
||||
inline float ramp(float value)
|
||||
{
|
||||
return value * delta;
|
||||
}
|
||||
/// Return value after many steps
|
||||
inline float ramp_many(float value, float count)
|
||||
{
|
||||
return value * pow(delta, count);
|
||||
}
|
||||
};
|
||||
|
||||
/// Generic inertia using ramping algorithm specified as template argument. The basic idea
|
||||
/// is producing smooth(ish) output for discrete input, using specified algorithm to go from
|
||||
/// last output value to input value. It is not the same as classic running average lowpass
|
||||
/// filter, because ramping time is finite and pre-determined (it calls ramp algorithm's length()
|
||||
/// function to obtain the expected ramp length)
|
||||
template<class Ramp>
|
||||
class inertia
|
||||
{
|
||||
public:
|
||||
float old_value;
|
||||
float value;
|
||||
unsigned int count;
|
||||
Ramp ramp;
|
||||
|
||||
public:
|
||||
inertia(const Ramp &_ramp, float init_value = 0.f)
|
||||
: ramp(_ramp)
|
||||
{
|
||||
value = old_value = init_value;
|
||||
count = 0;
|
||||
}
|
||||
/// Set value immediately (no inertia)
|
||||
void set_now(float _value)
|
||||
{
|
||||
value = old_value = _value;
|
||||
count = 0;
|
||||
}
|
||||
/// Set with inertia
|
||||
void set_inertia(float source)
|
||||
{
|
||||
if (source != old_value) {
|
||||
ramp.start_ramp(value, source);
|
||||
count = ramp.length();
|
||||
old_value = source;
|
||||
}
|
||||
}
|
||||
/// Get smoothed value of given source value
|
||||
inline float get(float source)
|
||||
{
|
||||
if (source != old_value) {
|
||||
ramp.start_ramp(value, source);
|
||||
count = ramp.length();
|
||||
old_value = source;
|
||||
}
|
||||
if (!count)
|
||||
return old_value;
|
||||
value = ramp.ramp(value);
|
||||
count--;
|
||||
if (!count) // finished ramping, set to desired value to get rid of accumulated rounding errors
|
||||
value = old_value;
|
||||
return value;
|
||||
}
|
||||
/// Get smoothed value assuming no new input
|
||||
inline float get()
|
||||
{
|
||||
if (!count)
|
||||
return old_value;
|
||||
value = ramp.ramp(value);
|
||||
count--;
|
||||
if (!count) // finished ramping, set to desired value to get rid of accumulated rounding errors
|
||||
value = old_value;
|
||||
return value;
|
||||
}
|
||||
/// Do one inertia step, without returning the new value and without changing destination value
|
||||
inline void step()
|
||||
{
|
||||
if (count) {
|
||||
value = ramp.ramp(value);
|
||||
count--;
|
||||
if (!count) // finished ramping, set to desired value to get rid of accumulated rounding errors
|
||||
value = old_value;
|
||||
}
|
||||
}
|
||||
/// Do many inertia steps, without returning the new value and without changing destination value
|
||||
inline void step_many(unsigned int steps)
|
||||
{
|
||||
if (steps < count) {
|
||||
// Skip only a part of the current ramping period
|
||||
value = ramp.ramp_many(value, steps);
|
||||
count -= steps;
|
||||
if (!count) // finished ramping, set to desired value to get rid of accumulated rounding errors
|
||||
value = old_value;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The whole ramping period has been skipped, just go to destination
|
||||
value = old_value;
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
/// Get last smoothed value, without affecting anything
|
||||
inline float get_last() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
/// Is it still ramping?
|
||||
inline bool active() const
|
||||
{
|
||||
return count > 0;
|
||||
}
|
||||
};
|
||||
|
||||
class once_per_n
|
||||
{
|
||||
public:
|
||||
unsigned int frequency;
|
||||
unsigned int left;
|
||||
public:
|
||||
once_per_n(unsigned int _frequency)
|
||||
: frequency(_frequency), left(_frequency)
|
||||
{}
|
||||
inline void start()
|
||||
{
|
||||
left = frequency;
|
||||
}
|
||||
/// Set timer to "elapsed" state (elapsed() will return true during next call)
|
||||
inline void signal()
|
||||
{
|
||||
left = 0;
|
||||
}
|
||||
inline unsigned int get(unsigned int desired)
|
||||
{
|
||||
if (desired > left) {
|
||||
desired = left;
|
||||
left = 0;
|
||||
return desired;
|
||||
}
|
||||
left -= desired;
|
||||
return desired;
|
||||
}
|
||||
inline bool elapsed()
|
||||
{
|
||||
if (!left) {
|
||||
left = frequency;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class gain_smoothing: public inertia<linear_ramp>
|
||||
{
|
||||
public:
|
||||
gain_smoothing()
|
||||
: inertia<linear_ramp>(linear_ramp(64))
|
||||
{
|
||||
}
|
||||
void set_sample_rate(int sr)
|
||||
{
|
||||
ramp = linear_ramp(sr / 441);
|
||||
}
|
||||
// to change param, use set_inertia(value)
|
||||
// to read param, use get()
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
516
plugins/ladspa_effect/calf/calf/ladspa_wrap.h
Normal file
516
plugins/ladspa_effect/calf/calf/ladspa_wrap.h
Normal file
@@ -0,0 +1,516 @@
|
||||
/* Calf DSP Library
|
||||
* API wrappers for LADSPA/DSSI
|
||||
*
|
||||
* Copyright (C) 2007-2008 Krzysztof Foltman
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __CALF_LADSPA_WRAP_H
|
||||
#define __CALF_LADSPA_WRAP_H
|
||||
|
||||
#if USE_LADSPA
|
||||
|
||||
#include <ladspa.h>
|
||||
#if USE_DSSI
|
||||
#include <dssi.h>
|
||||
#endif
|
||||
#include "giface.h"
|
||||
|
||||
namespace calf_plugins {
|
||||
|
||||
template<class Module>
|
||||
inline int calc_real_param_count()
|
||||
{
|
||||
for (int i=0; i < Module::param_count; i++)
|
||||
{
|
||||
if ((Module::param_props[i].flags & PF_TYPEMASK) >= PF_STRING)
|
||||
return i;
|
||||
}
|
||||
return Module::param_count;
|
||||
}
|
||||
|
||||
/// A template implementing plugin_ctl_iface for a given plugin
|
||||
template<class Module>
|
||||
struct ladspa_instance: public Module, public plugin_ctl_iface
|
||||
{
|
||||
bool activate_flag;
|
||||
#if USE_DSSI
|
||||
dssi_feedback_sender *feedback_sender;
|
||||
#endif
|
||||
|
||||
static int real_param_count()
|
||||
{
|
||||
static int _real_param_count = calc_real_param_count<Module>();
|
||||
return _real_param_count;
|
||||
}
|
||||
ladspa_instance()
|
||||
{
|
||||
for (int i=0; i < Module::in_count; i++)
|
||||
Module::ins[i] = NULL;
|
||||
for (int i=0; i < Module::out_count; i++)
|
||||
Module::outs[i] = NULL;
|
||||
int rpc = real_param_count();
|
||||
for (int i=0; i < rpc; i++)
|
||||
Module::params[i] = NULL;
|
||||
activate_flag = true;
|
||||
#if USE_DSSI
|
||||
feedback_sender = NULL;
|
||||
#endif
|
||||
}
|
||||
virtual parameter_properties *get_param_props(int param_no)
|
||||
{
|
||||
return &Module::param_props[param_no];
|
||||
}
|
||||
virtual float get_param_value(int param_no)
|
||||
{
|
||||
// XXXKF hack
|
||||
if (param_no >= real_param_count())
|
||||
return 0;
|
||||
return *Module::params[param_no];
|
||||
}
|
||||
virtual void set_param_value(int param_no, float value)
|
||||
{
|
||||
// XXXKF hack
|
||||
if (param_no >= real_param_count())
|
||||
return;
|
||||
*Module::params[param_no] = value;
|
||||
}
|
||||
virtual int get_param_count()
|
||||
{
|
||||
return real_param_count();
|
||||
}
|
||||
virtual int get_param_port_offset()
|
||||
{
|
||||
return Module::in_count + Module::out_count;
|
||||
}
|
||||
virtual const char *get_gui_xml() {
|
||||
return Module::get_gui_xml();
|
||||
}
|
||||
virtual line_graph_iface *get_line_graph_iface()
|
||||
{
|
||||
return dynamic_cast<line_graph_iface *>(this);
|
||||
}
|
||||
virtual bool activate_preset(int bank, int program) {
|
||||
return false;
|
||||
}
|
||||
virtual const char *get_name()
|
||||
{
|
||||
return Module::get_name();
|
||||
}
|
||||
virtual const char *get_id()
|
||||
{
|
||||
return Module::get_id();
|
||||
}
|
||||
virtual const char *get_label()
|
||||
{
|
||||
return Module::get_label();
|
||||
}
|
||||
virtual char *configure(const char *key, const char *value)
|
||||
{
|
||||
#if USE_DSSI
|
||||
if (!strcmp(key, "OSC:FEEDBACK_URI"))
|
||||
{
|
||||
line_graph_iface *lgi = dynamic_cast<line_graph_iface *>(this);
|
||||
if (!lgi)
|
||||
return NULL;
|
||||
if (*value)
|
||||
{
|
||||
if (feedback_sender) {
|
||||
delete feedback_sender;
|
||||
feedback_sender = NULL;
|
||||
}
|
||||
feedback_sender = new dssi_feedback_sender(value, lgi, get_param_props(0), get_param_count());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (feedback_sender) {
|
||||
delete feedback_sender;
|
||||
feedback_sender = NULL;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
if (!strcmp(key, "OSC:UPDATE"))
|
||||
{
|
||||
if (feedback_sender)
|
||||
feedback_sender->update();
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (!strcmp(key, "ExecCommand"))
|
||||
{
|
||||
if (*value)
|
||||
{
|
||||
execute(atoi(value));
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
return Module::configure(key, value);
|
||||
}
|
||||
virtual int get_input_count() { return Module::in_count; }
|
||||
virtual int get_output_count() { return Module::out_count; }
|
||||
virtual bool get_midi() { return Module::support_midi; }
|
||||
virtual float get_level(unsigned int port) { return 0.f; }
|
||||
virtual void execute(int cmd_no) {
|
||||
Module::execute(cmd_no);
|
||||
}
|
||||
virtual void send_configures(send_configure_iface *sci) {
|
||||
Module::send_configures(sci);
|
||||
}
|
||||
};
|
||||
|
||||
/// A wrapper class for plugin class object (there is only one ladspa_wrapper for many instances of the same plugin)
|
||||
template<class Module>
|
||||
struct ladspa_wrapper
|
||||
{
|
||||
typedef ladspa_instance<Module> instance;
|
||||
|
||||
/// LADSPA descriptor
|
||||
static LADSPA_Descriptor descriptor;
|
||||
/// LADSPA descriptor for DSSI (uses a different name for the plugin, otherwise same as descriptor)
|
||||
static LADSPA_Descriptor descriptor_for_dssi;
|
||||
#if USE_DSSI
|
||||
/// Extended DSSI descriptor (points to descriptor_for_dssi for things like name/label/port info etc.)
|
||||
static DSSI_Descriptor dssi_descriptor;
|
||||
static DSSI_Program_Descriptor dssi_default_program;
|
||||
|
||||
static std::vector<plugin_preset> *presets;
|
||||
static std::vector<DSSI_Program_Descriptor> *preset_descs;
|
||||
#endif
|
||||
|
||||
ladspa_wrapper()
|
||||
{
|
||||
int ins = Module::in_count;
|
||||
int outs = Module::out_count;
|
||||
int params = ladspa_instance<Module>::real_param_count();
|
||||
ladspa_plugin_info &plugin_info = Module::plugin_info;
|
||||
descriptor.UniqueID = plugin_info.unique_id;
|
||||
descriptor.Label = plugin_info.label;
|
||||
descriptor.Name = strdup((std::string(plugin_info.name) + " LADSPA").c_str());
|
||||
descriptor.Maker = plugin_info.maker;
|
||||
descriptor.Copyright = plugin_info.copyright;
|
||||
descriptor.Properties = Module::rt_capable ? LADSPA_PROPERTY_HARD_RT_CAPABLE : 0;
|
||||
descriptor.PortCount = ins + outs + params;
|
||||
descriptor.PortNames = new char *[descriptor.PortCount];
|
||||
descriptor.PortDescriptors = new LADSPA_PortDescriptor[descriptor.PortCount];
|
||||
descriptor.PortRangeHints = new LADSPA_PortRangeHint[descriptor.PortCount];
|
||||
int i;
|
||||
for (i = 0; i < ins + outs; i++)
|
||||
{
|
||||
LADSPA_PortRangeHint &prh = ((LADSPA_PortRangeHint *)descriptor.PortRangeHints)[i];
|
||||
((int *)descriptor.PortDescriptors)[i] = i < ins ? LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO
|
||||
: i < ins + outs ? LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO
|
||||
: LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
|
||||
prh.HintDescriptor = 0;
|
||||
((const char **)descriptor.PortNames)[i] = Module::port_names[i];
|
||||
}
|
||||
for (; i < ins + outs + params; i++)
|
||||
{
|
||||
LADSPA_PortRangeHint &prh = ((LADSPA_PortRangeHint *)descriptor.PortRangeHints)[i];
|
||||
parameter_properties &pp = Module::param_props[i - ins - outs];
|
||||
((int *)descriptor.PortDescriptors)[i] =
|
||||
LADSPA_PORT_CONTROL | (pp.flags & PF_PROP_OUTPUT ? LADSPA_PORT_OUTPUT : LADSPA_PORT_INPUT);
|
||||
prh.HintDescriptor = LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW;
|
||||
((const char **)descriptor.PortNames)[i] = pp.name;
|
||||
prh.LowerBound = pp.min;
|
||||
prh.UpperBound = pp.max;
|
||||
switch(pp.flags & PF_TYPEMASK) {
|
||||
case PF_BOOL:
|
||||
prh.HintDescriptor |= LADSPA_HINT_TOGGLED;
|
||||
prh.HintDescriptor &= ~(LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW);
|
||||
break;
|
||||
case PF_INT:
|
||||
case PF_ENUM:
|
||||
prh.HintDescriptor |= LADSPA_HINT_INTEGER;
|
||||
break;
|
||||
default: {
|
||||
int defpt = (int)(100 * (pp.def_value - pp.min) / (pp.max - pp.min));
|
||||
if ((pp.flags & PF_SCALEMASK) == PF_SCALE_LOG)
|
||||
defpt = (int)(100 * log(pp.def_value / pp.min) / log(pp.max / pp.min));
|
||||
if (defpt < 12)
|
||||
prh.HintDescriptor |= LADSPA_HINT_DEFAULT_MINIMUM;
|
||||
else if (defpt < 37)
|
||||
prh.HintDescriptor |= LADSPA_HINT_DEFAULT_LOW;
|
||||
else if (defpt < 63)
|
||||
prh.HintDescriptor |= LADSPA_HINT_DEFAULT_MIDDLE;
|
||||
else if (defpt < 88)
|
||||
prh.HintDescriptor |= LADSPA_HINT_DEFAULT_HIGH;
|
||||
else
|
||||
prh.HintDescriptor |= LADSPA_HINT_DEFAULT_MAXIMUM;
|
||||
}
|
||||
}
|
||||
if (pp.def_value == 0 || pp.def_value == 1 || pp.def_value == 100 || pp.def_value == 440 ) {
|
||||
prh.HintDescriptor &= ~LADSPA_HINT_DEFAULT_MASK;
|
||||
if (pp.def_value == 1)
|
||||
prh.HintDescriptor |= LADSPA_HINT_DEFAULT_1;
|
||||
else if (pp.def_value == 100)
|
||||
prh.HintDescriptor |= LADSPA_HINT_DEFAULT_100;
|
||||
else if (pp.def_value == 440)
|
||||
prh.HintDescriptor |= LADSPA_HINT_DEFAULT_440;
|
||||
else
|
||||
prh.HintDescriptor |= LADSPA_HINT_DEFAULT_0;
|
||||
}
|
||||
switch(pp.flags & PF_SCALEMASK) {
|
||||
case PF_SCALE_LOG:
|
||||
prh.HintDescriptor |= LADSPA_HINT_LOGARITHMIC;
|
||||
break;
|
||||
}
|
||||
}
|
||||
descriptor.ImplementationData = this;
|
||||
descriptor.instantiate = cb_instantiate;
|
||||
descriptor.connect_port = cb_connect;
|
||||
descriptor.activate = cb_activate;
|
||||
descriptor.run = cb_run;
|
||||
descriptor.run_adding = NULL;
|
||||
descriptor.set_run_adding_gain = NULL;
|
||||
descriptor.deactivate = cb_deactivate;
|
||||
descriptor.cleanup = cb_cleanup;
|
||||
#if USE_DSSI
|
||||
memcpy(&descriptor_for_dssi, &descriptor, sizeof(descriptor));
|
||||
descriptor_for_dssi.Name = strdup((std::string(plugin_info.name) + " DSSI").c_str());
|
||||
memset(&dssi_descriptor, 0, sizeof(dssi_descriptor));
|
||||
dssi_descriptor.DSSI_API_Version = 1;
|
||||
dssi_descriptor.LADSPA_Plugin = &descriptor_for_dssi;
|
||||
dssi_descriptor.configure = cb_configure;
|
||||
dssi_descriptor.get_program = cb_get_program;
|
||||
dssi_descriptor.select_program = cb_select_program;
|
||||
if (Module::support_midi)
|
||||
dssi_descriptor.run_synth = cb_run_synth;
|
||||
|
||||
presets = new std::vector<plugin_preset>;
|
||||
preset_descs = new std::vector<DSSI_Program_Descriptor>;
|
||||
|
||||
preset_list plist_tmp, plist;
|
||||
plist.load_defaults(true);
|
||||
plist_tmp.load_defaults(false);
|
||||
plist.presets.insert(plist.presets.end(), plist_tmp.presets.begin(), plist_tmp.presets.end());
|
||||
|
||||
// XXXKF this assumes that plugin name in preset is case-insensitive equal to plugin label
|
||||
// if I forget about this, I'll be in a deep trouble
|
||||
dssi_default_program.Bank = 0;
|
||||
dssi_default_program.Program = 0;
|
||||
dssi_default_program.Name = "default";
|
||||
|
||||
int pos = 1;
|
||||
for (unsigned int i = 0; i < plist.presets.size(); i++)
|
||||
{
|
||||
plugin_preset &pp = plist.presets[i];
|
||||
if (strcasecmp(pp.plugin.c_str(), descriptor.Label))
|
||||
continue;
|
||||
DSSI_Program_Descriptor pd;
|
||||
pd.Bank = pos >> 7;
|
||||
pd.Program = pos++;
|
||||
pd.Name = pp.name.c_str();
|
||||
preset_descs->push_back(pd);
|
||||
presets->push_back(pp);
|
||||
}
|
||||
// printf("presets = %p:%d name = %s\n", presets, presets->size(), descriptor.Label);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
~ladspa_wrapper()
|
||||
{
|
||||
delete []descriptor.PortNames;
|
||||
delete []descriptor.PortDescriptors;
|
||||
delete []descriptor.PortRangeHints;
|
||||
#if USE_DSSI
|
||||
presets->clear();
|
||||
preset_descs->clear();
|
||||
delete presets;
|
||||
delete preset_descs;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// LADSPA instantiation function (create a plugin instance)
|
||||
static LADSPA_Handle cb_instantiate(const struct _LADSPA_Descriptor * Descriptor, unsigned long sample_rate)
|
||||
{
|
||||
instance *mod = new instance();
|
||||
mod->set_sample_rate(sample_rate);
|
||||
mod->post_instantiate();
|
||||
return mod;
|
||||
}
|
||||
|
||||
#if USE_DSSI
|
||||
/// DSSI get program descriptor function; for 0, it returns the default program (from parameter properties table), for others, it uses global or user preset
|
||||
static const DSSI_Program_Descriptor *cb_get_program(LADSPA_Handle Instance, unsigned long index) {
|
||||
if (index > presets->size())
|
||||
return NULL;
|
||||
if (index)
|
||||
return &(*preset_descs)[index - 1];
|
||||
return &dssi_default_program;
|
||||
}
|
||||
|
||||
/// DSSI select program function; for 0, it sets the defaults, for others, it sets global or user preset
|
||||
static void cb_select_program(LADSPA_Handle Instance, unsigned long Bank, unsigned long Program) {
|
||||
instance *mod = (instance *)Instance;
|
||||
unsigned int no = (Bank << 7) + Program - 1;
|
||||
// printf("no = %d presets = %p:%d\n", no, presets, presets->size());
|
||||
if (no == -1U) {
|
||||
int rpc = ladspa_instance<Module>::real_param_count();
|
||||
for (int i =0 ; i < rpc; i++)
|
||||
*mod->params[i] = Module::param_props[i].def_value;
|
||||
return;
|
||||
}
|
||||
if (no >= presets->size())
|
||||
return;
|
||||
plugin_preset &p = (*presets)[no];
|
||||
// printf("activating preset %s\n", p.name.c_str());
|
||||
p.activate(mod);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// LADSPA port connection function
|
||||
static void cb_connect(LADSPA_Handle Instance, unsigned long port, LADSPA_Data *DataLocation) {
|
||||
unsigned long ins = Module::in_count;
|
||||
unsigned long outs = Module::out_count;
|
||||
unsigned long params = ladspa_instance<Module>::real_param_count();
|
||||
instance *const mod = (instance *)Instance;
|
||||
if (port < ins)
|
||||
mod->ins[port] = DataLocation;
|
||||
else if (port < ins + outs)
|
||||
mod->outs[port - ins] = DataLocation;
|
||||
else if (port < ins + outs + params) {
|
||||
int i = port - ins - outs;
|
||||
mod->params[i] = DataLocation;
|
||||
*mod->params[i] = Module::param_props[i].def_value;
|
||||
}
|
||||
}
|
||||
|
||||
/// LADSPA activate function (note that at this moment the ports are not set)
|
||||
static void cb_activate(LADSPA_Handle Instance) {
|
||||
instance *const mod = (instance *)Instance;
|
||||
mod->activate_flag = true;
|
||||
}
|
||||
|
||||
/// utility function: zero port values if mask is 0
|
||||
static inline void zero_by_mask(Module *module, uint32_t mask, uint32_t offset, uint32_t nsamples)
|
||||
{
|
||||
for (int i=0; i<Module::out_count; i++) {
|
||||
if ((mask & (1 << i)) == 0) {
|
||||
dsp::zero(module->outs[i] + offset, nsamples);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// LADSPA run function - does set sample rate / activate logic when it's run first time after activation
|
||||
static void cb_run(LADSPA_Handle Instance, unsigned long SampleCount) {
|
||||
instance *const mod = (instance *)Instance;
|
||||
if (mod->activate_flag)
|
||||
{
|
||||
mod->activate();
|
||||
mod->activate_flag = false;
|
||||
}
|
||||
mod->params_changed();
|
||||
process_slice(mod, 0, SampleCount);
|
||||
}
|
||||
|
||||
/// utility function: call process, and if it returned zeros in output masks, zero out the relevant output port buffers
|
||||
static inline void process_slice(Module *mod, uint32_t offset, uint32_t end)
|
||||
{
|
||||
while(offset < end)
|
||||
{
|
||||
uint32_t newend = std::min(offset + MAX_SAMPLE_RUN, end);
|
||||
uint32_t out_mask = mod->process(offset, newend - offset, -1, -1);
|
||||
zero_by_mask(mod, out_mask, offset, newend - offset);
|
||||
offset = newend;
|
||||
}
|
||||
}
|
||||
|
||||
#if USE_DSSI
|
||||
/// DSSI "run synth" function, same as run() except it allows for event delivery
|
||||
static void cb_run_synth(LADSPA_Handle Instance, unsigned long SampleCount,
|
||||
snd_seq_event_t *Events, unsigned long EventCount) {
|
||||
instance *const mod = (instance *)Instance;
|
||||
if (mod->activate_flag)
|
||||
{
|
||||
mod->activate();
|
||||
mod->activate_flag = false;
|
||||
}
|
||||
mod->params_changed();
|
||||
|
||||
uint32_t offset = 0;
|
||||
for (uint32_t e = 0; e < EventCount; e++)
|
||||
{
|
||||
uint32_t timestamp = Events[e].time.tick;
|
||||
if (timestamp != offset)
|
||||
process_slice(mod, offset, timestamp);
|
||||
process_dssi_event(mod, Events[e]);
|
||||
offset = timestamp;
|
||||
}
|
||||
if (offset != SampleCount)
|
||||
process_slice(mod, offset, SampleCount);
|
||||
}
|
||||
|
||||
/// DSSI configure function (named properties)
|
||||
static char *cb_configure(LADSPA_Handle Instance,
|
||||
const char *Key,
|
||||
const char *Value)
|
||||
{
|
||||
instance *const mod = (instance *)Instance;
|
||||
return mod->configure(Key, Value);
|
||||
}
|
||||
|
||||
/// Utility function: handle MIDI event (only handles a subset in this version)
|
||||
static void process_dssi_event(Module *module, snd_seq_event_t &event)
|
||||
{
|
||||
switch(event.type) {
|
||||
case SND_SEQ_EVENT_NOTEON:
|
||||
module->note_on(event.data.note.note, event.data.note.velocity);
|
||||
break;
|
||||
case SND_SEQ_EVENT_NOTEOFF:
|
||||
module->note_off(event.data.note.note, event.data.note.velocity);
|
||||
break;
|
||||
case SND_SEQ_EVENT_PGMCHANGE:
|
||||
module->program_change(event.data.control.value);
|
||||
break;
|
||||
case SND_SEQ_EVENT_CONTROLLER:
|
||||
module->control_change(event.data.control.param, event.data.control.value);
|
||||
break;
|
||||
case SND_SEQ_EVENT_PITCHBEND:
|
||||
module->pitch_bend(event.data.control.value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/// LADSPA deactivate function
|
||||
static void cb_deactivate(LADSPA_Handle Instance) {
|
||||
instance *const mod = (instance *)Instance;
|
||||
mod->deactivate();
|
||||
}
|
||||
|
||||
/// LADSPA cleanup (delete instance) function
|
||||
static void cb_cleanup(LADSPA_Handle Instance) {
|
||||
instance *const mod = (instance *)Instance;
|
||||
delete mod;
|
||||
}
|
||||
|
||||
/// Get a wrapper singleton - used to prevent initialization order problems which were present in older versions
|
||||
static ladspa_wrapper &get() {
|
||||
static ladspa_wrapper instance;
|
||||
return instance;
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
90
plugins/ladspa_effect/calf/calf/loudness.h
Normal file
90
plugins/ladspa_effect/calf/calf/loudness.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/* Calf DSP Library
|
||||
* A-weighting filter for
|
||||
* Copyright (C) 2001-2007 Krzysztof Foltman
|
||||
*
|
||||
* Most of code in this file is based on freely
|
||||
* available other work of other people (filter equations).
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __CALF_LOUDNESS_H
|
||||
#define __CALF_LOUDNESS_H
|
||||
|
||||
#include "biquad.h"
|
||||
|
||||
namespace dsp {
|
||||
|
||||
class aweighter {
|
||||
public:
|
||||
biquad_d2<float> bq1, bq2, bq3;
|
||||
|
||||
/// Produce one output sample from one input sample
|
||||
float process(float sample)
|
||||
{
|
||||
return bq1.process(bq2.process(bq3.process(sample)));
|
||||
}
|
||||
|
||||
/// Set sample rate (updates filter coefficients)
|
||||
void set(float sr)
|
||||
{
|
||||
// analog coeffs taken from: http://www.diracdelta.co.uk/science/source/a/w/aweighting/source.html
|
||||
// first we need to adjust them by doing some obscene sort of reverse pre-warping (a broken one, too!)
|
||||
float f1 = biquad_coeffs<float>::unwarpf(20.6f, sr);
|
||||
float f2 = biquad_coeffs<float>::unwarpf(107.7f, sr);
|
||||
float f3 = biquad_coeffs<float>::unwarpf(738.f, sr);
|
||||
float f4 = biquad_coeffs<float>::unwarpf(12200.f, sr);
|
||||
// then map s domain to z domain using bilinear transform
|
||||
// note: f1 and f4 are double poles
|
||||
bq1.set_bilinear(0, 0, 1, f1*f1, 2 * f1, 1);
|
||||
bq2.set_bilinear(1, 0, 0, f2*f3, f2 + f3, 1);
|
||||
bq3.set_bilinear(0, 0, 1, f4*f4, 2 * f4, 1);
|
||||
// the coeffs above give non-normalized value, so it should be normalized to produce 0dB at 1 kHz
|
||||
// find actual gain
|
||||
float gain1kHz = freq_gain(1000.0, sr);
|
||||
// divide one filter's x[n-m] coefficients by that value
|
||||
float gc = 1.0 / gain1kHz;
|
||||
bq1.a0 *= gc;
|
||||
bq1.a1 *= gc;
|
||||
bq1.a2 *= gc;
|
||||
}
|
||||
|
||||
/// Reset to zero if at risk of denormals
|
||||
void sanitize()
|
||||
{
|
||||
bq1.sanitize();
|
||||
bq2.sanitize();
|
||||
bq3.sanitize();
|
||||
}
|
||||
|
||||
/// Reset state to zero
|
||||
void reset()
|
||||
{
|
||||
bq1.reset();
|
||||
bq2.reset();
|
||||
bq3.reset();
|
||||
}
|
||||
|
||||
/// Gain and a given frequency
|
||||
float freq_gain(float freq, float sr)
|
||||
{
|
||||
return bq1.freq_gain(freq, sr) * bq2.freq_gain(freq, sr) * bq3.freq_gain(freq, sr);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
281
plugins/ladspa_effect/calf/calf/lv2helpers.h
Normal file
281
plugins/ladspa_effect/calf/calf/lv2helpers.h
Normal file
@@ -0,0 +1,281 @@
|
||||
/* Calf DSP Library
|
||||
* LV2-related helper classes and functions
|
||||
*
|
||||
* Copyright (C) 2001-2008 Krzysztof Foltman
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef CALF_LV2HELPERS_H
|
||||
#define CALF_LV2HELPERS_H
|
||||
|
||||
#if USE_LV2
|
||||
|
||||
#include <calf/lv2_event.h>
|
||||
#include <calf/lv2_uri_map.h>
|
||||
|
||||
class uri_map_access
|
||||
{
|
||||
public:
|
||||
/// URI map feature pointer (previously in a mixin, but polymorphic ports made it necessary for most plugins)
|
||||
LV2_URI_Map_Feature *uri_map;
|
||||
|
||||
uri_map_access()
|
||||
: uri_map(NULL)
|
||||
{}
|
||||
|
||||
/// Map an URI through an URI map
|
||||
uint32_t map_uri(const char *ns, const char *URI)
|
||||
{
|
||||
if (uri_map)
|
||||
return uri_map->uri_to_id(uri_map->callback_data, ns, URI);
|
||||
return 0;
|
||||
}
|
||||
/// Called on instantiation for every LV2 feature sent by a host
|
||||
void use_feature(const char *URI, void *data) {
|
||||
if (!strcmp(URI, LV2_URI_MAP_URI))
|
||||
{
|
||||
uri_map = (LV2_URI_Map_Feature *)data;
|
||||
map_uris();
|
||||
}
|
||||
}
|
||||
virtual void map_uris()
|
||||
{
|
||||
}
|
||||
virtual ~uri_map_access() {}
|
||||
};
|
||||
|
||||
/// A mixin for adding the event feature and URI map to the small plugin
|
||||
template<class T>
|
||||
class event_mixin: public T
|
||||
{
|
||||
public:
|
||||
/// Event feature pointer
|
||||
LV2_Event_Feature *event_feature;
|
||||
virtual void use_feature(const char *URI, void *data) {
|
||||
if (!strcmp(URI, LV2_EVENT_URI))
|
||||
{
|
||||
event_feature = (LV2_Event_Feature *)data;
|
||||
}
|
||||
T::use_feature(URI, data);
|
||||
}
|
||||
/// Create a reference
|
||||
inline void ref_event(LV2_Event *event) { event_feature->lv2_event_ref(event_feature->callback_data, event); }
|
||||
/// Destroy a reference
|
||||
inline void unref_event(LV2_Event *event) { event_feature->lv2_event_unref(event_feature->callback_data, event); }
|
||||
};
|
||||
|
||||
/// A mixin for adding the URI map and MIDI event type retrieval to small plugins
|
||||
template<class T>
|
||||
class midi_mixin: public virtual event_mixin<T>
|
||||
{
|
||||
public:
|
||||
/// MIDI event ID, as resolved using the URI map feature
|
||||
uint32_t midi_event_type;
|
||||
virtual void map_uris() {
|
||||
midi_event_type = this->map_uri("http://lv2plug.in/ns/ext/event", "http://lv2plug.in/ns/ext/midi#MidiEvent");
|
||||
printf("MIDI event type = %d\n", midi_event_type);
|
||||
event_mixin<T>::map_uris();
|
||||
}
|
||||
};
|
||||
|
||||
/// A mixin for adding the URI map and MIDI event type retrieval to small plugins
|
||||
template<class T>
|
||||
class message_mixin: public virtual event_mixin<T>
|
||||
{
|
||||
public:
|
||||
/// MIDI event ID, as resolved using the URI map feature
|
||||
uint32_t message_event_type;
|
||||
virtual void map_uris() {
|
||||
message_event_type = this->map_uri("http://lv2plug.in/ns/ext/event", "http://lv2plug.in/ns/dev/msg#MessageEvent");
|
||||
printf("Message event type = %d\n", message_event_type);
|
||||
event_mixin<T>::map_uris();
|
||||
}
|
||||
};
|
||||
|
||||
/// LV2 event structure + payload as 0-length array for easy access
|
||||
struct lv2_event: public LV2_Event
|
||||
{
|
||||
uint8_t data[];
|
||||
inline lv2_event &operator=(const lv2_event &src) {
|
||||
*(LV2_Event *)this = (const LV2_Event &)src;
|
||||
memcpy(data, src.data, src.size);
|
||||
return *this;
|
||||
}
|
||||
/// Returns a 64-bit timestamp for easy and inefficient comparison
|
||||
inline uint64_t timestamp() const {
|
||||
return ((uint64_t)frames << 32) | subframes;
|
||||
}
|
||||
private:
|
||||
/// forbid default constructor - this object cannot be constructed, only obtained via cast from LV2_Event* (or &) to lv2_event* (or &)
|
||||
lv2_event() {}
|
||||
/// forbid copy constructor - see default constructor
|
||||
lv2_event(const lv2_event &) {}
|
||||
};
|
||||
|
||||
/// A read-only iterator-like object for reading from event buffers
|
||||
class event_port_read_iterator
|
||||
{
|
||||
protected:
|
||||
const LV2_Event_Buffer *buffer;
|
||||
uint32_t offset;
|
||||
public:
|
||||
/// Default constructor creating a useless iterator you can assign to
|
||||
event_port_read_iterator()
|
||||
: buffer(NULL)
|
||||
, offset(0)
|
||||
{
|
||||
}
|
||||
|
||||
/// Create an iterator based on specified buffer and index/offset values
|
||||
event_port_read_iterator(const LV2_Event_Buffer *_buffer, uint32_t _offset = 0)
|
||||
: buffer(_buffer)
|
||||
, offset(0)
|
||||
{
|
||||
}
|
||||
|
||||
/// Are any data left to be read?
|
||||
inline operator bool() const {
|
||||
return offset < buffer->size;
|
||||
}
|
||||
|
||||
/// Read pointer
|
||||
inline const lv2_event &operator*() const {
|
||||
return *(const lv2_event *)(buffer->data + offset);
|
||||
}
|
||||
/// Pointer to member
|
||||
inline const lv2_event *operator->() const {
|
||||
return &**this;
|
||||
}
|
||||
|
||||
/// Move to the next element
|
||||
inline event_port_read_iterator operator++() {
|
||||
offset += ((**this).size + 19) &~7;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Move to the next element
|
||||
inline event_port_read_iterator operator++(int) {
|
||||
event_port_read_iterator old = *this;
|
||||
offset += ((**this).size + 19) &~7;
|
||||
return old;
|
||||
}
|
||||
};
|
||||
|
||||
/// A write-only iterator-like object for writing to event buffers
|
||||
class event_port_write_iterator
|
||||
{
|
||||
protected:
|
||||
LV2_Event_Buffer *buffer;
|
||||
public:
|
||||
/// Default constructor creating a useless iterator you can assign to
|
||||
event_port_write_iterator()
|
||||
: buffer(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
/// Create a write iterator based on specified buffer and index/offset values
|
||||
event_port_write_iterator(LV2_Event_Buffer *_buffer)
|
||||
: buffer(_buffer)
|
||||
{
|
||||
}
|
||||
|
||||
/// @return the remaining buffer space
|
||||
inline uint32_t space_left() const {
|
||||
return buffer->capacity - buffer->size;
|
||||
}
|
||||
/// @return write pointer
|
||||
inline lv2_event &operator*() {
|
||||
return *(lv2_event *)(buffer->data + buffer->size);
|
||||
}
|
||||
/// Pointer to member
|
||||
inline lv2_event *operator->() {
|
||||
return &**this;
|
||||
}
|
||||
/// Move to the next element after the current one has been written (must be called after each write)
|
||||
inline event_port_write_iterator operator++() {
|
||||
buffer->size += ((**this).size + 19) &~7;
|
||||
buffer->event_count ++;
|
||||
return *this;
|
||||
}
|
||||
/// Move to the next element after the current one has been written
|
||||
inline lv2_event *operator++(int) {
|
||||
lv2_event *ptr = &**this;
|
||||
buffer->size += ((**this).size + 19) &~7;
|
||||
buffer->event_count ++;
|
||||
return ptr;
|
||||
}
|
||||
};
|
||||
|
||||
template<class Iter1, class Iter2>
|
||||
class event_port_merge_iterator
|
||||
{
|
||||
public:
|
||||
Iter1 first;
|
||||
Iter2 second;
|
||||
public:
|
||||
event_port_merge_iterator() {}
|
||||
event_port_merge_iterator(const Iter1 &_first, const Iter2 &_second)
|
||||
: first(_first)
|
||||
, second(_second)
|
||||
{
|
||||
}
|
||||
/// @retval true if any of the iterators have any data left
|
||||
inline operator bool() const {
|
||||
return ((bool)first) || ((bool)second);
|
||||
}
|
||||
inline bool select_first() const
|
||||
{
|
||||
if (!(bool)second)
|
||||
return true;
|
||||
if (!(bool)first)
|
||||
return false;
|
||||
return first->timestamp() < second->timestamp();
|
||||
}
|
||||
/// Returns the earliest of (*first, *second)
|
||||
inline const lv2_event &operator*() const {
|
||||
if (select_first())
|
||||
{
|
||||
assert((bool)first);
|
||||
return *first;
|
||||
}
|
||||
assert((bool)second);
|
||||
return *second;
|
||||
}
|
||||
/// Pointer to member
|
||||
inline const lv2_event *operator->() const {
|
||||
return &**this;
|
||||
}
|
||||
/// Prefix increment
|
||||
inline event_port_merge_iterator operator++() {
|
||||
if (select_first())
|
||||
first++;
|
||||
else
|
||||
second++;
|
||||
return *this;
|
||||
}
|
||||
/// Postfix increment
|
||||
inline event_port_merge_iterator operator++(int) {
|
||||
event_port_merge_iterator ptr = *this;
|
||||
if (select_first())
|
||||
first++;
|
||||
else
|
||||
second++;
|
||||
return ptr;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
349
plugins/ladspa_effect/calf/calf/lv2wrap.h
Normal file
349
plugins/ladspa_effect/calf/calf/lv2wrap.h
Normal file
@@ -0,0 +1,349 @@
|
||||
/* Calf DSP Library
|
||||
* LV2 wrapper templates
|
||||
*
|
||||
* Copyright (C) 2001-2008 Krzysztof Foltman
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef CALF_LV2WRAP_H
|
||||
#define CALF_LV2WRAP_H
|
||||
|
||||
#if USE_LV2
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <lv2.h>
|
||||
#include <calf/giface.h>
|
||||
#include <calf/lv2-midiport.h>
|
||||
#include <calf/lv2_contexts.h>
|
||||
#include <calf/lv2_event.h>
|
||||
#include <calf/lv2_progress.h>
|
||||
#include <calf/lv2_string_port.h>
|
||||
#include <calf/lv2_uri_map.h>
|
||||
|
||||
namespace calf_plugins {
|
||||
|
||||
template<class Module>
|
||||
struct lv2_instance: public plugin_ctl_iface, public progress_report_iface, public Module
|
||||
{
|
||||
bool set_srate;
|
||||
int srate_to_set;
|
||||
LV2_MIDI *midi_data;
|
||||
LV2_Event_Buffer *event_data;
|
||||
LV2_URI_Map_Feature *uri_map;
|
||||
LV2_Event_Feature *event_feature;
|
||||
uint32_t midi_event_type;
|
||||
std::vector<int> message_params;
|
||||
LV2_Progress *progress_report_feature;
|
||||
lv2_instance()
|
||||
{
|
||||
for (int i=0; i < Module::in_count; i++)
|
||||
Module::ins[i] = NULL;
|
||||
for (int i=0; i < Module::out_count; i++)
|
||||
Module::outs[i] = NULL;
|
||||
for (int i=0; i < Module::param_count; i++)
|
||||
Module::params[i] = NULL;
|
||||
uri_map = NULL;
|
||||
midi_data = NULL;
|
||||
event_data = NULL;
|
||||
midi_event_type = 0xFFFFFFFF;
|
||||
set_srate = true;
|
||||
srate_to_set = 44100;
|
||||
get_message_context_parameters(message_params);
|
||||
progress_report_feature = NULL;
|
||||
// printf("message params %d\n", (int)message_params.size());
|
||||
}
|
||||
/// This, and not Module::post_instantiate, is actually called by lv2_wrapper class
|
||||
void post_instantiate()
|
||||
{
|
||||
if (progress_report_feature)
|
||||
Module::progress_report = this;
|
||||
Module::post_instantiate();
|
||||
}
|
||||
virtual parameter_properties *get_param_props(int param_no)
|
||||
{
|
||||
return &Module::param_props[param_no];
|
||||
}
|
||||
virtual float get_param_value(int param_no)
|
||||
{
|
||||
return *Module::params[param_no];
|
||||
}
|
||||
virtual void set_param_value(int param_no, float value)
|
||||
{
|
||||
*Module::params[param_no] = value;
|
||||
}
|
||||
virtual int get_param_count()
|
||||
{
|
||||
return Module::param_count;
|
||||
}
|
||||
virtual int get_param_port_offset()
|
||||
{
|
||||
return Module::in_count + Module::out_count;
|
||||
}
|
||||
virtual const char *get_gui_xml() {
|
||||
return Module::get_gui_xml();
|
||||
}
|
||||
virtual line_graph_iface *get_line_graph_iface()
|
||||
{
|
||||
return dynamic_cast<line_graph_iface *>(this);
|
||||
}
|
||||
virtual bool activate_preset(int bank, int program) {
|
||||
return false;
|
||||
}
|
||||
virtual const char *get_name()
|
||||
{
|
||||
return Module::get_name();
|
||||
}
|
||||
virtual const char *get_id()
|
||||
{
|
||||
return Module::get_id();
|
||||
}
|
||||
virtual const char *get_label()
|
||||
{
|
||||
return Module::get_label();
|
||||
}
|
||||
virtual int get_input_count() { return Module::in_count; }
|
||||
virtual int get_output_count() { return Module::out_count; }
|
||||
virtual bool get_midi() { return Module::support_midi; }
|
||||
virtual float get_level(unsigned int port) { return 0.f; }
|
||||
virtual void execute(int cmd_no) {
|
||||
Module::execute(cmd_no);
|
||||
}
|
||||
virtual void report_progress(float percentage, const std::string &message) {
|
||||
if (progress_report_feature)
|
||||
(*progress_report_feature->progress)(progress_report_feature->context, percentage, !message.empty() ? message.c_str() : NULL);
|
||||
}
|
||||
void send_configures(send_configure_iface *sci) {
|
||||
Module::send_configures(sci);
|
||||
}
|
||||
uint32_t impl_message_run(const void *valid_inputs, void *output_ports) {
|
||||
for (unsigned int i = 0; i < message_params.size(); i++)
|
||||
{
|
||||
int pn = message_params[i];
|
||||
parameter_properties &pp = *get_param_props(pn);
|
||||
if ((pp.flags & PF_TYPEMASK) == PF_STRING
|
||||
&& (((LV2_String_Data *)Module::params[pn])->flags & LV2_STRING_DATA_CHANGED_FLAG)) {
|
||||
printf("Calling configure on %s\n", pp.short_name);
|
||||
configure(pp.short_name, ((LV2_String_Data *)Module::params[pn])->data);
|
||||
}
|
||||
}
|
||||
return Module::message_run(valid_inputs, output_ports);
|
||||
}
|
||||
char *configure(const char *key, const char *value) {
|
||||
// disambiguation - the plugin_ctl_iface version is just a stub, so don't use it
|
||||
return Module::configure(key, value);
|
||||
}
|
||||
#if 0
|
||||
// the default implementation should be fine
|
||||
virtual void clear_preset() {
|
||||
// This is never called in practice, at least for now
|
||||
// However, it will change when presets are implemented
|
||||
for (int i=0; i < Module::param_count; i++)
|
||||
*Module::params[i] = Module::param_props[i].def_value;
|
||||
/*
|
||||
const char **p = Module::get_default_configure_vars();
|
||||
if (p)
|
||||
{
|
||||
for(; p[0]; p += 2)
|
||||
configure(p[0], p[1]);
|
||||
}
|
||||
*/
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
struct LV2_Calf_Descriptor {
|
||||
plugin_ctl_iface *(*get_pci)(LV2_Handle Instance);
|
||||
};
|
||||
|
||||
template<class Module>
|
||||
struct lv2_wrapper
|
||||
{
|
||||
typedef lv2_instance<Module> instance;
|
||||
static LV2_Descriptor descriptor;
|
||||
static LV2_Calf_Descriptor calf_descriptor;
|
||||
static LV2MessageContext message_context;
|
||||
std::string uri;
|
||||
|
||||
lv2_wrapper()
|
||||
{
|
||||
ladspa_plugin_info &info = Module::plugin_info;
|
||||
uri = "http://calf.sourceforge.net/plugins/" + std::string(info.label);
|
||||
descriptor.URI = uri.c_str();
|
||||
descriptor.instantiate = cb_instantiate;
|
||||
descriptor.connect_port = cb_connect;
|
||||
descriptor.activate = cb_activate;
|
||||
descriptor.run = cb_run;
|
||||
descriptor.deactivate = cb_deactivate;
|
||||
descriptor.cleanup = cb_cleanup;
|
||||
descriptor.extension_data = cb_ext_data;
|
||||
calf_descriptor.get_pci = cb_get_pci;
|
||||
message_context.message_connect_port = cb_connect;
|
||||
message_context.message_run = cb_message_run;
|
||||
}
|
||||
|
||||
static void cb_connect(LV2_Handle Instance, uint32_t port, void *DataLocation) {
|
||||
unsigned long ins = Module::in_count;
|
||||
unsigned long outs = Module::out_count;
|
||||
unsigned long params = Module::param_count;
|
||||
instance *const mod = (instance *)Instance;
|
||||
if (port < ins)
|
||||
mod->ins[port] = (float *)DataLocation;
|
||||
else if (port < ins + outs)
|
||||
mod->outs[port - ins] = (float *)DataLocation;
|
||||
else if (port < ins + outs + params) {
|
||||
int i = port - ins - outs;
|
||||
mod->params[i] = (float *)DataLocation;
|
||||
}
|
||||
else if (Module::support_midi && port == ins + outs + params) {
|
||||
mod->event_data = (LV2_Event_Buffer *)DataLocation;
|
||||
}
|
||||
}
|
||||
|
||||
static void cb_activate(LV2_Handle Instance) {
|
||||
instance *const mod = (instance *)Instance;
|
||||
mod->set_srate = true;
|
||||
}
|
||||
|
||||
static void cb_deactivate(LV2_Handle Instance) {
|
||||
instance *const mod = (instance *)Instance;
|
||||
mod->deactivate();
|
||||
}
|
||||
|
||||
static LV2_Handle cb_instantiate(const LV2_Descriptor * Descriptor, double sample_rate, const char *bundle_path, const LV2_Feature *const *features)
|
||||
{
|
||||
instance *mod = new instance();
|
||||
// XXXKF some people use fractional sample rates; we respect them ;-)
|
||||
mod->srate_to_set = (uint32_t)sample_rate;
|
||||
mod->set_srate = true;
|
||||
while(*features)
|
||||
{
|
||||
if (!strcmp((*features)->URI, LV2_URI_MAP_URI))
|
||||
{
|
||||
mod->uri_map = (LV2_URI_Map_Feature *)((*features)->data);
|
||||
mod->midi_event_type = mod->uri_map->uri_to_id(
|
||||
mod->uri_map->callback_data,
|
||||
"http://lv2plug.in/ns/ext/event",
|
||||
"http://lv2plug.in/ns/ext/midi#MidiEvent");
|
||||
}
|
||||
else if (!strcmp((*features)->URI, LV2_EVENT_URI))
|
||||
{
|
||||
mod->event_feature = (LV2_Event_Feature *)((*features)->data);
|
||||
}
|
||||
else if (!strcmp((*features)->URI, LV2_PROGRESS_URI))
|
||||
{
|
||||
mod->progress_report_feature = (LV2_Progress *)((*features)->data);
|
||||
}
|
||||
features++;
|
||||
}
|
||||
mod->post_instantiate();
|
||||
return mod;
|
||||
}
|
||||
static inline void zero_by_mask(Module *module, uint32_t mask, uint32_t offset, uint32_t nsamples)
|
||||
{
|
||||
for (int i=0; i<Module::out_count; i++) {
|
||||
if ((mask & (1 << i)) == 0) {
|
||||
dsp::zero(module->outs[i] + offset, nsamples);
|
||||
}
|
||||
}
|
||||
}
|
||||
static plugin_ctl_iface *cb_get_pci(LV2_Handle Instance)
|
||||
{
|
||||
return static_cast<plugin_ctl_iface *>(Instance);
|
||||
}
|
||||
|
||||
static inline void process_slice(Module *mod, uint32_t offset, uint32_t end)
|
||||
{
|
||||
while(offset < end)
|
||||
{
|
||||
uint32_t newend = std::min(offset + MAX_SAMPLE_RUN, end);
|
||||
uint32_t out_mask = mod->process(offset, newend - offset, -1, -1);
|
||||
zero_by_mask(mod, out_mask, offset, newend - offset);
|
||||
offset = newend;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t cb_message_run(LV2_Handle Instance, const void *valid_inputs, void *outputs_written) {
|
||||
instance *mod = (instance *)Instance;
|
||||
return mod->impl_message_run(valid_inputs, outputs_written);
|
||||
}
|
||||
static void cb_run(LV2_Handle Instance, uint32_t SampleCount) {
|
||||
instance *const mod = (instance *)Instance;
|
||||
if (mod->set_srate) {
|
||||
mod->set_sample_rate(mod->srate_to_set);
|
||||
mod->activate();
|
||||
mod->set_srate = false;
|
||||
}
|
||||
mod->params_changed();
|
||||
uint32_t offset = 0;
|
||||
if (mod->event_data)
|
||||
{
|
||||
// printf("Event data: count %d\n", mod->event_data->event_count);
|
||||
struct LV2_Midi_Event: public LV2_Event {
|
||||
unsigned char data[1];
|
||||
};
|
||||
unsigned char *data = (unsigned char *)(mod->event_data->data);
|
||||
for (uint32_t i = 0; i < mod->event_data->event_count; i++) {
|
||||
LV2_Midi_Event *item = (LV2_Midi_Event *)data;
|
||||
uint32_t ts = item->frames;
|
||||
// printf("Event: timestamp %d subframes %d type %d vs %d\n", item->frames, item->subframes, item->type, mod->midi_event_type);
|
||||
if (ts > offset)
|
||||
{
|
||||
process_slice(mod, offset, ts);
|
||||
offset = ts;
|
||||
}
|
||||
if (item->type == mod->midi_event_type)
|
||||
{
|
||||
// printf("Midi message %x %x %x %x %d\n", item->data[0], item->data[1], item->data[2], item->data[3], item->size);
|
||||
switch(item->data[0] >> 4)
|
||||
{
|
||||
case 8: mod->note_off(item->data[1], item->data[2]); break;
|
||||
case 9: mod->note_on(item->data[1], item->data[2]); break;
|
||||
case 11: mod->control_change(item->data[1], item->data[2]); break;
|
||||
case 12: mod->program_change(item->data[1]); break;
|
||||
case 14: mod->pitch_bend(item->data[1] + 128 * item->data[2] - 8192); break;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (item->type == 0 && mod->event_feature)
|
||||
mod->event_feature->lv2_event_unref(mod->event_feature->callback_data, item);
|
||||
// printf("timestamp %f item size %d first byte %x\n", item->timestamp, item->size, item->data[0]);
|
||||
data += ((sizeof(LV2_Event) + item->size + 7))&~7;
|
||||
}
|
||||
}
|
||||
process_slice(mod, offset, SampleCount);
|
||||
}
|
||||
static void cb_cleanup(LV2_Handle Instance) {
|
||||
instance *const mod = (instance *)Instance;
|
||||
delete mod;
|
||||
}
|
||||
static const void *cb_ext_data(const char *URI) {
|
||||
if (!strcmp(URI, "http://foltman.com/ns/calf-plugin-instance"))
|
||||
return &calf_descriptor;
|
||||
if (!strcmp(URI, LV2_CONTEXT_MESSAGE))
|
||||
return &message_context;
|
||||
return NULL;
|
||||
}
|
||||
static lv2_wrapper &get() {
|
||||
static lv2_wrapper instance;
|
||||
return instance;
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
312
plugins/ladspa_effect/calf/calf/metadata.h
Normal file
312
plugins/ladspa_effect/calf/calf/metadata.h
Normal file
@@ -0,0 +1,312 @@
|
||||
/* Calf DSP Library
|
||||
* Audio module (plugin) metadata - header file
|
||||
*
|
||||
* Copyright (C) 2007-2008 Krzysztof Foltman
|
||||
* Copyright (C) 2008 Thor Harald Johansen <thj@thj.no>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __CALF_METADATA_H
|
||||
#define __CALF_METADATA_H
|
||||
|
||||
#include "giface.h"
|
||||
|
||||
namespace calf_plugins {
|
||||
|
||||
struct flanger_metadata: public plugin_metadata<flanger_metadata>
|
||||
{
|
||||
public:
|
||||
enum { par_delay, par_depth, par_rate, par_fb, par_stereo, par_reset, par_amount, par_dryamount, param_count };
|
||||
enum { in_count = 2, out_count = 2, support_midi = false, require_midi = false, rt_capable = true };
|
||||
PLUGIN_NAME_ID_LABEL("flanger", "flanger", "Flanger")
|
||||
};
|
||||
|
||||
struct phaser_metadata: public plugin_metadata<phaser_metadata>
|
||||
{
|
||||
enum { par_freq, par_depth, par_rate, par_fb, par_stages, par_stereo, par_reset, par_amount, par_dryamount, param_count };
|
||||
enum { in_count = 2, out_count = 2, support_midi = false, require_midi = false, rt_capable = true };
|
||||
PLUGIN_NAME_ID_LABEL("phaser", "phaser", "Phaser")
|
||||
};
|
||||
|
||||
struct filter_metadata: public plugin_metadata<filter_metadata>
|
||||
{
|
||||
enum { par_cutoff, par_resonance, par_mode, par_inertia, param_count };
|
||||
enum { in_count = 2, out_count = 2, rt_capable = true, require_midi = false, support_midi = false };
|
||||
PLUGIN_NAME_ID_LABEL("filter", "filter", "Filter")
|
||||
/// do not export mode and inertia as CVs, as those are settings and not parameters
|
||||
bool is_cv(int param_no) { return param_no != par_mode && param_no != par_inertia; }
|
||||
};
|
||||
|
||||
/// Filterclavier - metadata
|
||||
struct filterclavier_metadata: public plugin_metadata<filterclavier_metadata>
|
||||
{
|
||||
enum { par_transpose, par_detune, par_max_resonance, par_mode, par_inertia, param_count };
|
||||
enum { in_count = 2, out_count = 2, rt_capable = true, require_midi = true, support_midi = true };
|
||||
PLUGIN_NAME_ID_LABEL("filterclavier", "filterclavier", "Filterclavier")
|
||||
/// do not export mode and inertia as CVs, as those are settings and not parameters
|
||||
bool is_cv(int param_no) { return param_no != par_mode && param_no != par_inertia; }
|
||||
};
|
||||
|
||||
struct reverb_metadata: public plugin_metadata<reverb_metadata>
|
||||
{
|
||||
enum { par_decay, par_hfdamp, par_roomsize, par_diffusion, par_amount, par_dry, par_predelay, par_basscut, par_treblecut, param_count };
|
||||
enum { in_count = 2, out_count = 2, support_midi = false, require_midi = false, rt_capable = true };
|
||||
PLUGIN_NAME_ID_LABEL("reverb", "reverb", "Reverb")
|
||||
};
|
||||
|
||||
struct vintage_delay_metadata: public plugin_metadata<vintage_delay_metadata>
|
||||
{
|
||||
enum { par_bpm, par_divide, par_time_l, par_time_r, par_feedback, par_amount, par_mixmode, par_medium, par_dryamount, param_count };
|
||||
enum { in_count = 2, out_count = 2, rt_capable = true, support_midi = false, require_midi = false };
|
||||
PLUGIN_NAME_ID_LABEL("vintage_delay", "vintagedelay", "Vintage Delay")
|
||||
};
|
||||
|
||||
struct rotary_speaker_metadata: public plugin_metadata<rotary_speaker_metadata>
|
||||
{
|
||||
public:
|
||||
enum { par_speed, par_spacing, par_shift, par_moddepth, par_treblespeed, par_bassspeed, par_micdistance, par_reflection, param_count };
|
||||
enum { in_count = 2, out_count = 2, support_midi = true, require_midi = false, rt_capable = true };
|
||||
PLUGIN_NAME_ID_LABEL("rotary_speaker", "rotaryspeaker", "Rotary Speaker")
|
||||
};
|
||||
|
||||
/// A multitap stereo chorus thing - metadata
|
||||
struct multichorus_metadata: public plugin_metadata<multichorus_metadata>
|
||||
{
|
||||
public:
|
||||
enum { par_delay, par_depth, par_rate, par_stereo, par_voices, par_vphase, par_amount, par_dryamount, par_freq, par_freq2, par_q, par_overlap, param_count };
|
||||
enum { in_count = 2, out_count = 2, rt_capable = true, support_midi = false, require_midi = false };
|
||||
PLUGIN_NAME_ID_LABEL("multichorus", "multichorus", "Multi Chorus")
|
||||
};
|
||||
|
||||
/// Monosynth - metadata
|
||||
struct monosynth_metadata: public plugin_metadata<monosynth_metadata>
|
||||
{
|
||||
enum { wave_saw, wave_sqr, wave_pulse, wave_sine, wave_triangle, wave_varistep, wave_skewsaw, wave_skewsqr, wave_test1, wave_test2, wave_test3, wave_test4, wave_test5, wave_test6, wave_test7, wave_test8, wave_count };
|
||||
enum { flt_lp12, flt_lp24, flt_2lp12, flt_hp12, flt_lpbr, flt_hpbr, flt_bp6, flt_2bp6 };
|
||||
enum { par_wave1, par_wave2, par_pw1, par_pw2, par_detune, par_osc2xpose, par_oscmode, par_oscmix, par_filtertype, par_cutoff, par_resonance, par_cutoffsep, par_envmod, par_envtores, par_envtoamp, par_attack, par_decay, par_sustain, par_fade, par_release,
|
||||
par_keyfollow, par_legato, par_portamento, par_vel2filter, par_vel2amp, par_master, par_pwhlrange,
|
||||
par_lforate, par_lfodelay, par_lfofilter, par_lfopitch, par_lfopw, par_mwhl_lfo, par_scaledetune,
|
||||
param_count };
|
||||
enum { in_count = 0, out_count = 2, support_midi = true, require_midi = true, rt_capable = true };
|
||||
enum { step_size = 64, step_shift = 6 };
|
||||
enum {
|
||||
modsrc_none,
|
||||
modsrc_velocity,
|
||||
modsrc_pressure,
|
||||
modsrc_modwheel,
|
||||
modsrc_env1,
|
||||
modsrc_lfo1,
|
||||
modsrc_count,
|
||||
};
|
||||
enum {
|
||||
moddest_none,
|
||||
moddest_attenuation,
|
||||
moddest_oscmix,
|
||||
moddest_cutoff,
|
||||
moddest_resonance,
|
||||
moddest_o1detune,
|
||||
moddest_o2detune,
|
||||
moddest_o1pw,
|
||||
moddest_o2pw,
|
||||
moddest_count,
|
||||
};
|
||||
PLUGIN_NAME_ID_LABEL("monosynth", "monosynth", "Monosynth")
|
||||
};
|
||||
|
||||
/// Thor's compressor - metadata
|
||||
struct compressor_metadata: public plugin_metadata<compressor_metadata>
|
||||
{
|
||||
enum { in_count = 2, out_count = 2, support_midi = false, require_midi = false, rt_capable = true };
|
||||
enum { param_threshold, param_ratio, param_attack, param_release, param_makeup, param_knee, param_detection, param_stereo_link, param_aweighting, param_compression, param_peak, param_clip, param_bypass, // param_freq, param_bw,
|
||||
param_count };
|
||||
PLUGIN_NAME_ID_LABEL("compressor", "compressor", "Compressor")
|
||||
};
|
||||
|
||||
/// Organ - enums for parameter IDs etc. (this mess is caused by organ split between plugin and generic class - which was
|
||||
/// a bad design decision and should be sorted out some day) XXXKF @todo
|
||||
struct organ_enums
|
||||
{
|
||||
enum {
|
||||
par_drawbar1, par_drawbar2, par_drawbar3, par_drawbar4, par_drawbar5, par_drawbar6, par_drawbar7, par_drawbar8, par_drawbar9,
|
||||
par_frequency1, par_frequency2, par_frequency3, par_frequency4, par_frequency5, par_frequency6, par_frequency7, par_frequency8, par_frequency9,
|
||||
par_waveform1, par_waveform2, par_waveform3, par_waveform4, par_waveform5, par_waveform6, par_waveform7, par_waveform8, par_waveform9,
|
||||
par_detune1, par_detune2, par_detune3, par_detune4, par_detune5, par_detune6, par_detune7, par_detune8, par_detune9,
|
||||
par_phase1, par_phase2, par_phase3, par_phase4, par_phase5, par_phase6, par_phase7, par_phase8, par_phase9,
|
||||
par_pan1, par_pan2, par_pan3, par_pan4, par_pan5, par_pan6, par_pan7, par_pan8, par_pan9,
|
||||
par_routing1, par_routing2, par_routing3, par_routing4, par_routing5, par_routing6, par_routing7, par_routing8, par_routing9,
|
||||
par_foldover,
|
||||
par_percdecay, par_perclevel, par_percwave, par_percharm, par_percvel2amp,
|
||||
par_percfmdecay, par_percfmdepth, par_percfmwave, par_percfmharm, par_percvel2fm,
|
||||
par_perctrigger, par_percstereo,
|
||||
par_filterchain,
|
||||
par_filter1type,
|
||||
par_master,
|
||||
par_f1cutoff, par_f1res, par_f1env1, par_f1env2, par_f1env3, par_f1keyf,
|
||||
par_f2cutoff, par_f2res, par_f2env1, par_f2env2, par_f2env3, par_f2keyf,
|
||||
par_eg1attack, par_eg1decay, par_eg1sustain, par_eg1release, par_eg1velscl, par_eg1ampctl,
|
||||
par_eg2attack, par_eg2decay, par_eg2sustain, par_eg2release, par_eg2velscl, par_eg2ampctl,
|
||||
par_eg3attack, par_eg3decay, par_eg3sustain, par_eg3release, par_eg3velscl, par_eg3ampctl,
|
||||
par_lforate, par_lfoamt, par_lfowet, par_lfophase, par_lfomode,
|
||||
par_transpose, par_detune,
|
||||
par_polyphony,
|
||||
par_quadenv,
|
||||
par_pwhlrange,
|
||||
par_bassfreq,
|
||||
par_bassgain,
|
||||
par_treblefreq,
|
||||
par_treblegain,
|
||||
par_var_mapcurve,
|
||||
param_count
|
||||
};
|
||||
enum {
|
||||
var_count = 1
|
||||
};
|
||||
enum organ_waveform {
|
||||
wave_sine,
|
||||
wave_sinepl1, wave_sinepl2, wave_sinepl3,
|
||||
wave_ssaw, wave_ssqr, wave_spls, wave_saw, wave_sqr, wave_pulse, wave_sinepl05, wave_sqr05, wave_halfsin, wave_clvg, wave_bell, wave_bell2,
|
||||
wave_w1, wave_w2, wave_w3, wave_w4, wave_w5, wave_w6, wave_w7, wave_w8, wave_w9,
|
||||
wave_dsaw, wave_dsqr, wave_dpls,
|
||||
wave_count_small,
|
||||
wave_strings = wave_count_small,
|
||||
wave_strings2,
|
||||
wave_sinepad,
|
||||
wave_bellpad,
|
||||
wave_space,
|
||||
wave_choir,
|
||||
wave_choir2,
|
||||
wave_choir3,
|
||||
wave_count,
|
||||
wave_count_big = wave_count - wave_count_small
|
||||
};
|
||||
enum {
|
||||
ampctl_none,
|
||||
ampctl_direct,
|
||||
ampctl_f1,
|
||||
ampctl_f2,
|
||||
ampctl_all,
|
||||
ampctl_count
|
||||
};
|
||||
enum {
|
||||
lfomode_off = 0,
|
||||
lfomode_direct,
|
||||
lfomode_filter1,
|
||||
lfomode_filter2,
|
||||
lfomode_voice,
|
||||
lfomode_global,
|
||||
lfomode_count
|
||||
};
|
||||
enum {
|
||||
perctrig_first = 0,
|
||||
perctrig_each,
|
||||
perctrig_eachplus,
|
||||
perctrig_polyphonic,
|
||||
perctrig_count
|
||||
};
|
||||
};
|
||||
|
||||
/// Organ - metadata
|
||||
struct organ_metadata: public organ_enums, public plugin_metadata<organ_metadata>
|
||||
{
|
||||
enum { in_count = 0, out_count = 2, support_midi = true, require_midi = true, rt_capable = true };
|
||||
PLUGIN_NAME_ID_LABEL("organ", "organ", "Organ")
|
||||
plugin_command_info *get_commands();
|
||||
const char **get_default_configure_vars();
|
||||
};
|
||||
|
||||
/// FluidSynth - metadata
|
||||
struct fluidsynth_metadata: public plugin_metadata<fluidsynth_metadata>
|
||||
{
|
||||
enum { par_master, par_soundfont, par_interpolation, par_reverb, par_chorus, param_count };
|
||||
enum { in_count = 0, out_count = 2, support_midi = true, require_midi = true, rt_capable = false };
|
||||
PLUGIN_NAME_ID_LABEL("fluidsynth", "fluidsynth", "Fluidsynth")
|
||||
const char **get_default_configure_vars();
|
||||
};
|
||||
|
||||
/// Wavetable - metadata
|
||||
struct wavetable_metadata: public plugin_metadata<wavetable_metadata>
|
||||
{
|
||||
enum {
|
||||
wt_fmshiny,
|
||||
wt_fmshiny2,
|
||||
wt_rezo,
|
||||
wt_metal,
|
||||
wt_bell,
|
||||
wt_blah,
|
||||
wt_pluck,
|
||||
wt_stretch,
|
||||
wt_stretch2,
|
||||
wt_hardsync,
|
||||
wt_hardsync2,
|
||||
wt_softsync,
|
||||
wt_bell2,
|
||||
wt_bell3,
|
||||
wt_tine,
|
||||
wt_tine2,
|
||||
wt_clav,
|
||||
wt_clav2,
|
||||
wt_gtr,
|
||||
wt_gtr2,
|
||||
wt_gtr3,
|
||||
wt_gtr4,
|
||||
wt_gtr5,
|
||||
wt_reed,
|
||||
wt_reed2,
|
||||
wt_silver,
|
||||
wt_brass,
|
||||
wt_multi,
|
||||
wt_multi2,
|
||||
wt_count
|
||||
};
|
||||
enum {
|
||||
modsrc_none,
|
||||
modsrc_velocity,
|
||||
modsrc_pressure,
|
||||
modsrc_modwheel,
|
||||
modsrc_env1,
|
||||
modsrc_env2,
|
||||
modsrc_env3,
|
||||
modsrc_count,
|
||||
};
|
||||
enum {
|
||||
moddest_none,
|
||||
moddest_attenuation,
|
||||
moddest_oscmix,
|
||||
moddest_cutoff,
|
||||
moddest_resonance,
|
||||
moddest_o1shift,
|
||||
moddest_o2shift,
|
||||
moddest_o1detune,
|
||||
moddest_o2detune,
|
||||
moddest_count,
|
||||
};
|
||||
enum {
|
||||
par_o1wave, par_o1offset, par_o1transpose, par_o1detune, par_o1level,
|
||||
par_o2wave, par_o2offset, par_o2transpose, par_o2detune, par_o2level,
|
||||
par_eg1attack, par_eg1decay, par_eg1sustain, par_eg1fade, par_eg1release, par_eg1velscl,
|
||||
par_eg2attack, par_eg2decay, par_eg2sustain, par_eg2fade, par_eg2release, par_eg2velscl,
|
||||
par_eg3attack, par_eg3decay, par_eg3sustain, par_eg3fade, par_eg3release, par_eg3velscl,
|
||||
par_pwhlrange,
|
||||
param_count };
|
||||
enum { in_count = 0, out_count = 2, support_midi = true, require_midi = true, rt_capable = true };
|
||||
enum { step_size = 64 };
|
||||
PLUGIN_NAME_ID_LABEL("wavetable", "wavetable", "Wavetable")
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
112
plugins/ladspa_effect/calf/calf/modmatrix.h
Normal file
112
plugins/ladspa_effect/calf/calf/modmatrix.h
Normal file
@@ -0,0 +1,112 @@
|
||||
/* Calf DSP Library
|
||||
* Modulation matrix boilerplate code.
|
||||
*
|
||||
* Copyright (C) 2009 Krzysztof Foltman
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __CALF_MODMATRIX_H
|
||||
#define __CALF_MODMATRIX_H
|
||||
|
||||
#include "giface.h"
|
||||
|
||||
namespace dsp {
|
||||
|
||||
/// Mapping modes
|
||||
enum mapping_mode {
|
||||
map_positive, ///< 0..100%
|
||||
map_bipolar, ///< -100%..100%
|
||||
map_negative, ///< -100%..0%
|
||||
map_squared, ///< x^2
|
||||
map_squared_bipolar, ///< x^2 scaled to -100%..100%
|
||||
map_antisquared, ///< 1-(1-x)^2 scaled to 0..100%
|
||||
map_antisquared_bipolar, ///< 1-(1-x)^2 scaled to -100..100%
|
||||
map_parabola, ///< inverted parabola (peaks at 0.5, then decreases to 0)
|
||||
map_type_count
|
||||
};
|
||||
|
||||
/// Single entry in modulation matrix
|
||||
struct modulation_entry
|
||||
{
|
||||
/// Mapped source
|
||||
int src1;
|
||||
/// Source mapping mode
|
||||
mapping_mode mapping;
|
||||
/// Unmapped modulating source
|
||||
int src2;
|
||||
/// Modulation amount
|
||||
float amount;
|
||||
/// Modulation destination
|
||||
int dest;
|
||||
|
||||
modulation_entry() {
|
||||
reset();
|
||||
}
|
||||
|
||||
/// Reset the row to default
|
||||
void reset() {
|
||||
src1 = 0;
|
||||
src2 = 0;
|
||||
mapping = map_positive;
|
||||
amount = 0.f;
|
||||
dest = 0;
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
namespace calf_plugins {
|
||||
|
||||
class mod_matrix: public table_edit_iface
|
||||
{
|
||||
protected:
|
||||
/// Polynomials for different scaling modes (1, x, x^2)
|
||||
static const float scaling_coeffs[dsp::map_type_count][3];
|
||||
/// Column descriptions for table widget
|
||||
table_column_info table_columns[6];
|
||||
|
||||
dsp::modulation_entry *matrix;
|
||||
unsigned int matrix_rows;
|
||||
const char **mod_src_names, **mod_dest_names;
|
||||
|
||||
mod_matrix(dsp::modulation_entry *_matrix, unsigned int _rows, const char **_src_names, const char **_dest_names);
|
||||
public:
|
||||
virtual const table_column_info *get_table_columns(int param);
|
||||
virtual uint32_t get_table_rows(int param);
|
||||
virtual std::string get_cell(int param, int row, int column);
|
||||
virtual void set_cell(int param, int row, int column, const std::string &src, std::string &error);
|
||||
|
||||
/// Process modulation matrix, calculate outputs from inputs
|
||||
inline void calculate_modmatrix(float *moddest, int moddest_count, float *modsrc)
|
||||
{
|
||||
for (int i = 0; i < moddest_count; i++)
|
||||
moddest[i] = 0;
|
||||
for (unsigned int i = 0; i < matrix_rows; i++)
|
||||
{
|
||||
dsp::modulation_entry &slot = matrix[i];
|
||||
if (slot.dest) {
|
||||
float value = modsrc[slot.src1];
|
||||
const float *c = scaling_coeffs[slot.mapping];
|
||||
value = c[0] + c[1] * value + c[2] * value * value;
|
||||
moddest[slot.dest] += value * modsrc[slot.src2] * slot.amount;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
95
plugins/ladspa_effect/calf/calf/modulelist.h
Normal file
95
plugins/ladspa_effect/calf/calf/modulelist.h
Normal file
@@ -0,0 +1,95 @@
|
||||
#ifdef PER_MODULE_ITEM
|
||||
PER_MODULE_ITEM(filter, false, "filter")
|
||||
PER_MODULE_ITEM(filterclavier, false, "filterclavier")
|
||||
PER_MODULE_ITEM(flanger, false, "flanger")
|
||||
PER_MODULE_ITEM(reverb, false, "reverb")
|
||||
PER_MODULE_ITEM(monosynth, true, "monosynth")
|
||||
PER_MODULE_ITEM(vintage_delay, false, "vintagedelay")
|
||||
PER_MODULE_ITEM(organ, true, "organ")
|
||||
PER_MODULE_ITEM(rotary_speaker, false, "rotaryspeaker")
|
||||
PER_MODULE_ITEM(phaser, false, "phaser")
|
||||
PER_MODULE_ITEM(multichorus, false, "multichorus")
|
||||
PER_MODULE_ITEM(compressor, false, "compressor")
|
||||
#ifdef ENABLE_EXPERIMENTAL
|
||||
PER_MODULE_ITEM(fluidsynth, true, "fluidsynth")
|
||||
PER_MODULE_ITEM(wavetable, true, "wavetable")
|
||||
#endif
|
||||
#undef PER_MODULE_ITEM
|
||||
#endif
|
||||
#ifdef PER_SMALL_MODULE_ITEM
|
||||
#ifdef ENABLE_EXPERIMENTAL
|
||||
PER_SMALL_MODULE_ITEM(lp_filter, "lowpass12")
|
||||
PER_SMALL_MODULE_ITEM(hp_filter, "highpass12")
|
||||
PER_SMALL_MODULE_ITEM(bp_filter, "bandpass6")
|
||||
PER_SMALL_MODULE_ITEM(br_filter, "notch6")
|
||||
PER_SMALL_MODULE_ITEM(onepole_lp_filter, "lowpass6")
|
||||
PER_SMALL_MODULE_ITEM(onepole_hp_filter, "highpass6")
|
||||
PER_SMALL_MODULE_ITEM(onepole_ap_filter, "allpass")
|
||||
PER_SMALL_MODULE_ITEM(min, "min")
|
||||
PER_SMALL_MODULE_ITEM(max, "max")
|
||||
PER_SMALL_MODULE_ITEM(minus, "minus")
|
||||
PER_SMALL_MODULE_ITEM(mul, "mul")
|
||||
PER_SMALL_MODULE_ITEM(neg, "neg")
|
||||
PER_SMALL_MODULE_ITEM(min_c, "min_c")
|
||||
PER_SMALL_MODULE_ITEM(max_c, "max_c")
|
||||
PER_SMALL_MODULE_ITEM(minus_c, "minus_c")
|
||||
PER_SMALL_MODULE_ITEM(mul_c, "mul_c")
|
||||
PER_SMALL_MODULE_ITEM(neg_c, "neg_c")
|
||||
PER_SMALL_MODULE_ITEM(level2edge_c, "level2edge_c")
|
||||
PER_SMALL_MODULE_ITEM(map_lin2exp, "lin2exp")
|
||||
PER_SMALL_MODULE_ITEM(square_osc, "square_osc")
|
||||
PER_SMALL_MODULE_ITEM(saw_osc, "saw_osc")
|
||||
PER_SMALL_MODULE_ITEM(square_lfo, "square_lfo")
|
||||
PER_SMALL_MODULE_ITEM(saw_lfo, "saw_lfo")
|
||||
PER_SMALL_MODULE_ITEM(pulse_lfo, "pulse_lfo")
|
||||
PER_SMALL_MODULE_ITEM(print_a, "print_a")
|
||||
PER_SMALL_MODULE_ITEM(print_c, "print_c")
|
||||
PER_SMALL_MODULE_ITEM(print_e, "print_e")
|
||||
PER_SMALL_MODULE_ITEM(print_em, "print_em")
|
||||
PER_SMALL_MODULE_ITEM(copy_em, "copy_em")
|
||||
PER_SMALL_MODULE_ITEM(notefilter_m, "notefilter_m")
|
||||
PER_SMALL_MODULE_ITEM(ccfilter_m, "ccfilter_m")
|
||||
PER_SMALL_MODULE_ITEM(pcfilter_m, "pcfilter_m")
|
||||
PER_SMALL_MODULE_ITEM(pressurefilter_m, "pressurefilter_m")
|
||||
PER_SMALL_MODULE_ITEM(pitchbendfilter_m, "pitchbendfilter_m")
|
||||
PER_SMALL_MODULE_ITEM(systemfilter_m, "systemfilter_m")
|
||||
PER_SMALL_MODULE_ITEM(channelfilter_m, "channelfilter_m")
|
||||
PER_SMALL_MODULE_ITEM(keyfilter_m, "keyfilter_m")
|
||||
PER_SMALL_MODULE_ITEM(setchannel_m, "setchannel_m")
|
||||
PER_SMALL_MODULE_ITEM(key_less_than_m, "key_less_than_m")
|
||||
PER_SMALL_MODULE_ITEM(channel_less_than_m, "channel_less_than_m")
|
||||
PER_SMALL_MODULE_ITEM(transpose_m, "transpose_m")
|
||||
PER_SMALL_MODULE_ITEM(eventmerge_e, "eventmerge_e")
|
||||
PER_SMALL_MODULE_ITEM(quadpower_a, "quadpower_a")
|
||||
PER_SMALL_MODULE_ITEM(quadpower_c, "quadpower_c")
|
||||
PER_SMALL_MODULE_ITEM(crossfader2_a, "crossfader2_a")
|
||||
PER_SMALL_MODULE_ITEM(crossfader2_c, "crossfader2_c")
|
||||
PER_SMALL_MODULE_ITEM(linear_inertia_c, "linear_inertia_c")
|
||||
PER_SMALL_MODULE_ITEM(exp_inertia_c, "exp_inertia_c")
|
||||
PER_SMALL_MODULE_ITEM(sample_hold_edge_c, "sample_hold_edge_c")
|
||||
PER_SMALL_MODULE_ITEM(sample_hold_level_c, "sample_hold_level_c")
|
||||
PER_SMALL_MODULE_ITEM(bit_and_c, "bit_and_c")
|
||||
PER_SMALL_MODULE_ITEM(bit_or_c, "bit_or_c")
|
||||
PER_SMALL_MODULE_ITEM(bit_xor_c, "bit_xor_c")
|
||||
PER_SMALL_MODULE_ITEM(logical_and_c, "logical_and_c")
|
||||
PER_SMALL_MODULE_ITEM(logical_or_c, "logical_or_c")
|
||||
PER_SMALL_MODULE_ITEM(logical_xor_c, "logical_xor_c")
|
||||
PER_SMALL_MODULE_ITEM(logical_not_c, "logical_not_c")
|
||||
PER_SMALL_MODULE_ITEM(flipflop_c, "flipflop_c")
|
||||
PER_SMALL_MODULE_ITEM(schmitt_c, "schmitt_c")
|
||||
PER_SMALL_MODULE_ITEM(between_c, "between_c")
|
||||
PER_SMALL_MODULE_ITEM(less_c, "less_c")
|
||||
PER_SMALL_MODULE_ITEM(clip_c, "clip_c")
|
||||
PER_SMALL_MODULE_ITEM(trigger_a2c, "trigger_a2c")
|
||||
PER_SMALL_MODULE_ITEM(timer_c, "timer_c")
|
||||
PER_SMALL_MODULE_ITEM(prio_mux_c, "prio_mux_c")
|
||||
PER_SMALL_MODULE_ITEM(prio_enc8_c, "prio_enc8_c")
|
||||
PER_SMALL_MODULE_ITEM(ifthenelse_c, "ifthenelse_c")
|
||||
PER_SMALL_MODULE_ITEM(counter_c, "counter_c")
|
||||
PER_SMALL_MODULE_ITEM(mux4_c, "mux4_c")
|
||||
PER_SMALL_MODULE_ITEM(mux8_c, "mux8_c")
|
||||
PER_SMALL_MODULE_ITEM(mux16_c, "mux16_c")
|
||||
PER_SMALL_MODULE_ITEM(msgread_e, "msgread_e")
|
||||
#endif
|
||||
#undef PER_SMALL_MODULE_ITEM
|
||||
#endif
|
||||
1016
plugins/ladspa_effect/calf/calf/modules.h
Normal file
1016
plugins/ladspa_effect/calf/calf/modules.h
Normal file
File diff suppressed because it is too large
Load Diff
119
plugins/ladspa_effect/calf/calf/modules_dev.h
Normal file
119
plugins/ladspa_effect/calf/calf/modules_dev.h
Normal file
@@ -0,0 +1,119 @@
|
||||
/* Calf DSP Library
|
||||
* Prototype audio modules
|
||||
*
|
||||
* Copyright (C) 2008 Thor Harald Johansen <thj@thj.no>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __CALF_MODULES_DEV_H
|
||||
#define __CALF_MODULES_DEV_H
|
||||
|
||||
#include <calf/metadata.h>
|
||||
#include <calf/modules.h>
|
||||
|
||||
#if ENABLE_EXPERIMENTAL
|
||||
#include <fluidsynth.h>
|
||||
#endif
|
||||
|
||||
namespace calf_plugins {
|
||||
|
||||
#if ENABLE_EXPERIMENTAL
|
||||
|
||||
/// Tiny wrapper for fluidsynth
|
||||
class fluidsynth_audio_module: public audio_module<fluidsynth_metadata>
|
||||
{
|
||||
public:
|
||||
float *ins[in_count];
|
||||
float *outs[out_count];
|
||||
float *params[param_count];
|
||||
|
||||
protected:
|
||||
/// Current sample rate
|
||||
uint32_t srate;
|
||||
/// FluidSynth Settings object
|
||||
fluid_settings_t *settings;
|
||||
/// FluidSynth Synth object
|
||||
fluid_synth_t *synth;
|
||||
/// Soundfont filename
|
||||
std::string soundfont;
|
||||
/// Soundfont filename (as received from Fluidsynth)
|
||||
std::string soundfont_name;
|
||||
/// TAB-separated preset list (preset+128*bank TAB preset name LF)
|
||||
std::string soundfont_preset_list;
|
||||
/// FluidSynth assigned SoundFont ID
|
||||
int sfid;
|
||||
/// Map of preset+128*bank to preset name
|
||||
std::map<uint32_t, std::string> sf_preset_names;
|
||||
/// Last selected preset+128*bank
|
||||
uint32_t last_selected_preset;
|
||||
/// Serial number of status data
|
||||
int status_serial;
|
||||
/// Preset number to set on next process() call
|
||||
volatile int set_preset;
|
||||
|
||||
/// Update last_selected_preset based on synth object state
|
||||
void update_preset_num();
|
||||
/// Create a fluidsynth object and load the current soundfont
|
||||
fluid_synth_t *create_synth(int &new_sfid);
|
||||
public:
|
||||
/// Constructor to initialize handles to NULL
|
||||
fluidsynth_audio_module();
|
||||
|
||||
void post_instantiate();
|
||||
void set_sample_rate(uint32_t sr) { srate = sr; }
|
||||
/// Handle MIDI Note On message (by sending it to fluidsynth)
|
||||
void note_on(int note, int vel);
|
||||
/// Handle MIDI Note Off message (by sending it to fluidsynth)
|
||||
void note_off(int note, int vel);
|
||||
/// Handle pitch bend message.
|
||||
inline void pitch_bend(int value)
|
||||
{
|
||||
fluid_synth_pitch_bend(synth, 0, value + 0x2000);
|
||||
}
|
||||
/// Handle control change messages.
|
||||
void control_change(int controller, int value);
|
||||
/// Handle program change messages.
|
||||
void program_change(int program);
|
||||
|
||||
/// Update variables from control ports.
|
||||
void params_changed() {
|
||||
}
|
||||
void activate();
|
||||
void deactivate();
|
||||
/// No CV inputs for now
|
||||
bool is_cv(int param_no) { return false; }
|
||||
/// Practically all the stuff here is noisy... for now
|
||||
bool is_noisy(int param_no) { return true; }
|
||||
/// Main processing function
|
||||
uint32_t process(uint32_t offset, uint32_t nsamples, uint32_t inputs_mask, uint32_t outputs_mask);
|
||||
/// DSSI-style configure function for handling string port data
|
||||
char *configure(const char *key, const char *value);
|
||||
void send_configures(send_configure_iface *sci);
|
||||
int send_status_updates(send_updates_iface *sui, int last_serial);
|
||||
uint32_t message_run(const void *valid_inputs, void *output_ports) {
|
||||
// silence a default printf (which is kind of a warning about unhandled message_run)
|
||||
return 0;
|
||||
}
|
||||
~fluidsynth_audio_module();
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
202
plugins/ladspa_effect/calf/calf/modules_small.h
Normal file
202
plugins/ladspa_effect/calf/calf/modules_small.h
Normal file
@@ -0,0 +1,202 @@
|
||||
/* Calf DSP Library
|
||||
* "Small" audio modules for modular synthesis
|
||||
*
|
||||
* Copyright (C) 2001-2007 Krzysztof Foltman
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __CALF_MODULES_SMALL_H
|
||||
#define __CALF_MODULES_SMALL_H
|
||||
|
||||
#if USE_LV2
|
||||
|
||||
#include <lv2.h>
|
||||
#include "plugininfo.h"
|
||||
#include "lv2_polymorphic_port.h"
|
||||
#include "lv2helpers.h"
|
||||
|
||||
namespace calf_plugins {
|
||||
|
||||
/// Empty implementations for plugin functions. Note, that some functions aren't virtual, because they're called via the particular
|
||||
/// subclass via template wrappers (ladspa_small_wrapper<> etc), not via base class pointer/reference. On the other hand,
|
||||
/// other functions are virtual when overhead is acceptable (instantiation time functions etc.)
|
||||
class null_small_audio_module: public uri_map_access
|
||||
{
|
||||
public:
|
||||
uint32_t srate;
|
||||
double odsr;
|
||||
uint32_t poly_type_control, poly_type_audio;
|
||||
/// for polymorphic ports: "is audio" flags for first 32 ports (should be sufficient for most plugins)
|
||||
uint32_t poly_port_types;
|
||||
|
||||
null_small_audio_module()
|
||||
: srate((uint32_t)-1)
|
||||
, odsr(0.)
|
||||
, poly_type_control(0)
|
||||
, poly_type_audio(0)
|
||||
, poly_port_types(0)
|
||||
{
|
||||
}
|
||||
|
||||
/// Called when host changes type of the polymorphic port
|
||||
inline void set_port_type(uint32_t port, uint32_t type, void *type_data) {
|
||||
if (port >= 32)
|
||||
return;
|
||||
uint32_t port_mask = 1 << port;
|
||||
if (type == poly_type_control)
|
||||
poly_port_types &= ~port_mask;
|
||||
else if (type == poly_type_audio)
|
||||
poly_port_types |= port_mask;
|
||||
on_port_types_changed();
|
||||
}
|
||||
|
||||
/// Returns 1 for audio ports and 0 for control ports
|
||||
inline unsigned int port_is_audio(unsigned int port) {
|
||||
return (poly_port_types >> port) & 1;
|
||||
}
|
||||
|
||||
/// Returns (unsigned)-1 for audio ports and 0 for control ports
|
||||
inline unsigned int port_audio_mask(unsigned int port) {
|
||||
return 0 - ((poly_port_types >> port) & 1);
|
||||
}
|
||||
|
||||
/// Returns (unsigned)-1 for audio ports and 0 for control ports
|
||||
static inline unsigned int port_audio_mask(unsigned int port, uint32_t poly_port_types) {
|
||||
return 0 - ((poly_port_types >> port) & 1);
|
||||
}
|
||||
|
||||
virtual void on_port_types_changed() {}
|
||||
inline void set_bundle_path(const char *path) {}
|
||||
/// Called to map all the necessary URIs
|
||||
virtual void map_uris()
|
||||
{
|
||||
poly_type_control = map_uri(LV2_POLYMORPHIC_PORT_URI, "http://lv2plug.in/ns/lv2core#ControlPort");
|
||||
poly_type_audio = map_uri(LV2_POLYMORPHIC_PORT_URI, "http://lv2plug.in/ns/lv2core#AudioPort");
|
||||
}
|
||||
/// Called on instantiation with the list of LV2 features called
|
||||
virtual void use_features(const LV2_Feature *const *features) {
|
||||
while(*features)
|
||||
{
|
||||
use_feature((*features)->URI, (*features)->data);
|
||||
features++;
|
||||
}
|
||||
}
|
||||
/// LADSPA-esque activate function, except it is called after ports are connected, not before
|
||||
inline void activate() {}
|
||||
/// LADSPA-esque deactivate function
|
||||
inline void deactivate() {}
|
||||
/// Set sample rate for the plugin
|
||||
inline void set_sample_rate(uint32_t sr) { srate = sr; odsr = 1.0 / sr; }
|
||||
static inline const void *ext_data(const char *URI) { return NULL; }
|
||||
};
|
||||
|
||||
/// Templatized version useful when the number of inputs and outputs is small
|
||||
template<unsigned int Inputs, unsigned int Outputs>
|
||||
class small_audio_module_base: public null_small_audio_module
|
||||
{
|
||||
public:
|
||||
enum { in_count = Inputs, out_count = Outputs };
|
||||
/// Input pointers
|
||||
float *ins[in_count];
|
||||
/// Output pointers
|
||||
float *outs[out_count];
|
||||
};
|
||||
|
||||
template<class Module>
|
||||
struct lv2_small_wrapper
|
||||
{
|
||||
typedef Module instance;
|
||||
static LV2_Descriptor descriptor;
|
||||
std::string uri;
|
||||
static uint32_t poly_port_types;
|
||||
|
||||
lv2_small_wrapper(const char *id)
|
||||
{
|
||||
uri = "http://calf.sourceforge.net/small_plugins/" + std::string(id);
|
||||
descriptor.URI = uri.c_str();
|
||||
descriptor.instantiate = cb_instantiate;
|
||||
descriptor.connect_port = cb_connect;
|
||||
descriptor.activate = cb_activate;
|
||||
descriptor.run = cb_run;
|
||||
descriptor.deactivate = cb_deactivate;
|
||||
descriptor.cleanup = cb_cleanup;
|
||||
descriptor.extension_data = cb_ext_data;
|
||||
|
||||
plugin_port_type_grabber ptg(poly_port_types);
|
||||
Module::plugin_info(&ptg);
|
||||
}
|
||||
|
||||
static void cb_connect(LV2_Handle Instance, uint32_t port, void *DataLocation) {
|
||||
unsigned long ins = Module::in_count;
|
||||
unsigned long outs = Module::out_count;
|
||||
instance *const mod = (instance *)Instance;
|
||||
if (port < ins)
|
||||
mod->ins[port] = (float *)DataLocation;
|
||||
else if (port < ins + outs)
|
||||
mod->outs[port - ins] = (float *)DataLocation;
|
||||
}
|
||||
|
||||
static void cb_activate(LV2_Handle Instance) {
|
||||
// Note the changed semantics (now more LV2-like)
|
||||
instance *const mod = (instance *)Instance;
|
||||
mod->activate();
|
||||
}
|
||||
|
||||
static void cb_deactivate(LV2_Handle Instance) {
|
||||
instance *const mod = (instance *)Instance;
|
||||
mod->deactivate();
|
||||
}
|
||||
|
||||
static uint32_t cb_set_type(LV2_Handle Instance, uint32_t port, uint32_t type, void *type_data)
|
||||
{
|
||||
instance *const mod = (instance *)Instance;
|
||||
mod->set_port_type(port, type, type_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static LV2_Handle cb_instantiate(const LV2_Descriptor * Descriptor, double sample_rate, const char *bundle_path, const LV2_Feature *const *features)
|
||||
{
|
||||
instance *mod = new instance();
|
||||
mod->poly_port_types = poly_port_types;
|
||||
// XXXKF some people use fractional sample rates; we respect them ;-)
|
||||
mod->set_bundle_path(bundle_path);
|
||||
mod->use_features(features);
|
||||
mod->set_sample_rate((uint32_t)sample_rate);
|
||||
return mod;
|
||||
}
|
||||
|
||||
static void cb_run(LV2_Handle Instance, uint32_t SampleCount) {
|
||||
instance *const mod = (instance *)Instance;
|
||||
mod->process(SampleCount);
|
||||
}
|
||||
|
||||
static void cb_cleanup(LV2_Handle Instance) {
|
||||
instance *const mod = (instance *)Instance;
|
||||
delete mod;
|
||||
}
|
||||
|
||||
static const void *cb_ext_data(const char *URI) {
|
||||
return Module::ext_data(URI);
|
||||
}
|
||||
};
|
||||
|
||||
extern const LV2_Descriptor *lv2_small_descriptor(uint32_t index);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
264
plugins/ladspa_effect/calf/calf/modules_synths.h
Normal file
264
plugins/ladspa_effect/calf/calf/modules_synths.h
Normal file
@@ -0,0 +1,264 @@
|
||||
/* Calf DSP Library
|
||||
* Audio modules - synthesizers
|
||||
*
|
||||
* Copyright (C) 2001-2007 Krzysztof Foltman
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __CALF_MODULES_SYNTHS_H
|
||||
#define __CALF_MODULES_SYNTHS_H
|
||||
|
||||
#include <assert.h>
|
||||
#include "biquad.h"
|
||||
#include "onepole.h"
|
||||
#include "audio_fx.h"
|
||||
#include "inertia.h"
|
||||
#include "osc.h"
|
||||
#include "synth.h"
|
||||
#include "envelope.h"
|
||||
#include "organ.h"
|
||||
#include "modmatrix.h"
|
||||
|
||||
namespace calf_plugins {
|
||||
|
||||
#define MONOSYNTH_WAVE_BITS 12
|
||||
|
||||
/// Monosynth-in-making. Parameters may change at any point, so don't make songs with it!
|
||||
/// It lacks inertia for parameters, even for those that really need it.
|
||||
class monosynth_audio_module: public audio_module<monosynth_metadata>, public line_graph_iface, public mod_matrix
|
||||
{
|
||||
public:
|
||||
enum { mod_matrix_slots = 10 };
|
||||
float *ins[in_count];
|
||||
float *outs[out_count];
|
||||
float *params[param_count];
|
||||
uint32_t srate, crate;
|
||||
static dsp::waveform_family<MONOSYNTH_WAVE_BITS> *waves;
|
||||
dsp::waveform_oscillator<MONOSYNTH_WAVE_BITS> osc1, osc2;
|
||||
dsp::triangle_lfo lfo;
|
||||
bool running, stopping, gate, force_fadeout;
|
||||
int last_key;
|
||||
|
||||
float buffer[step_size], buffer2[step_size];
|
||||
uint32_t output_pos;
|
||||
dsp::onepole<float> phaseshifter;
|
||||
dsp::biquad_d1_lerp<float> filter;
|
||||
dsp::biquad_d1_lerp<float> filter2;
|
||||
/// Waveform number - OSC1
|
||||
int wave1;
|
||||
/// Waveform number - OSC2
|
||||
int wave2;
|
||||
/// Last used waveform number - OSC1
|
||||
int prev_wave1;
|
||||
/// Last used waveform number - OSC2
|
||||
int prev_wave2;
|
||||
int filter_type, last_filter_type;
|
||||
float freq, start_freq, target_freq, cutoff, decay_factor, fgain, fgain_delta, separation;
|
||||
float detune, xpose, xfade, ampctl, fltctl, queue_vel;
|
||||
float odcr, porta_time, lfo_bend, lfo_clock, last_lfov, modwheel_value;
|
||||
/// Last value of phase shift for pulse width emulation for OSC1
|
||||
int32_t last_pwshift1;
|
||||
/// Last value of phase shift for pulse width emulation for OSC2
|
||||
int32_t last_pwshift2;
|
||||
int queue_note_on, stop_count, modwheel_value_int;
|
||||
int legato;
|
||||
dsp::adsr envelope;
|
||||
dsp::keystack stack;
|
||||
dsp::gain_smoothing master;
|
||||
/// Smoothed cutoff value
|
||||
dsp::inertia<dsp::exponential_ramp> inertia_cutoff;
|
||||
/// Smoothed pitch bend value
|
||||
dsp::inertia<dsp::exponential_ramp> inertia_pitchbend;
|
||||
/// Smoothed channel pressure value
|
||||
dsp::inertia<dsp::linear_ramp> inertia_pressure;
|
||||
/// Rows of the modulation matrix
|
||||
dsp::modulation_entry mod_matrix_data[mod_matrix_slots];
|
||||
/// Currently used velocity
|
||||
float velocity;
|
||||
/// Last value of oscillator mix ratio
|
||||
float last_xfade;
|
||||
/// Current calculated mod matrix outputs
|
||||
float moddest[moddest_count];
|
||||
|
||||
monosynth_audio_module();
|
||||
static void precalculate_waves(progress_report_iface *reporter);
|
||||
void set_sample_rate(uint32_t sr);
|
||||
void delayed_note_on();
|
||||
/// Handle MIDI Note On message (does not immediately trigger a note, as it must start on
|
||||
/// boundary of step_size samples).
|
||||
void note_on(int note, int vel);
|
||||
/// Handle MIDI Note Off message
|
||||
void note_off(int note, int vel);
|
||||
/// Handle MIDI Channel Pressure
|
||||
void channel_pressure(int value);
|
||||
/// Handle pitch bend message.
|
||||
inline void pitch_bend(int value)
|
||||
{
|
||||
inertia_pitchbend.set_inertia(pow(2.0, (value * *params[par_pwhlrange]) / (1200.0 * 8192.0)));
|
||||
}
|
||||
/// Update oscillator frequency based on base frequency, detune amount, pitch bend scaling factor and sample rate.
|
||||
inline void set_frequency()
|
||||
{
|
||||
float detune_scaled = (detune - 1); // * log(freq / 440);
|
||||
if (*params[par_scaledetune] > 0)
|
||||
detune_scaled *= pow(20.0 / freq, *params[par_scaledetune]);
|
||||
float p1 = 1, p2 = 1;
|
||||
if (moddest[moddest_o1detune] != 0)
|
||||
p1 = pow(2.0, moddest[moddest_o1detune] * (1.0 / 1200.0));
|
||||
if (moddest[moddest_o2detune] != 0)
|
||||
p2 = pow(2.0, moddest[moddest_o2detune] * (1.0 / 1200.0));
|
||||
osc1.set_freq(freq * (1 - detune_scaled) * p1 * inertia_pitchbend.get_last() * lfo_bend, srate);
|
||||
osc2.set_freq(freq * (1 + detune_scaled) * p2 * inertia_pitchbend.get_last() * lfo_bend * xpose, srate);
|
||||
}
|
||||
/// Handle control change messages.
|
||||
void control_change(int controller, int value);
|
||||
/// Update variables from control ports.
|
||||
void params_changed() {
|
||||
float sf = 0.001f;
|
||||
envelope.set(*params[par_attack] * sf, *params[par_decay] * sf, std::min(0.999f, *params[par_sustain]), *params[par_release] * sf, srate / step_size, *params[par_fade] * sf);
|
||||
filter_type = dsp::fastf2i_drm(*params[par_filtertype]);
|
||||
decay_factor = odcr * 1000.0 / *params[par_decay];
|
||||
separation = pow(2.0, *params[par_cutoffsep] / 1200.0);
|
||||
wave1 = dsp::clip(dsp::fastf2i_drm(*params[par_wave1]), 0, (int)wave_count - 1);
|
||||
wave2 = dsp::clip(dsp::fastf2i_drm(*params[par_wave2]), 0, (int)wave_count - 1);
|
||||
detune = pow(2.0, *params[par_detune] / 1200.0);
|
||||
xpose = pow(2.0, *params[par_osc2xpose] / 12.0);
|
||||
xfade = *params[par_oscmix];
|
||||
legato = dsp::fastf2i_drm(*params[par_legato]);
|
||||
master.set_inertia(*params[par_master]);
|
||||
set_frequency();
|
||||
if (wave1 != prev_wave1 || wave2 != prev_wave2)
|
||||
lookup_waveforms();
|
||||
}
|
||||
void activate();
|
||||
void deactivate();
|
||||
void post_instantiate()
|
||||
{
|
||||
precalculate_waves(progress_report);
|
||||
}
|
||||
/// Set waveform addresses for oscillators
|
||||
void lookup_waveforms();
|
||||
/// Run oscillators
|
||||
void calculate_buffer_oscs(float lfo);
|
||||
/// Run two filters in series to produce mono output samples.
|
||||
void calculate_buffer_ser();
|
||||
/// Run one filter to produce mono output samples.
|
||||
void calculate_buffer_single();
|
||||
/// Run two filters (one per channel) to produce stereo output samples.
|
||||
void calculate_buffer_stereo();
|
||||
/// Retrieve filter graph (which is 'live' so it cannot be generated by get_static_graph), or fall back to get_static_graph.
|
||||
bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context);
|
||||
/// @retval true if the filter 1 is to be used for the left channel and filter 2 for the right channel
|
||||
/// @retval false if filters are to be connected in series and sent (mono) to both channels
|
||||
inline bool is_stereo_filter() const
|
||||
{
|
||||
return filter_type == flt_2lp12 || filter_type == flt_2bp6;
|
||||
}
|
||||
/// No CV inputs for now
|
||||
bool is_cv(int param_no) { return false; }
|
||||
/// Practically all the stuff here is noisy
|
||||
bool is_noisy(int param_no) { return param_no != par_cutoff; }
|
||||
/// Calculate control signals and produce step_size samples of output.
|
||||
void calculate_step();
|
||||
/// Main processing function
|
||||
uint32_t process(uint32_t offset, uint32_t nsamples, uint32_t inputs_mask, uint32_t outputs_mask);
|
||||
};
|
||||
|
||||
struct organ_audio_module: public audio_module<organ_metadata>, public dsp::drawbar_organ, public line_graph_iface
|
||||
{
|
||||
public:
|
||||
using drawbar_organ::note_on;
|
||||
using drawbar_organ::note_off;
|
||||
using drawbar_organ::control_change;
|
||||
enum { param_count = drawbar_organ::param_count};
|
||||
float *ins[in_count];
|
||||
float *outs[out_count];
|
||||
float *params[param_count];
|
||||
dsp::organ_parameters par_values;
|
||||
uint32_t srate;
|
||||
bool panic_flag;
|
||||
/// Value for configure variable map_curve
|
||||
std::string var_map_curve;
|
||||
|
||||
organ_audio_module()
|
||||
: drawbar_organ(&par_values)
|
||||
{
|
||||
var_map_curve = "2\n0 1\n1 1\n"; // XXXKF hacky bugfix
|
||||
}
|
||||
|
||||
void post_instantiate()
|
||||
{
|
||||
dsp::organ_voice_base::precalculate_waves(progress_report);
|
||||
}
|
||||
|
||||
void set_sample_rate(uint32_t sr) {
|
||||
srate = sr;
|
||||
}
|
||||
void params_changed() {
|
||||
for (int i = 0; i < param_count - var_count; i++)
|
||||
((float *)&par_values)[i] = *params[i];
|
||||
|
||||
unsigned int old_poly = polyphony_limit;
|
||||
polyphony_limit = dsp::clip(dsp::fastf2i_drm(*params[par_polyphony]), 1, 32);
|
||||
if (polyphony_limit < old_poly)
|
||||
trim_voices();
|
||||
|
||||
update_params();
|
||||
}
|
||||
inline void pitch_bend(int amt)
|
||||
{
|
||||
drawbar_organ::pitch_bend(amt);
|
||||
}
|
||||
void activate() {
|
||||
setup(srate);
|
||||
panic_flag = false;
|
||||
}
|
||||
void deactivate();
|
||||
uint32_t process(uint32_t offset, uint32_t nsamples, uint32_t inputs_mask, uint32_t outputs_mask) {
|
||||
float *o[2] = { outs[0] + offset, outs[1] + offset };
|
||||
if (panic_flag)
|
||||
{
|
||||
control_change(120, 0); // stop all sounds
|
||||
control_change(121, 0); // reset all controllers
|
||||
panic_flag = false;
|
||||
}
|
||||
render_separate(o, nsamples);
|
||||
return 3;
|
||||
}
|
||||
/// No CV inputs for now
|
||||
bool is_cv(int param_no) { return false; }
|
||||
/// Practically all the stuff here is noisy
|
||||
bool is_noisy(int param_no) { return true; }
|
||||
void execute(int cmd_no);
|
||||
bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context);
|
||||
|
||||
char *configure(const char *key, const char *value);
|
||||
void send_configures(send_configure_iface *);
|
||||
uint32_t message_run(const void *valid_inputs, void *output_ports) {
|
||||
// silence a default printf (which is kind of a warning about unhandled message_run)
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#if ENABLE_EXPERIMENTAL
|
||||
|
||||
#include "wavetable.h"
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
213
plugins/ladspa_effect/calf/calf/multichorus.h
Normal file
213
plugins/ladspa_effect/calf/calf/multichorus.h
Normal file
@@ -0,0 +1,213 @@
|
||||
/* Calf DSP Library
|
||||
* Multitap chorus class.
|
||||
*
|
||||
* Copyright (C) 2001-2007 Krzysztof Foltman
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __CALF_MULTICHORUS_H
|
||||
#define __CALF_MULTICHORUS_H
|
||||
|
||||
#include "audio_fx.h"
|
||||
|
||||
namespace dsp {
|
||||
|
||||
typedef fixed_point<unsigned int, 20> chorus_phase;
|
||||
|
||||
template<class T, uint32_t Voices>
|
||||
class sine_multi_lfo
|
||||
{
|
||||
protected:
|
||||
sine_table<int, 4096, 65535> sine;
|
||||
|
||||
public:
|
||||
/// Current LFO phase
|
||||
chorus_phase phase;
|
||||
/// LFO phase increment
|
||||
chorus_phase dphase;
|
||||
/// LFO phase per-voice increment
|
||||
chorus_phase vphase;
|
||||
/// Current number of voices
|
||||
uint32_t voices;
|
||||
/// Current scale (output multiplier)
|
||||
T scale;
|
||||
/// Per-voice offset unit (the value that says how much the voices are offset with respect to each other in non-100% 'overlap' mode), scaled so that full range = 131072
|
||||
int32_t voice_offset;
|
||||
/// LFO Range scaling for non-100% overlap
|
||||
uint32_t voice_depth;
|
||||
public:
|
||||
sine_multi_lfo()
|
||||
{
|
||||
phase = dphase = vphase = 0.0;
|
||||
voice_offset = 0;
|
||||
voice_depth = 1U << 31;
|
||||
|
||||
set_voices(Voices);
|
||||
}
|
||||
inline uint32_t get_voices() const
|
||||
{
|
||||
return voices;
|
||||
}
|
||||
inline void set_voices(uint32_t value)
|
||||
{
|
||||
voices = value;
|
||||
// use sqrt, because some phases will cancel each other - so 1 / N is usually too low
|
||||
scale = sqrt(1.0 / voices);
|
||||
}
|
||||
inline void set_overlap(float overlap)
|
||||
{
|
||||
// If we scale the delay amount so that full range of a single LFO is 0..1, all the overlapped LFOs will cover 0..range
|
||||
// How it's calculated:
|
||||
// 1. First voice is assumed to always cover the range of 0..1
|
||||
// 2. Each remaining voice contributes an interval of a width = 1 - overlap, starting from the end of the interval of the previous voice
|
||||
// Coverage = non-overlapped part of the LFO range in the 1st voice
|
||||
float range = 1.f + (1.f - overlap) * (voices - 1);
|
||||
float scaling = 1.f / range;
|
||||
voice_offset = (int)(131072 * (1 - overlap) / range);
|
||||
voice_depth = (unsigned int)((1U << 30) * 1.0 * scaling);
|
||||
}
|
||||
/// Get LFO value for given voice, returns a values in range of [-65536, 65535] (or close)
|
||||
inline int get_value(uint32_t voice) {
|
||||
// find this voice's phase (= phase + voice * 360 degrees / number of voices)
|
||||
chorus_phase voice_phase = phase + vphase * (int)voice;
|
||||
// find table offset
|
||||
unsigned int ipart = voice_phase.ipart();
|
||||
// interpolate (use 14 bits of precision - because the table itself uses 17 bits and the result of multiplication must fit in int32_t)
|
||||
// note, the result is still -65535 .. 65535, it's just interpolated
|
||||
// it is never reaching -65536 - but that's acceptable
|
||||
int intval = voice_phase.lerp_by_fract_int<int, 14, int>(sine.data[ipart], sine.data[ipart+1]);
|
||||
// apply the voice offset/depth (rescale from -65535..65535 to appropriate voice's "band")
|
||||
return -65535 + voice * voice_offset + ((voice_depth >> (30-13)) * (65536 + intval) >> 13);
|
||||
}
|
||||
inline void step() {
|
||||
phase += dphase;
|
||||
}
|
||||
inline T get_scale() const {
|
||||
return scale;
|
||||
}
|
||||
void reset() {
|
||||
phase = 0.f;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Multi-tap chorus without feedback.
|
||||
* Perhaps MaxDelay should be a bit longer!
|
||||
*/
|
||||
template<class T, class MultiLfo, class Postprocessor, int MaxDelay=4096>
|
||||
class multichorus: public chorus_base
|
||||
{
|
||||
protected:
|
||||
simple_delay<MaxDelay,T> delay;
|
||||
public:
|
||||
MultiLfo lfo;
|
||||
Postprocessor post;
|
||||
public:
|
||||
multichorus() {
|
||||
rate = 0.63f;
|
||||
dry = 0.5f;
|
||||
wet = 0.5f;
|
||||
min_delay = 0.005f;
|
||||
mod_depth = 0.0025f;
|
||||
setup(44100);
|
||||
}
|
||||
void reset() {
|
||||
delay.reset();
|
||||
lfo.reset();
|
||||
}
|
||||
void set_rate(float rate) {
|
||||
chorus_base::set_rate(rate);
|
||||
lfo.dphase = dphase;
|
||||
}
|
||||
virtual void setup(int sample_rate) {
|
||||
modulation_effect::setup(sample_rate);
|
||||
delay.reset();
|
||||
lfo.reset();
|
||||
set_min_delay(get_min_delay());
|
||||
set_mod_depth(get_mod_depth());
|
||||
}
|
||||
template<class OutIter, class InIter>
|
||||
void process(OutIter buf_out, InIter buf_in, int nsamples) {
|
||||
int mds = min_delay_samples + mod_depth_samples * 1024 + 2*65536;
|
||||
int mdepth = mod_depth_samples;
|
||||
// 1 sample peak-to-peak = mod_depth_samples of 32 (this scaling stuff is tricky and may - but shouldn't - be wrong)
|
||||
// with 192 kHz sample rate, 1 ms = 192 samples, and the maximum 20 ms = 3840 samples (so, 4096 will be used)
|
||||
// 3840 samples of mod depth = mdepth of 122880 (which multiplied by 65536 doesn't fit in int32_t)
|
||||
// so, it will be right-shifted by 2, which gives it a safe range of 30720
|
||||
// NB: calculation of mod_depth_samples (and multiply-by-32) is in chorus_base::set_mod_depth
|
||||
mdepth = mdepth >> 2;
|
||||
T scale = lfo.get_scale();
|
||||
for (int i=0; i<nsamples; i++) {
|
||||
phase += dphase;
|
||||
|
||||
float in = *buf_in++;
|
||||
|
||||
delay.put(in);
|
||||
unsigned int nvoices = lfo.get_voices();
|
||||
T out = 0.f;
|
||||
// add up values from all voices, each voice tell its LFO phase and the buffer value is picked at that location
|
||||
for (unsigned int v = 0; v < nvoices; v++)
|
||||
{
|
||||
int lfo_output = lfo.get_value(v);
|
||||
// 3 = log2(32 >> 2) + 1 because the LFO value is in range of [-65535, 65535] (17 bits)
|
||||
int dv = mds + (mdepth * lfo_output >> (3 + 1));
|
||||
int ifv = dv >> 16;
|
||||
T fd; // signal from delay's output
|
||||
delay.get_interp(fd, ifv, (dv & 0xFFFF)*(1.0/65536.0));
|
||||
out += fd;
|
||||
}
|
||||
// apply the post filter
|
||||
out = post.process(out);
|
||||
T sdry = in * gs_dry.get();
|
||||
T swet = out * gs_wet.get() * scale;
|
||||
*buf_out++ = sdry + swet;
|
||||
lfo.step();
|
||||
}
|
||||
post.sanitize();
|
||||
}
|
||||
float freq_gain(float freq, float sr)
|
||||
{
|
||||
typedef std::complex<double> cfloat;
|
||||
freq *= 2.0 * M_PI / sr;
|
||||
cfloat z = 1.0 / exp(cfloat(0.0, freq)); // z^-1
|
||||
cfloat h = 0.0;
|
||||
int mds = min_delay_samples + mod_depth_samples * 1024 + 2*65536;
|
||||
int mdepth = mod_depth_samples;
|
||||
mdepth = mdepth >> 2;
|
||||
T scale = lfo.get_scale();
|
||||
unsigned int nvoices = lfo.get_voices();
|
||||
for (unsigned int v = 0; v < nvoices; v++)
|
||||
{
|
||||
int lfo_output = lfo.get_value(v);
|
||||
// 3 = log2(32 >> 2) + 1 because the LFO value is in range of [-65535, 65535] (17 bits)
|
||||
int dv = mds + (mdepth * lfo_output >> (3 + 1));
|
||||
int fldp = dv >> 16;
|
||||
cfloat zn = std::pow(z, fldp); // z^-N
|
||||
h += zn + (zn * z - zn) * cfloat(dv / 65536.0 - fldp);
|
||||
}
|
||||
// apply the post filter
|
||||
h *= post.h_z(z);
|
||||
// mix with dry signal
|
||||
float v = std::abs(cfloat(gs_dry.get_last()) + cfloat(scale * gs_wet.get_last()) * h);
|
||||
return v;
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
192
plugins/ladspa_effect/calf/calf/onepole.h
Normal file
192
plugins/ladspa_effect/calf/calf/onepole.h
Normal file
@@ -0,0 +1,192 @@
|
||||
/* Calf DSP Library
|
||||
* Basic one-pole one-zero filters based on bilinear transform.
|
||||
* Copyright (C) 2001-2007 Krzysztof Foltman
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __CALF_ONEPOLE_H
|
||||
#define __CALF_ONEPOLE_H
|
||||
|
||||
#include "primitives.h"
|
||||
|
||||
namespace dsp {
|
||||
|
||||
/**
|
||||
* one-pole filter, for floating point values
|
||||
* coefficient calculation is based on bilinear transform, and the code itself is based on my very old OneSignal lib
|
||||
* lp and hp are *somewhat* tested, allpass is not tested at all
|
||||
* don't use this for integers because it won't work
|
||||
*/
|
||||
template<class T = float, class Coeff = float>
|
||||
class onepole
|
||||
{
|
||||
public:
|
||||
typedef std::complex<double> cfloat;
|
||||
|
||||
T x1, y1;
|
||||
Coeff a0, a1, b1;
|
||||
|
||||
onepole()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
/// Set coefficients for a lowpass filter
|
||||
void set_lp(float fc, float sr)
|
||||
{
|
||||
// x x
|
||||
// x+1 x-1
|
||||
Coeff x = tan (M_PI * fc / (2 * sr));
|
||||
Coeff q = 1/(1+x);
|
||||
a0 = a1 = x*q;
|
||||
b1 = (x-1)*q;
|
||||
}
|
||||
|
||||
/// Set coefficients for an allpass filter
|
||||
void set_ap(float fc, float sr)
|
||||
{
|
||||
// x-1 x+1
|
||||
// x+1 x-1
|
||||
Coeff x = tan (M_PI * fc / (2 * sr));
|
||||
Coeff q = 1/(1+x);
|
||||
b1 = a0 = (x-1)*q;
|
||||
a1 = 1;
|
||||
}
|
||||
|
||||
/// Set coefficients for an allpass filter, using omega instead of fc and sr
|
||||
/// omega = (PI / 2) * fc / sr
|
||||
void set_ap_w(float w)
|
||||
{
|
||||
// x-1 x+1
|
||||
// x+1 x-1
|
||||
Coeff x = tan (w);
|
||||
Coeff q = 1/(1+x);
|
||||
b1 = a0 = (x-1)*q;
|
||||
a1 = 1;
|
||||
}
|
||||
|
||||
/// Set coefficients for a highpass filter
|
||||
void set_hp(float fc, float sr)
|
||||
{
|
||||
// x -x
|
||||
// x+1 x-1
|
||||
Coeff x = tan (M_PI * fc / (2 * sr));
|
||||
Coeff q = 1/(1+x);
|
||||
a0 = q;
|
||||
a1 = -a0;
|
||||
b1 = (x-1)*q;
|
||||
}
|
||||
|
||||
/// Process one sample
|
||||
inline T process(T in)
|
||||
{
|
||||
T out = in * a0 + x1 * a1 - y1 * b1;
|
||||
x1 = in;
|
||||
y1 = out;
|
||||
return out;
|
||||
}
|
||||
|
||||
/// Process one sample, assuming it's a lowpass filter (optimized special case)
|
||||
inline T process_lp(T in)
|
||||
{
|
||||
T out = (in + x1) * a0 - y1 * b1;
|
||||
x1 = in;
|
||||
y1 = out;
|
||||
return out;
|
||||
}
|
||||
|
||||
/// Process one sample, assuming it's a highpass filter (optimized special case)
|
||||
inline T process_hp(T in)
|
||||
{
|
||||
T out = (in - x1) * a0 - y1 * b1;
|
||||
x1 = in;
|
||||
y1 = out;
|
||||
return out;
|
||||
}
|
||||
|
||||
/// Process one sample, assuming it's an allpass filter (optimized special case)
|
||||
inline T process_ap(T in)
|
||||
{
|
||||
T out = (in - y1) * a0 + x1;
|
||||
x1 = in;
|
||||
y1 = out;
|
||||
return out;
|
||||
}
|
||||
|
||||
/// Process one sample using external state variables
|
||||
inline T process_ap(T in, float &x1, float &y1)
|
||||
{
|
||||
T out = (in - y1) * a0 + x1;
|
||||
x1 = in;
|
||||
y1 = out;
|
||||
return out;
|
||||
}
|
||||
|
||||
/// Process one sample using external state variables, including filter coeff
|
||||
inline T process_ap(T in, float &x1, float &y1, float a0)
|
||||
{
|
||||
T out = (in - y1) * a0 + x1;
|
||||
x1 = in;
|
||||
y1 = out;
|
||||
return out;
|
||||
}
|
||||
|
||||
inline bool empty() {
|
||||
return y1 == 0;
|
||||
}
|
||||
|
||||
inline void sanitize()
|
||||
{
|
||||
dsp::sanitize(x1);
|
||||
dsp::sanitize(y1);
|
||||
}
|
||||
|
||||
inline void reset()
|
||||
{
|
||||
dsp::zero(x1);
|
||||
dsp::zero(y1);
|
||||
}
|
||||
|
||||
template<class U>
|
||||
inline void copy_coeffs(const onepole<U> &src)
|
||||
{
|
||||
a0 = src.a0;
|
||||
a1 = src.a1;
|
||||
b1 = src.b1;
|
||||
}
|
||||
|
||||
/// Return the filter's gain at frequency freq
|
||||
/// @param freq Frequency to look up
|
||||
/// @param sr Filter sample rate (used to convert frequency to angular frequency)
|
||||
float freq_gain(float freq, float sr)
|
||||
{
|
||||
freq *= 2.0 * M_PI / sr;
|
||||
cfloat z = 1.0 / exp(cfloat(0.0, freq));
|
||||
|
||||
return std::abs(h_z(z));
|
||||
}
|
||||
|
||||
/// Return H(z) the filter's gain at frequency freq
|
||||
/// @param z Z variable (e^jw)
|
||||
cfloat h_z(const cfloat &z)
|
||||
{
|
||||
return (cfloat(a0) + double(a1) * z) / (cfloat(1.0) + double(b1) * z);
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
374
plugins/ladspa_effect/calf/calf/organ.h
Normal file
374
plugins/ladspa_effect/calf/calf/organ.h
Normal file
@@ -0,0 +1,374 @@
|
||||
/* Calf DSP Library
|
||||
* Drawbar organ emulator.
|
||||
*
|
||||
* Copyright (C) 2007 Krzysztof Foltman
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __CALF_ORGAN_H
|
||||
#define __CALF_ORGAN_H
|
||||
|
||||
#include "synth.h"
|
||||
#include "envelope.h"
|
||||
#include "metadata.h"
|
||||
|
||||
#define ORGAN_KEYTRACK_POINTS 4
|
||||
|
||||
namespace dsp
|
||||
{
|
||||
|
||||
struct organ_parameters {
|
||||
enum { FilterCount = 2, EnvCount = 3 };
|
||||
struct organ_filter_parameters
|
||||
{
|
||||
float cutoff;
|
||||
float resonance;
|
||||
float envmod[organ_parameters::EnvCount];
|
||||
float keyf;
|
||||
};
|
||||
|
||||
struct organ_env_parameters
|
||||
{
|
||||
float attack, decay, sustain, release, velscale, ampctl;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// these parameters are binary-copied from control ports (order is important!)
|
||||
|
||||
float drawbars[9];
|
||||
float harmonics[9];
|
||||
float waveforms[9];
|
||||
float detune[9];
|
||||
float phase[9];
|
||||
float pan[9];
|
||||
float routing[9];
|
||||
float foldover;
|
||||
float percussion_time;
|
||||
float percussion_level;
|
||||
float percussion_wave;
|
||||
float percussion_harmonic;
|
||||
float percussion_vel2amp;
|
||||
float percussion_fm_time;
|
||||
float percussion_fm_depth;
|
||||
float percussion_fm_wave;
|
||||
float percussion_fm_harmonic;
|
||||
float percussion_vel2fm;
|
||||
float percussion_trigger;
|
||||
float percussion_stereo;
|
||||
float filter_chain;
|
||||
float filter1_type;
|
||||
float master;
|
||||
|
||||
organ_filter_parameters filters[organ_parameters::FilterCount];
|
||||
organ_env_parameters envs[organ_parameters::EnvCount];
|
||||
float lfo_rate;
|
||||
float lfo_amt;
|
||||
float lfo_wet;
|
||||
float lfo_phase;
|
||||
float lfo_mode;
|
||||
|
||||
float global_transpose;
|
||||
float global_detune;
|
||||
|
||||
float polyphony;
|
||||
|
||||
float quad_env;
|
||||
|
||||
float pitch_bend_range;
|
||||
|
||||
float bass_freq;
|
||||
float bass_gain;
|
||||
float treble_freq;
|
||||
float treble_gain;
|
||||
|
||||
float dummy_mapcurve;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// these parameters are calculated
|
||||
|
||||
double perc_decay_const, perc_fm_decay_const;
|
||||
float multiplier[9];
|
||||
int phaseshift[9];
|
||||
float cutoff;
|
||||
unsigned int foldvalue;
|
||||
float pitch_bend;
|
||||
|
||||
float percussion_keytrack[ORGAN_KEYTRACK_POINTS][2];
|
||||
|
||||
organ_parameters() : pitch_bend(1.0f) {}
|
||||
|
||||
inline int get_percussion_wave() { return dsp::fastf2i_drm(percussion_wave); }
|
||||
inline int get_percussion_fm_wave() { return dsp::fastf2i_drm(percussion_fm_wave); }
|
||||
};
|
||||
|
||||
#define ORGAN_WAVE_BITS 12
|
||||
#define ORGAN_WAVE_SIZE 4096
|
||||
#define ORGAN_BIG_WAVE_BITS 17
|
||||
#define ORGAN_BIG_WAVE_SIZE 131072
|
||||
/// 2^ORGAN_BIG_WAVE_SHIFT = how many (quasi)periods per sample
|
||||
#define ORGAN_BIG_WAVE_SHIFT 5
|
||||
|
||||
class organ_voice_base: public calf_plugins::organ_enums
|
||||
{
|
||||
public:
|
||||
typedef waveform_family<ORGAN_WAVE_BITS> small_wave_family;
|
||||
typedef waveform_family<ORGAN_BIG_WAVE_BITS> big_wave_family;
|
||||
public:
|
||||
organ_parameters *parameters;
|
||||
protected:
|
||||
static small_wave_family (*waves)[wave_count_small];
|
||||
static big_wave_family (*big_waves)[wave_count_big];
|
||||
|
||||
// dsp::sine_table<float, ORGAN_WAVE_SIZE, 1> sine_wave;
|
||||
int note;
|
||||
dsp::decay amp;
|
||||
/// percussion FM carrier amplitude envelope
|
||||
dsp::decay pamp;
|
||||
/// percussion FM modulator amplitude envelope
|
||||
dsp::decay fm_amp;
|
||||
dsp::fixed_point<int64_t, 20> pphase, dpphase;
|
||||
dsp::fixed_point<int64_t, 20> modphase, moddphase;
|
||||
float fm_keytrack;
|
||||
int &sample_rate_ref;
|
||||
bool &released_ref;
|
||||
/// pamp per-sample (linear) step during release stage (calculated on release so that it will take 30ms for it to go from "current value at release point" to 0)
|
||||
float rel_age_const;
|
||||
|
||||
organ_voice_base(organ_parameters *_parameters, int &_sample_rate_ref, bool &_released_ref);
|
||||
|
||||
inline float wave(float *data, dsp::fixed_point<int, 20> ph) {
|
||||
return ph.lerp_table_lookup_float(data);
|
||||
}
|
||||
inline float big_wave(float *data, dsp::fixed_point<int64_t, 20> &ph) {
|
||||
// wrap to fit within the wave
|
||||
return ph.lerp_table_lookup_float_mask(data, ORGAN_BIG_WAVE_SIZE - 1);
|
||||
}
|
||||
public:
|
||||
static inline small_wave_family &get_wave(int wave) {
|
||||
return (*waves)[wave];
|
||||
}
|
||||
static inline big_wave_family &get_big_wave(int wave) {
|
||||
return (*big_waves)[wave];
|
||||
}
|
||||
static void precalculate_waves(calf_plugins::progress_report_iface *reporter);
|
||||
void update_pitch()
|
||||
{
|
||||
float phase = dsp::midi_note_to_phase(note, 100 * parameters->global_transpose + parameters->global_detune, sample_rate_ref);
|
||||
dpphase.set((long int) (phase * parameters->percussion_harmonic * parameters->pitch_bend));
|
||||
moddphase.set((long int) (phase * parameters->percussion_fm_harmonic * parameters->pitch_bend));
|
||||
}
|
||||
// this doesn't really have a voice interface
|
||||
void render_percussion_to(float (*buf)[2], int nsamples);
|
||||
void perc_note_on(int note, int vel);
|
||||
void perc_note_off(int note, int vel);
|
||||
void perc_reset()
|
||||
{
|
||||
pphase = 0;
|
||||
modphase = 0;
|
||||
dpphase = 0;
|
||||
moddphase = 0;
|
||||
note = -1;
|
||||
}
|
||||
};
|
||||
|
||||
class organ_vibrato
|
||||
{
|
||||
protected:
|
||||
enum { VibratoSize = 6 };
|
||||
float vibrato_x1[VibratoSize][2], vibrato_y1[VibratoSize][2];
|
||||
float lfo_phase;
|
||||
dsp::onepole<float> vibrato[2];
|
||||
public:
|
||||
void reset();
|
||||
void process(organ_parameters *parameters, float (*data)[2], unsigned int len, float sample_rate);
|
||||
};
|
||||
|
||||
class organ_voice: public dsp::voice, public organ_voice_base {
|
||||
protected:
|
||||
enum { Channels = 2, BlockSize = 64, EnvCount = organ_parameters::EnvCount, FilterCount = organ_parameters::FilterCount };
|
||||
union {
|
||||
float output_buffer[BlockSize][Channels];
|
||||
float aux_buffers[3][BlockSize][Channels];
|
||||
};
|
||||
dsp::fixed_point<int64_t, 52> phase, dphase;
|
||||
dsp::biquad_d1<float> filterL[2], filterR[2];
|
||||
adsr envs[EnvCount];
|
||||
dsp::inertia<dsp::linear_ramp> expression;
|
||||
organ_vibrato vibrato;
|
||||
float velocity;
|
||||
bool perc_released;
|
||||
/// The envelopes have ended and the voice is in final fadeout stage
|
||||
bool finishing;
|
||||
dsp::inertia<dsp::exponential_ramp> inertia_pitchbend;
|
||||
|
||||
public:
|
||||
organ_voice()
|
||||
: organ_voice_base(NULL, sample_rate, perc_released)
|
||||
, expression(dsp::linear_ramp(16))
|
||||
, inertia_pitchbend(dsp::exponential_ramp(1))
|
||||
{
|
||||
inertia_pitchbend.set_now(1);
|
||||
}
|
||||
|
||||
void reset() {
|
||||
inertia_pitchbend.ramp.set_length(sample_rate / (BlockSize * 30)); // 1/30s
|
||||
vibrato.reset();
|
||||
phase = 0;
|
||||
for (int i = 0; i < FilterCount; i++)
|
||||
{
|
||||
filterL[i].reset();
|
||||
filterR[i].reset();
|
||||
}
|
||||
}
|
||||
|
||||
void note_on(int note, int vel) {
|
||||
stolen = false;
|
||||
finishing = false;
|
||||
perc_released = false;
|
||||
released = false;
|
||||
reset();
|
||||
this->note = note;
|
||||
const float sf = 0.001f;
|
||||
for (int i = 0; i < EnvCount; i++)
|
||||
{
|
||||
organ_parameters::organ_env_parameters &p = parameters->envs[i];
|
||||
envs[i].set(sf * p.attack, sf * p.decay, p.sustain, sf * p.release, sample_rate / BlockSize);
|
||||
envs[i].note_on();
|
||||
}
|
||||
update_pitch();
|
||||
velocity = vel * 1.0 / 127.0;
|
||||
amp.set(1.0f);
|
||||
perc_note_on(note, vel);
|
||||
}
|
||||
|
||||
void note_off(int /* vel */) {
|
||||
// reset age to 0 (because decay will turn from exponential to linear, necessary because of error cumulation prevention)
|
||||
perc_released = true;
|
||||
if (pamp.get_active())
|
||||
{
|
||||
pamp.reinit();
|
||||
}
|
||||
rel_age_const = pamp.get() * ((1.0/44100.0)/0.03);
|
||||
for (int i = 0; i < EnvCount; i++)
|
||||
envs[i].note_off();
|
||||
}
|
||||
|
||||
virtual float get_priority() { return stolen ? 20000 : (perc_released ? 1 : (sostenuto ? 200 : 100)); }
|
||||
|
||||
virtual void steal() {
|
||||
perc_released = true;
|
||||
finishing = true;
|
||||
stolen = true;
|
||||
}
|
||||
|
||||
void render_block();
|
||||
|
||||
virtual int get_current_note() {
|
||||
return note;
|
||||
}
|
||||
virtual bool get_active() {
|
||||
// printf("note %d getactive %d use_percussion %d pamp active %d\n", note, amp.get_active(), use_percussion(), pamp.get_active());
|
||||
return (note != -1) && (amp.get_active() || (use_percussion() && pamp.get_active()));
|
||||
}
|
||||
void update_pitch();
|
||||
inline bool use_percussion()
|
||||
{
|
||||
return dsp::fastf2i_drm(parameters->percussion_trigger) == perctrig_polyphonic && parameters->percussion_level > 0;
|
||||
}
|
||||
};
|
||||
|
||||
/// Not a true voice, just something with similar-ish interface.
|
||||
class percussion_voice: public organ_voice_base {
|
||||
public:
|
||||
int sample_rate;
|
||||
bool released;
|
||||
|
||||
percussion_voice(organ_parameters *_parameters)
|
||||
: organ_voice_base(_parameters, sample_rate, released)
|
||||
, released(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool get_active() {
|
||||
return (note != -1) && pamp.get_active();
|
||||
}
|
||||
bool get_noticable() {
|
||||
return (note != -1) && (pamp.get() > 0.2 * parameters->percussion_level);
|
||||
}
|
||||
void setup(int sr) {
|
||||
sample_rate = sr;
|
||||
}
|
||||
};
|
||||
|
||||
struct drawbar_organ: public dsp::basic_synth, public calf_plugins::organ_enums {
|
||||
organ_parameters *parameters;
|
||||
percussion_voice percussion;
|
||||
organ_vibrato global_vibrato;
|
||||
two_band_eq eq_l, eq_r;
|
||||
|
||||
drawbar_organ(organ_parameters *_parameters)
|
||||
: parameters(_parameters)
|
||||
, percussion(_parameters) {
|
||||
}
|
||||
void render_separate(float *output[], int nsamples);
|
||||
dsp::voice *alloc_voice() {
|
||||
block_voice<organ_voice> *v = new block_voice<organ_voice>();
|
||||
v->parameters = parameters;
|
||||
return v;
|
||||
}
|
||||
virtual void percussion_note_on(int note, int vel) {
|
||||
percussion.perc_note_on(note, vel);
|
||||
}
|
||||
virtual void params_changed() = 0;
|
||||
virtual void setup(int sr) {
|
||||
basic_synth::setup(sr);
|
||||
percussion.setup(sr);
|
||||
parameters->cutoff = 0;
|
||||
params_changed();
|
||||
global_vibrato.reset();
|
||||
}
|
||||
void update_params();
|
||||
void control_change(int controller, int value)
|
||||
{
|
||||
#if 0
|
||||
if (controller == 11)
|
||||
{
|
||||
parameters->cutoff = value / 64.0 - 1;
|
||||
}
|
||||
#endif
|
||||
dsp::basic_synth::control_change(controller, value);
|
||||
}
|
||||
void pitch_bend(int amt);
|
||||
virtual bool check_percussion() {
|
||||
switch(dsp::fastf2i_drm(parameters->percussion_trigger))
|
||||
{
|
||||
case organ_voice_base::perctrig_first:
|
||||
return active_voices.empty();
|
||||
case organ_voice_base::perctrig_each:
|
||||
default:
|
||||
return true;
|
||||
case organ_voice_base::perctrig_eachplus:
|
||||
return !percussion.get_noticable();
|
||||
case organ_voice_base::perctrig_polyphonic:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
306
plugins/ladspa_effect/calf/calf/osc.h
Normal file
306
plugins/ladspa_effect/calf/calf/osc.h
Normal file
@@ -0,0 +1,306 @@
|
||||
/* Calf DSP Library
|
||||
* Oscillators
|
||||
*
|
||||
* Copyright (C) 2001-2007 Krzysztof Foltman
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __CALF_OSC_H
|
||||
#define __CALF_OSC_H
|
||||
|
||||
#include "fft.h"
|
||||
#include <map>
|
||||
|
||||
namespace dsp
|
||||
{
|
||||
|
||||
/** Very simple, non-bandlimited saw oscillator. Should not be used for anything
|
||||
* else than testing/prototyping. Unless get() function is replaced with something
|
||||
* with "proper" oscillator code, as the frequency setting function is fine.
|
||||
*/
|
||||
struct simple_oscillator
|
||||
{
|
||||
/// Phase (from 0 to 0xFFFFFFFF)
|
||||
uint32_t phase;
|
||||
/// Per-sample phase delta (phase increment), equal to 2^32*freq/sr.
|
||||
uint32_t phasedelta;
|
||||
/// Reset oscillator phase to zero.
|
||||
void reset()
|
||||
{
|
||||
phase = 0;
|
||||
}
|
||||
/// Set phase delta based on oscillator frequency and sample rate.
|
||||
void set_freq(float freq, float sr)
|
||||
{
|
||||
phasedelta = (int)(freq * 65536.0 * 256.0 * 16.0 / sr) << 4;
|
||||
}
|
||||
/// Set phase delta based on oscillator frequency and inverse of sample rate.
|
||||
void set_freq_odsr(float freq, double odsr)
|
||||
{
|
||||
phasedelta = (int)(freq * 65536.0 * 256.0 * 16.0 * odsr) << 4;
|
||||
}
|
||||
inline float get()
|
||||
{
|
||||
float value = (phase >> 16 ) / 65535.0 - 0.5;
|
||||
phase += phasedelta;
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* FFT-based bandlimiting helper class. Allows conversion between time and frequency domains and generating brickwall filtered
|
||||
* versions of a waveform given a pre-computed spectrum.
|
||||
* Waveform size must be a power of two, and template argument SIZE_BITS is log2 of waveform size.
|
||||
*/
|
||||
template<int SIZE_BITS>
|
||||
struct bandlimiter
|
||||
{
|
||||
enum { SIZE = 1 << SIZE_BITS };
|
||||
static dsp::fft<float, SIZE_BITS> &get_fft()
|
||||
{
|
||||
static dsp::fft<float, SIZE_BITS> fft;
|
||||
return fft;
|
||||
}
|
||||
|
||||
std::complex<float> spectrum[SIZE];
|
||||
|
||||
/// Import time domain waveform and calculate spectrum from it
|
||||
void compute_spectrum(float input[SIZE])
|
||||
{
|
||||
dsp::fft<float, SIZE_BITS> &fft = get_fft();
|
||||
std::complex<float> *data = new std::complex<float>[SIZE];
|
||||
for (int i = 0; i < SIZE; i++)
|
||||
data[i] = input[i];
|
||||
fft.calculate(data, spectrum, false);
|
||||
delete []data;
|
||||
}
|
||||
|
||||
/// Generate the waveform from the contained spectrum.
|
||||
void compute_waveform(float output[SIZE])
|
||||
{
|
||||
dsp::fft<float, SIZE_BITS> &fft = get_fft();
|
||||
std::complex<float> *data = new std::complex<float>[SIZE];
|
||||
fft.calculate(spectrum, data, true);
|
||||
for (int i = 0; i < SIZE; i++)
|
||||
output[i] = data[i].real();
|
||||
delete []data;
|
||||
}
|
||||
|
||||
/// remove DC offset of the spectrum (it usually does more harm than good!)
|
||||
void remove_dc()
|
||||
{
|
||||
spectrum[0] = 0.f;
|
||||
}
|
||||
|
||||
/// Very basic bandlimiting (brickwall filter)
|
||||
/// might need to be improved much in future!
|
||||
void make_waveform(float output[SIZE], int cutoff, bool foldover = false)
|
||||
{
|
||||
dsp::fft<float, SIZE_BITS> &fft = get_fft();
|
||||
std::vector<std::complex<float> > new_spec, iffted;
|
||||
new_spec.resize(SIZE);
|
||||
iffted.resize(SIZE);
|
||||
// Copy original harmonics up to cutoff point
|
||||
new_spec[0] = spectrum[0];
|
||||
for (int i = 1; i < cutoff; i++)
|
||||
new_spec[i] = spectrum[i],
|
||||
new_spec[SIZE - i] = spectrum[SIZE - i];
|
||||
// Fill the rest with zeros, optionally folding over harmonics over the
|
||||
// cutoff point into the lower octaves while halving the amplitude.
|
||||
// (I think it is almost nice for bell type waveforms when the original
|
||||
// waveform has few widely spread harmonics)
|
||||
if (foldover)
|
||||
{
|
||||
std::complex<float> fatt(0.5);
|
||||
cutoff /= 2;
|
||||
if (cutoff < 2)
|
||||
cutoff = 2;
|
||||
for (int i = SIZE / 2; i >= cutoff; i--)
|
||||
{
|
||||
new_spec[i / 2] += new_spec[i] * fatt;
|
||||
new_spec[SIZE - i / 2] += new_spec[SIZE - i] * fatt;
|
||||
new_spec[i] = 0.f,
|
||||
new_spec[SIZE - i] = 0.f;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cutoff < 1)
|
||||
cutoff = 1;
|
||||
for (int i = cutoff; i < SIZE / 2; i++)
|
||||
new_spec[i] = 0.f,
|
||||
new_spec[SIZE - i] = 0.f;
|
||||
}
|
||||
// convert back to time domain (IFFT) and extract only real part
|
||||
fft.calculate(new_spec.data(), iffted.data(), true);
|
||||
for (int i = 0; i < SIZE; i++)
|
||||
output[i] = iffted[i].real();
|
||||
}
|
||||
};
|
||||
|
||||
/// Set of bandlimited wavetables
|
||||
template<int SIZE_BITS>
|
||||
struct waveform_family: public std::map<uint32_t, float *>
|
||||
{
|
||||
enum { SIZE = 1 << SIZE_BITS };
|
||||
using std::map<uint32_t, float *>::iterator;
|
||||
using std::map<uint32_t, float *>::end;
|
||||
using std::map<uint32_t, float *>::lower_bound;
|
||||
float original[SIZE];
|
||||
|
||||
/// Fill the family using specified bandlimiter and original waveform. Optionally apply foldover.
|
||||
/// Does not produce harmonics over specified limit (limit = (SIZE / 2) / min_number_of_harmonics)
|
||||
void make(bandlimiter<SIZE_BITS> &bl, float input[SIZE], bool foldover = false, uint32_t limit = SIZE / 2)
|
||||
{
|
||||
memcpy(original, input, sizeof(original));
|
||||
bl.compute_spectrum(input);
|
||||
make_from_spectrum(bl, foldover);
|
||||
}
|
||||
|
||||
/// Fill the family using specified bandlimiter and spectrum contained within. Optionally apply foldover.
|
||||
/// Does not produce harmonics over specified limit (limit = (SIZE / 2) / min_number_of_harmonics)
|
||||
void make_from_spectrum(bandlimiter<SIZE_BITS> &bl, bool foldover = false, uint32_t limit = SIZE / 2)
|
||||
{
|
||||
bl.remove_dc();
|
||||
|
||||
uint32_t base = 1 << (32 - SIZE_BITS);
|
||||
uint32_t cutoff = SIZE / 2, top = SIZE / 2;
|
||||
float vmax = 0;
|
||||
for (unsigned int i = 0; i < cutoff; i++)
|
||||
vmax = std::max(vmax, abs(bl.spectrum[i]));
|
||||
float vthres = vmax / 1024.0; // -60dB
|
||||
float cumul = 0.f;
|
||||
while(cutoff > (SIZE / limit)) {
|
||||
if (!foldover)
|
||||
{
|
||||
// skip harmonics too quiet to be heard, but measure their loudness cumulatively,
|
||||
// because even if a single harmonic is too quiet, a whole bunch of them may add up
|
||||
// to considerable amount of space
|
||||
cumul = 0.f;
|
||||
while(cutoff > 1 && cumul + abs(bl.spectrum[cutoff - 1]) < vthres)
|
||||
{
|
||||
cumul += abs(bl.spectrum[cutoff - 1]);
|
||||
cutoff--;
|
||||
}
|
||||
}
|
||||
float *wf = new float[SIZE+1];
|
||||
bl.make_waveform(wf, cutoff, foldover);
|
||||
wf[SIZE] = wf[0];
|
||||
(*this)[base * (top / cutoff)] = wf;
|
||||
cutoff = (int)(0.75 * cutoff);
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve waveform pointer suitable for specified phase_delta
|
||||
inline float *get_level(uint32_t phase_delta)
|
||||
{
|
||||
iterator i = upper_bound(phase_delta);
|
||||
if (i == end())
|
||||
return NULL;
|
||||
// printf("Level = %08x\n", i->first);
|
||||
return i->second;
|
||||
}
|
||||
/// Destructor, deletes the waveforms and removes them from the map.
|
||||
~waveform_family()
|
||||
{
|
||||
for (iterator i = begin(); i != end(); i++)
|
||||
delete []i->second;
|
||||
clear();
|
||||
}
|
||||
};
|
||||
|
||||
#if 0
|
||||
// cubic interpolation
|
||||
static inline float cerp(float pm1, float p0, float p1, float p2, float t)
|
||||
{
|
||||
return (-t*(t-1)*(t-2) * pm1 + 3*(t+1)*(t-1)*(t-2) * p0 - 3*(t+1)*t*(t-2) * p1 + (t+1)*t*(t-1) * p2) * (1.0 / 6.0);
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* Simple table-based lerping oscillator. Uses waveform of size 2^SIZE_BITS.
|
||||
* Combine with waveform_family if bandlimited waveforms are needed. Because
|
||||
* of linear interpolation, it's usually a good idea to use large tables
|
||||
* (2048-4096 points), otherwise aliasing may be produced.
|
||||
*/
|
||||
template<int SIZE_BITS>
|
||||
struct waveform_oscillator: public simple_oscillator
|
||||
{
|
||||
enum { SIZE = 1 << SIZE_BITS, MASK = SIZE - 1, SCALE = 1 << (32 - SIZE_BITS) };
|
||||
float *waveform;
|
||||
inline float get()
|
||||
{
|
||||
uint32_t wpos = phase >> (32 - SIZE_BITS);
|
||||
float value = dsp::lerp(waveform[wpos], waveform[(wpos + 1) & MASK], (phase & (SCALE - 1)) * (1.0f / SCALE));
|
||||
phase += phasedelta;
|
||||
return value;
|
||||
}
|
||||
/// Add/substract two phase-shifted values
|
||||
inline float get_phaseshifted(uint32_t shift, float mix)
|
||||
{
|
||||
uint32_t wpos = phase >> (32 - SIZE_BITS);
|
||||
float value1 = dsp::lerp(waveform[wpos], waveform[(wpos + 1) & MASK], (phase & (SCALE - 1)) * (1.0f / SCALE));
|
||||
wpos = (phase + shift) >> (32 - SIZE_BITS);
|
||||
float value2 = dsp::lerp(waveform[wpos], waveform[(wpos + 1) & MASK], ((phase + shift) & (SCALE - 1)) * (1.0f / SCALE));
|
||||
float value = value1 + mix * value2;
|
||||
phase += phasedelta;
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Simple triangle LFO without any smoothing or anything of this sort.
|
||||
*/
|
||||
struct triangle_lfo: public simple_oscillator
|
||||
{
|
||||
inline float get()
|
||||
{
|
||||
uint32_t phase2 = phase;
|
||||
// start at 90 degrees point of the "/\" wave (-1 to +1)
|
||||
phase2 += 1<<30;
|
||||
// if in second half, invert the wave (so it falls back into 0..0x7FFFFFFF)
|
||||
phase2 ^= ((int32_t)phase2)>>31;
|
||||
|
||||
float value = (phase2 >> 6) / 16777216.0 - 1.0;
|
||||
phase += phasedelta;
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
/// Simple stupid inline function to normalize a waveform (by removing DC offset and ensuring max absolute value of 1).
|
||||
static inline void normalize_waveform(float *table, unsigned int size)
|
||||
{
|
||||
float dc = 0;
|
||||
for (unsigned int i = 0; i < size; i++)
|
||||
dc += table[i];
|
||||
dc /= size;
|
||||
for (unsigned int i = 0; i < size; i++)
|
||||
table[i] -= dc;
|
||||
float thismax = 0;
|
||||
for (unsigned int i = 0; i < size; i++)
|
||||
thismax = std::max(thismax, fabsf(table[i]));
|
||||
if (thismax < 0.000001f)
|
||||
return;
|
||||
double divv = 1.0 / thismax;
|
||||
for (unsigned int i = 0; i < size; i++)
|
||||
table[i] *= divv;
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
502
plugins/ladspa_effect/calf/calf/osctl.h
Normal file
502
plugins/ladspa_effect/calf/calf/osctl.h
Normal file
@@ -0,0 +1,502 @@
|
||||
/* Calf DSP Library
|
||||
* Open Sound Control primitives
|
||||
*
|
||||
* Copyright (C) 2007 Krzysztof Foltman
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __CALF_OSCTL_H
|
||||
#define __CALF_OSCTL_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <iostream>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace osctl
|
||||
{
|
||||
|
||||
enum osc_type
|
||||
{
|
||||
osc_i32 = 'i',
|
||||
osc_f32 = 'f',
|
||||
osc_string = 's',
|
||||
osc_blob = 'b',
|
||||
|
||||
// unsupported
|
||||
osc_i64 = 'h',
|
||||
osc_ts = 't',
|
||||
osc_f64 = 'd',
|
||||
osc_string_alt = 'S',
|
||||
osc_char = 'c',
|
||||
osc_rgba = 'r',
|
||||
osc_midi = 'm',
|
||||
osc_true = 'T',
|
||||
osc_false = 'F',
|
||||
osc_nil = 'N',
|
||||
osc_inf = 'I',
|
||||
osc_start_array = '[',
|
||||
osc_end_array = ']'
|
||||
};
|
||||
|
||||
extern const char *osc_type_name(osc_type type);
|
||||
|
||||
struct osc_exception: public std::exception
|
||||
{
|
||||
virtual const char *what() const throw() { return "OSC parsing error"; }
|
||||
};
|
||||
|
||||
struct osc_read_exception: public std::exception
|
||||
{
|
||||
virtual const char *what() const throw() { return "OSC buffer underflow"; }
|
||||
};
|
||||
|
||||
struct osc_write_exception: public std::exception
|
||||
{
|
||||
virtual const char *what() const throw() { return "OSC buffer overflow"; }
|
||||
};
|
||||
|
||||
struct null_buffer
|
||||
{
|
||||
static bool read(uint8_t *dest, uint32_t bytes)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
static bool write(uint8_t *dest, uint32_t bytes)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
static void clear()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct raw_buffer
|
||||
{
|
||||
uint8_t *ptr;
|
||||
uint32_t pos, count, size;
|
||||
|
||||
raw_buffer()
|
||||
{
|
||||
ptr = NULL;
|
||||
pos = count = size = 0;
|
||||
}
|
||||
raw_buffer(uint8_t *_ptr, uint32_t _count, uint32_t _size)
|
||||
{
|
||||
set(_ptr, _count, _size);
|
||||
}
|
||||
inline void set(uint8_t *_ptr, uint32_t _count, uint32_t _size)
|
||||
{
|
||||
ptr = _ptr;
|
||||
pos = 0;
|
||||
count = _count;
|
||||
size = _size;
|
||||
}
|
||||
bool read(uint8_t *dest, uint32_t bytes)
|
||||
{
|
||||
if (pos + bytes > count)
|
||||
return false;
|
||||
memcpy(dest, ptr + pos, bytes);
|
||||
pos += bytes;
|
||||
return true;
|
||||
}
|
||||
bool write(const uint8_t *src, uint32_t bytes)
|
||||
{
|
||||
if (count + bytes > size)
|
||||
return false;
|
||||
memcpy(ptr + count, src, bytes);
|
||||
count += bytes;
|
||||
return true;
|
||||
}
|
||||
int read_left()
|
||||
{
|
||||
return count - pos;
|
||||
}
|
||||
int write_left()
|
||||
{
|
||||
return size - count;
|
||||
}
|
||||
inline int write_misalignment()
|
||||
{
|
||||
return 4 - (count & 3);
|
||||
}
|
||||
void clear()
|
||||
{
|
||||
pos = 0;
|
||||
count = 0;
|
||||
}
|
||||
int tell()
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
void seek(int _pos)
|
||||
{
|
||||
pos = _pos;
|
||||
}
|
||||
};
|
||||
|
||||
struct string_buffer
|
||||
{
|
||||
std::string data;
|
||||
uint32_t pos, size;
|
||||
|
||||
string_buffer()
|
||||
{
|
||||
pos = 0;
|
||||
size = 1048576;
|
||||
}
|
||||
string_buffer(std::string _data, int _size = 1048576)
|
||||
{
|
||||
data = _data;
|
||||
pos = 0;
|
||||
size = _size;
|
||||
}
|
||||
bool read(uint8_t *dest, uint32_t bytes)
|
||||
{
|
||||
if (pos + bytes > data.length())
|
||||
return false;
|
||||
memcpy(dest, &data[pos], bytes);
|
||||
pos += bytes;
|
||||
return true;
|
||||
}
|
||||
bool write(const uint8_t *src, uint32_t bytes)
|
||||
{
|
||||
if (data.length() + bytes > size)
|
||||
return false;
|
||||
uint32_t wpos = data.length();
|
||||
data.resize(wpos + bytes);
|
||||
memcpy(&data[wpos], src, bytes);
|
||||
return true;
|
||||
}
|
||||
inline int read_left()
|
||||
{
|
||||
return data.length() - pos;
|
||||
}
|
||||
inline int write_left()
|
||||
{
|
||||
return size - data.length();
|
||||
}
|
||||
inline int write_misalignment()
|
||||
{
|
||||
return 4 - (data.length() & 3);
|
||||
}
|
||||
void clear()
|
||||
{
|
||||
data.clear();
|
||||
pos = 0;
|
||||
}
|
||||
int tell()
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
void seek(int _pos)
|
||||
{
|
||||
pos = _pos;
|
||||
}
|
||||
};
|
||||
|
||||
template<class Buffer, class TypeBuffer = null_buffer, bool Throw = true>
|
||||
struct osc_stream
|
||||
{
|
||||
Buffer &buffer;
|
||||
TypeBuffer *type_buffer;
|
||||
bool error;
|
||||
|
||||
osc_stream(Buffer &_buffer) : buffer(_buffer), type_buffer(NULL), error(false) {}
|
||||
osc_stream(Buffer &_buffer, TypeBuffer &_type_buffer) : buffer(_buffer), type_buffer(&_type_buffer), error(false) {}
|
||||
inline void pad()
|
||||
{
|
||||
uint32_t zero = 0;
|
||||
write(&zero, buffer.write_misalignment());
|
||||
}
|
||||
inline void read(void *dest, uint32_t bytes)
|
||||
{
|
||||
if (!buffer.read((uint8_t *)dest, bytes))
|
||||
{
|
||||
#if 0
|
||||
if (Throw)
|
||||
throw osc_read_exception();
|
||||
else
|
||||
#endif
|
||||
{
|
||||
error = true;
|
||||
memset(dest, 0, bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void write(const void *src, uint32_t bytes)
|
||||
{
|
||||
if (!buffer.write((const uint8_t *)src, bytes))
|
||||
{
|
||||
#if 0
|
||||
if (Throw)
|
||||
throw osc_write_exception();
|
||||
else
|
||||
#endif
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
inline void clear()
|
||||
{
|
||||
buffer.clear();
|
||||
if (type_buffer)
|
||||
type_buffer->clear();
|
||||
}
|
||||
inline void write_type(char ch)
|
||||
{
|
||||
if (type_buffer)
|
||||
type_buffer->write((uint8_t *)&ch, 1);
|
||||
}
|
||||
};
|
||||
|
||||
typedef osc_stream<string_buffer> osc_strstream;
|
||||
typedef osc_stream<string_buffer, string_buffer> osc_typed_strstream;
|
||||
|
||||
struct osc_inline_strstream: public string_buffer, public osc_strstream
|
||||
{
|
||||
osc_inline_strstream()
|
||||
: string_buffer(), osc_strstream(static_cast<string_buffer &>(*this))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct osc_str_typed_buffer_pair
|
||||
{
|
||||
string_buffer buf_data, buf_types;
|
||||
};
|
||||
|
||||
struct osc_inline_typed_strstream: public osc_str_typed_buffer_pair, public osc_typed_strstream
|
||||
{
|
||||
osc_inline_typed_strstream()
|
||||
: osc_str_typed_buffer_pair(), osc_typed_strstream(buf_data, buf_types)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template<class Buffer, class TypeBuffer>
|
||||
inline osc_stream<Buffer, TypeBuffer> &
|
||||
operator <<(osc_stream<Buffer, TypeBuffer> &s, uint32_t val)
|
||||
{
|
||||
return s;
|
||||
}
|
||||
|
||||
template<class Buffer, class TypeBuffer>
|
||||
inline osc_stream<Buffer, TypeBuffer> &
|
||||
operator >>(osc_stream<Buffer, TypeBuffer> &s, uint32_t &val)
|
||||
{
|
||||
return s;
|
||||
}
|
||||
|
||||
template<class Buffer, class TypeBuffer>
|
||||
inline osc_stream<Buffer, TypeBuffer> &
|
||||
operator >>(osc_stream<Buffer, TypeBuffer> &s, int32_t &val)
|
||||
{
|
||||
return s;
|
||||
}
|
||||
|
||||
template<class Buffer, class TypeBuffer>
|
||||
inline osc_stream<Buffer, TypeBuffer> &
|
||||
operator <<(osc_stream<Buffer, TypeBuffer> &s, float val)
|
||||
{
|
||||
return s;
|
||||
}
|
||||
|
||||
template<class Buffer, class TypeBuffer>
|
||||
inline osc_stream<Buffer, TypeBuffer> &
|
||||
operator >>(osc_stream<Buffer, TypeBuffer> &s, float &val)
|
||||
{
|
||||
return s;
|
||||
}
|
||||
|
||||
template<class Buffer, class TypeBuffer>
|
||||
inline osc_stream<Buffer, TypeBuffer> &
|
||||
operator <<(osc_stream<Buffer, TypeBuffer> &s, const std::string &str)
|
||||
{
|
||||
s.write(&str[0], str.length());
|
||||
s.pad();
|
||||
s.write_type(osc_string);
|
||||
return s;
|
||||
}
|
||||
|
||||
template<class Buffer, class TypeBuffer>
|
||||
inline osc_stream<Buffer, TypeBuffer> &
|
||||
operator >>(osc_stream<Buffer, TypeBuffer> &s, std::string &str)
|
||||
{
|
||||
// inefficient...
|
||||
char five[5];
|
||||
five[4] = '\0';
|
||||
str.resize(0);
|
||||
while(1)
|
||||
{
|
||||
s.read(five, 4);
|
||||
if (five[0] == '\0')
|
||||
break;
|
||||
str += five;
|
||||
if (!five[1] || !five[2] || !five[3])
|
||||
break;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
template<class Buffer, class TypeBuffer, class DestBuffer>
|
||||
inline osc_stream<Buffer, TypeBuffer> &
|
||||
read_buffer_from_osc_stream(osc_stream<Buffer, TypeBuffer> &s, DestBuffer &buf)
|
||||
{
|
||||
return s;
|
||||
}
|
||||
|
||||
template<class Buffer, class TypeBuffer, class SrcBuffer>
|
||||
inline osc_stream<Buffer, TypeBuffer> &
|
||||
write_buffer_to_osc_stream(osc_stream<Buffer, TypeBuffer> &s, SrcBuffer &buf)
|
||||
{
|
||||
return s;
|
||||
}
|
||||
|
||||
template<class Buffer, class TypeBuffer>
|
||||
inline osc_stream<Buffer, TypeBuffer> &
|
||||
operator >>(osc_stream<Buffer, TypeBuffer> &s, raw_buffer &str)
|
||||
{
|
||||
return read_buffer_from_osc_stream(s, str);
|
||||
}
|
||||
|
||||
template<class Buffer, class TypeBuffer>
|
||||
inline osc_stream<Buffer, TypeBuffer> &
|
||||
operator >>(osc_stream<Buffer, TypeBuffer> &s, string_buffer &str)
|
||||
{
|
||||
return read_buffer_from_osc_stream(s, str);
|
||||
}
|
||||
|
||||
template<class Buffer, class TypeBuffer>
|
||||
inline osc_stream<Buffer, TypeBuffer> &
|
||||
operator <<(osc_stream<Buffer, TypeBuffer> &s, raw_buffer &str)
|
||||
{
|
||||
return write_buffer_to_osc_stream(s, str);
|
||||
}
|
||||
|
||||
template<class Buffer, class TypeBuffer>
|
||||
inline osc_stream<Buffer, TypeBuffer> &
|
||||
operator <<(osc_stream<Buffer, TypeBuffer> &s, string_buffer &str)
|
||||
{
|
||||
return write_buffer_to_osc_stream(s, str);
|
||||
}
|
||||
|
||||
// XXXKF: I don't support reading binary blobs yet
|
||||
#if 0
|
||||
struct osc_net_bad_address: public std::exception
|
||||
{
|
||||
std::string addr, error_msg;
|
||||
osc_net_bad_address(const char *_addr)
|
||||
{
|
||||
addr = _addr;
|
||||
error_msg = "Incorrect OSC URI: " + addr;
|
||||
}
|
||||
virtual const char *what() const throw() { return error_msg.c_str(); }
|
||||
virtual ~osc_net_bad_address() throw () {}
|
||||
};
|
||||
|
||||
struct osc_net_exception: public std::exception
|
||||
{
|
||||
int net_errno;
|
||||
std::string command, error_msg;
|
||||
osc_net_exception(const char *cmd, int _errno = errno)
|
||||
{
|
||||
command = cmd;
|
||||
net_errno = _errno;
|
||||
error_msg = "OSC error in "+command+": "+strerror(_errno);
|
||||
}
|
||||
virtual const char *what() const throw() { return error_msg.c_str(); }
|
||||
virtual ~osc_net_exception() throw () {}
|
||||
};
|
||||
|
||||
struct osc_net_dns_exception: public std::exception
|
||||
{
|
||||
int net_errno;
|
||||
std::string command, error_msg;
|
||||
virtual const char *what() const throw() { return error_msg.c_str(); }
|
||||
virtual ~osc_net_dns_exception() throw () {}
|
||||
};
|
||||
#endif
|
||||
|
||||
template<class OscStream>
|
||||
struct osc_message_sink
|
||||
{
|
||||
virtual void receive_osc_message(std::string address, std::string type_tag, OscStream &buffer)=0;
|
||||
virtual ~osc_message_sink() {}
|
||||
};
|
||||
|
||||
template<class OscStream, class DumpStream>
|
||||
struct osc_message_dump: public osc_message_sink<OscStream>
|
||||
{
|
||||
DumpStream &stream;
|
||||
osc_message_dump(DumpStream &_stream) : stream(_stream) {}
|
||||
|
||||
virtual void receive_osc_message(std::string address, std::string type_tag, OscStream &buffer)
|
||||
{
|
||||
int pos = buffer.buffer.tell();
|
||||
stream << "address: " << address << ", type tag: " << type_tag << std::endl;
|
||||
for (unsigned int i = 0; i < type_tag.size(); i++)
|
||||
{
|
||||
stream << "Argument " << i << " is ";
|
||||
switch(type_tag[i])
|
||||
{
|
||||
case 'i':
|
||||
{
|
||||
uint32_t val;
|
||||
buffer >> val;
|
||||
stream << val;
|
||||
break;
|
||||
}
|
||||
case 'f':
|
||||
{
|
||||
float val;
|
||||
buffer >> val;
|
||||
stream << val;
|
||||
break;
|
||||
}
|
||||
case 's':
|
||||
{
|
||||
std::string val;
|
||||
buffer >> val;
|
||||
stream << val;
|
||||
break;
|
||||
}
|
||||
case 'b':
|
||||
{
|
||||
osctl::string_buffer val;
|
||||
buffer >> val;
|
||||
stream << "blob (" << val.data.length() << " bytes)";
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
stream << "unknown - cannot parse more arguments" << std::endl;
|
||||
i = type_tag.size();
|
||||
break;
|
||||
}
|
||||
}
|
||||
stream << std::endl;
|
||||
}
|
||||
stream << std::flush;
|
||||
buffer.buffer.seek(pos);
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
105
plugins/ladspa_effect/calf/calf/plugininfo.h
Normal file
105
plugins/ladspa_effect/calf/calf/plugininfo.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/* Calf DSP Library
|
||||
* Plugin introspection interface
|
||||
*
|
||||
* Copyright (C) 2008 Krzysztof Foltman
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __CALF_PLUGININFO_H
|
||||
#define __CALF_PLUGININFO_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace calf_plugins {
|
||||
|
||||
/// A sink to send information about an audio port
|
||||
struct plain_port_info_iface
|
||||
{
|
||||
/// Called if it's an input port
|
||||
virtual plain_port_info_iface& input() { return *this; }
|
||||
/// Called if it's an output port
|
||||
virtual plain_port_info_iface& output() { return *this; }
|
||||
virtual plain_port_info_iface& lv2_ttl(const std::string &text) { return *this; }
|
||||
virtual ~plain_port_info_iface() {}
|
||||
};
|
||||
|
||||
/// A sink to send information about a control port (very incomplete, missing stuff: units, integer, boolean, toggled, notAutomatic, notGUI...)
|
||||
struct control_port_info_iface
|
||||
{
|
||||
/// Called if it's an input port
|
||||
virtual control_port_info_iface& input() { return *this; }
|
||||
/// Called if it's an output port
|
||||
virtual control_port_info_iface& output() { return *this; }
|
||||
/// Called to mark the port as using linear range [from, to]
|
||||
virtual control_port_info_iface& lin_range(double from, double to) { return *this; }
|
||||
/// Called to mark the port as using log range [from, to]
|
||||
virtual control_port_info_iface& log_range(double from, double to) { return *this; }
|
||||
virtual control_port_info_iface& toggle() { return *this; }
|
||||
virtual control_port_info_iface& trigger() { return *this; }
|
||||
virtual control_port_info_iface& integer() { return *this; }
|
||||
virtual control_port_info_iface& lv2_ttl(const std::string &text) { return *this; }
|
||||
virtual control_port_info_iface& polymorphic() { return lv2_ttl("a poly:PolymorphicPort ;"); }
|
||||
virtual control_port_info_iface& poly_audio() { return lv2_ttl("poly:supportsType lv2:AudioPort ;"); }
|
||||
virtual ~control_port_info_iface() {}
|
||||
};
|
||||
|
||||
/// A sink to send information about a plugin
|
||||
struct plugin_info_iface
|
||||
{
|
||||
/// Set plugin names (ID, name and label)
|
||||
virtual void names(const std::string &name, const std::string &label, const std::string &category, const std::string µname = std::string()) {}
|
||||
/// Add an audio port (returns a sink which accepts further description)
|
||||
virtual plain_port_info_iface &audio_port(const std::string &id, const std::string &name, const std::string µname = std::string("N/A"))=0;
|
||||
/// Add an event port (returns a sink which accepts further description)
|
||||
virtual plain_port_info_iface &event_port(const std::string &id, const std::string &name, const std::string µname = std::string("N/A"))=0;
|
||||
/// Add a control port (returns a sink which accepts further description)
|
||||
virtual control_port_info_iface &control_port(const std::string &id, const std::string &name, double def_value, const std::string µname = "N/A")=0;
|
||||
/// Add arbitrary TTL clauses
|
||||
virtual void lv2_ttl(const std::string &text) {}
|
||||
/// Add small plugin GUI
|
||||
virtual void has_gui() { lv2_ttl("uiext:ui <http://calf.sourceforge.net/small_plugins/gui/gtk2-gui> ;"); }
|
||||
/// Called after plugin has reported all the information
|
||||
virtual void finalize() {}
|
||||
virtual ~plugin_info_iface() {}
|
||||
};
|
||||
|
||||
struct plugin_port_type_grabber: public plugin_info_iface, public control_port_info_iface
|
||||
{
|
||||
uint32_t ⌖
|
||||
uint32_t index;
|
||||
|
||||
plain_port_info_iface pp;
|
||||
control_port_info_iface cp;
|
||||
|
||||
plugin_port_type_grabber(uint32_t &_target) : target(_target), index(0) { target = 0; }
|
||||
|
||||
virtual plain_port_info_iface &audio_port(const std::string &id, const std::string &name, const std::string µname = std::string("N/A")) { target |= (1 << index); index++; return pp; }
|
||||
virtual plain_port_info_iface &event_port(const std::string &id, const std::string &name, const std::string µname = std::string("N/A")) { index++; return pp; }
|
||||
virtual control_port_info_iface &control_port(const std::string &id, const std::string &name, double def_value, const std::string µname = "N/A") { index++; return cp; }
|
||||
};
|
||||
|
||||
/// A sink to send information about plugins
|
||||
struct plugin_list_info_iface
|
||||
{
|
||||
/// Add an empty plugin object and return the sink to be filled with information
|
||||
virtual plugin_info_iface &plugin(const std::string &id) = 0;
|
||||
virtual ~plugin_list_info_iface() {}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
142
plugins/ladspa_effect/calf/calf/preset.h
Normal file
142
plugins/ladspa_effect/calf/calf/preset.h
Normal file
@@ -0,0 +1,142 @@
|
||||
/* Calf DSP Library
|
||||
* Preset management
|
||||
*
|
||||
* Copyright (C) 2007 Krzysztof Foltman
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __CALF_PRESET_H
|
||||
#define __CALF_PRESET_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <ostream>
|
||||
#include <string.h>
|
||||
#include "utils.h"
|
||||
|
||||
namespace calf_plugins {
|
||||
|
||||
class plugin_ctl_iface;
|
||||
|
||||
/// Contents of single preset
|
||||
struct plugin_preset
|
||||
{
|
||||
/// Bank the preset belongs to (not used yet)
|
||||
int bank;
|
||||
/// Program number of the preset (not used yet)
|
||||
int program;
|
||||
/// Name of the preset
|
||||
std::string name;
|
||||
/// Name of the plugin the preset is for
|
||||
std::string plugin;
|
||||
/// Names of parameters in values array (for each item in param_names there should be a counterpart in values)
|
||||
std::vector<std::string> param_names;
|
||||
/// Values of parameters
|
||||
std::vector<float> values;
|
||||
/// DSSI configure-style variables
|
||||
std::map<std::string, std::string> variables;
|
||||
|
||||
plugin_preset() : bank(0), program(0) {}
|
||||
/// Export preset as XML
|
||||
std::string to_xml();
|
||||
/// "Upload" preset content to the plugin
|
||||
void activate(plugin_ctl_iface *plugin);
|
||||
/// "Download" preset content from the plugin
|
||||
void get_from(plugin_ctl_iface *plugin);
|
||||
|
||||
std::string get_safe_name();
|
||||
};
|
||||
|
||||
/// Exception thrown by preset system
|
||||
struct preset_exception
|
||||
{
|
||||
std::string message, param, fulltext;
|
||||
int error;
|
||||
preset_exception(const std::string &_message, const std::string &_param, int _error)
|
||||
: message(_message), param(_param), error(_error)
|
||||
{
|
||||
}
|
||||
const char *what() {
|
||||
if (error)
|
||||
fulltext = message + " " + param + " (" + strerror(error) + ")";
|
||||
else
|
||||
fulltext = message + " " + param;
|
||||
return fulltext.c_str();
|
||||
}
|
||||
~preset_exception()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/// A vector of presets
|
||||
typedef std::vector<plugin_preset> preset_vector;
|
||||
|
||||
/// A single list of presets (usually there are two - @see get_builtin_presets(), get_user_presets() )
|
||||
struct preset_list
|
||||
{
|
||||
/// Parser states
|
||||
enum parser_state
|
||||
{
|
||||
START, ///< Beginning of parsing process (before root element)
|
||||
LIST, ///< Inside root element
|
||||
PRESET, ///< Inside preset definition
|
||||
VALUE, ///< Inside (empty) param tag
|
||||
VAR, ///< Inside (non-empty) var tag
|
||||
} state;
|
||||
|
||||
/// Contained presets (usually for all plugins)
|
||||
preset_vector presets;
|
||||
/// Temporary preset used during parsing process
|
||||
plugin_preset parser_preset;
|
||||
/// Preset number counters for DSSI (currently broken)
|
||||
std::map<std::string, int> last_preset_ids;
|
||||
/// The key used in current <var name="key"> tag (for state == VAR)
|
||||
std::string current_key;
|
||||
|
||||
/// Return the name of the built-in or user-defined preset file
|
||||
static std::string get_preset_filename(bool builtin);
|
||||
/// Load default preset list (built-in or user-defined)
|
||||
bool load_defaults(bool builtin);
|
||||
void parse(const std::string &data);
|
||||
/// Load preset list from XML file
|
||||
void load(const char *filename);
|
||||
/// Save preset list as XML file
|
||||
void save(const char *filename);
|
||||
/// Append or replace a preset (replaces a preset with the same plugin and preset name)
|
||||
void add(const plugin_preset &sp);
|
||||
/// Get a sublist of presets for a given plugin (those with plugin_preset::plugin == plugin)
|
||||
void get_for_plugin(preset_vector &vec, const char *plugin);
|
||||
|
||||
protected:
|
||||
/// Internal function: start element handler for expat
|
||||
static void xml_start_element_handler(void *user_data, const char *name, const char *attrs[]);
|
||||
/// Internal function: end element handler for expat
|
||||
static void xml_end_element_handler(void *user_data, const char *name);
|
||||
/// Internal function: character data (tag text content) handler for expat
|
||||
static void xml_character_data_handler(void *user_data, const char *data, int len);
|
||||
};
|
||||
|
||||
/// Return the current list of built-in (factory) presets (these are loaded from system-wide file)
|
||||
extern preset_list &get_builtin_presets();
|
||||
|
||||
/// Return the current list of user-defined presets (these are loaded from ~/.calfpresets)
|
||||
extern preset_list &get_user_presets();
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
516
plugins/ladspa_effect/calf/calf/primitives.h
Normal file
516
plugins/ladspa_effect/calf/calf/primitives.h
Normal file
@@ -0,0 +1,516 @@
|
||||
/* Calf DSP Library
|
||||
* DSP primitives.
|
||||
*
|
||||
* Copyright (C) 2001-2007 Krzysztof Foltman
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __CALF_PRIMITIVES_H
|
||||
#define __CALF_PRIMITIVES_H
|
||||
|
||||
#include <stack>
|
||||
#include <map>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
namespace dsp {
|
||||
|
||||
/// Set a float to zero
|
||||
inline void zero(float &v) {
|
||||
v = 0;
|
||||
};
|
||||
|
||||
/// Set a double to zero
|
||||
inline void zero(double &v) {
|
||||
v = 0;
|
||||
};
|
||||
|
||||
/// Set 64-bit unsigned integer value to zero
|
||||
inline void zero(uint64_t &v) { v = 0; };
|
||||
/// Set 32-bit unsigned integer value to zero
|
||||
inline void zero(uint32_t &v) { v = 0; };
|
||||
/// Set 16-bit unsigned integer value to zero
|
||||
inline void zero(uint16_t &v) { v = 0; };
|
||||
/// Set 8-bit unsigned integer value to zero
|
||||
inline void zero(uint8_t &v) { v = 0; };
|
||||
/// Set 64-bit signed integer value to zero
|
||||
inline void zero(int64_t &v) { v = 0; };
|
||||
/// Set 32-bit signed integer value to zero
|
||||
inline void zero(int32_t &v) { v = 0; };
|
||||
/// Set 16-bit signed integer value to zero
|
||||
inline void zero(int16_t &v) { v = 0; };
|
||||
/// Set 8-bit signed integer value to zero
|
||||
inline void zero(int8_t &v) { v = 0; };
|
||||
|
||||
/// Set array (buffer or anything similar) to vector of zeroes
|
||||
template<class T>
|
||||
void zero(T *data, unsigned int size) {
|
||||
T value;
|
||||
dsp::zero(value);
|
||||
for (unsigned int i=0; i<size; i++)
|
||||
*data++ = value;
|
||||
}
|
||||
|
||||
template<class T = float>struct stereo_sample {
|
||||
T left;
|
||||
T right;
|
||||
/// default constructor - preserves T's semantics (ie. no implicit initialization to 0)
|
||||
inline stereo_sample() {
|
||||
}
|
||||
inline stereo_sample(T _left, T _right) {
|
||||
left = _left;
|
||||
right = _right;
|
||||
}
|
||||
inline stereo_sample(T _both) {
|
||||
left = right = _both;
|
||||
}
|
||||
template<typename U>
|
||||
inline stereo_sample(const stereo_sample<U> &value) {
|
||||
left = value.left;
|
||||
right = value.right;
|
||||
}
|
||||
inline stereo_sample& operator=(const T &value) {
|
||||
left = right = value;
|
||||
return *this;
|
||||
}
|
||||
template<typename U>
|
||||
inline stereo_sample& operator=(const stereo_sample<U> &value) {
|
||||
left = value.left;
|
||||
right = value.right;
|
||||
return *this;
|
||||
}
|
||||
/*
|
||||
inline operator T() const {
|
||||
return (left+right)/2;
|
||||
}
|
||||
*/
|
||||
inline stereo_sample& operator*=(const T &multiplier) {
|
||||
left *= multiplier;
|
||||
right *= multiplier;
|
||||
return *this;
|
||||
}
|
||||
inline stereo_sample& operator+=(const stereo_sample<T> &value) {
|
||||
left += value.left;
|
||||
right += value.right;
|
||||
return *this;
|
||||
}
|
||||
inline stereo_sample& operator-=(const stereo_sample<T> &value) {
|
||||
left -= value.left;
|
||||
right -= value.right;
|
||||
return *this;
|
||||
}
|
||||
template<typename U> inline stereo_sample<U> operator*(const U &value) const {
|
||||
return stereo_sample<U>(left*value, right*value);
|
||||
}
|
||||
/*inline stereo_sample<float> operator*(float value) const {
|
||||
return stereo_sample<float>(left*value, right*value);
|
||||
}
|
||||
inline stereo_sample<double> operator*(double value) const {
|
||||
return stereo_sample<double>(left*value, right*value);
|
||||
}*/
|
||||
inline stereo_sample<T> operator+(const stereo_sample<T> &value) {
|
||||
return stereo_sample(left+value.left, right+value.right);
|
||||
}
|
||||
inline stereo_sample<T> operator-(const stereo_sample<T> &value) {
|
||||
return stereo_sample(left-value.left, right-value.right);
|
||||
}
|
||||
inline stereo_sample<T> operator+(const T &value) {
|
||||
return stereo_sample(left+value, right+value);
|
||||
}
|
||||
inline stereo_sample<T> operator-(const T &value) {
|
||||
return stereo_sample(left-value, right-value);
|
||||
}
|
||||
inline stereo_sample<float> operator+(float value) {
|
||||
return stereo_sample<float>(left+value, right+value);
|
||||
}
|
||||
inline stereo_sample<float> operator-(float value) {
|
||||
return stereo_sample<float>(left-value, right-value);
|
||||
}
|
||||
inline stereo_sample<double> operator+(double value) {
|
||||
return stereo_sample<double>(left+value, right+value);
|
||||
}
|
||||
inline stereo_sample<double> operator-(double value) {
|
||||
return stereo_sample<double>(left-value, right-value);
|
||||
}
|
||||
};
|
||||
|
||||
/// Multiply constant by stereo_value
|
||||
template<class T>
|
||||
inline stereo_sample<T> operator*(const T &value, const stereo_sample<T> &value2) {
|
||||
return stereo_sample<T>(value2.left*value, value2.right*value);
|
||||
}
|
||||
|
||||
/// Add constant to stereo_value
|
||||
template<class T>
|
||||
inline stereo_sample<T> operator+(const T &value, const stereo_sample<T> &value2) {
|
||||
return stereo_sample<T>(value2.left+value, value2.right+value);
|
||||
}
|
||||
|
||||
/// Subtract stereo_value from constant (yields stereo_value of course)
|
||||
template<class T>
|
||||
inline stereo_sample<T> operator-(const T &value, const stereo_sample<T> &value2) {
|
||||
return stereo_sample<T>(value-value2.left, value-value2.right);
|
||||
}
|
||||
|
||||
/// Shift value right by 'bits' bits (multiply by 2^-bits)
|
||||
template<typename T>
|
||||
inline stereo_sample<T> shr(stereo_sample<T> v, int bits = 1) {
|
||||
v.left = shr(v.left, bits);
|
||||
v.right = shr(v.right, bits);
|
||||
return v;
|
||||
}
|
||||
|
||||
/// Set a stereo_sample<T> value to zero
|
||||
template<typename T>
|
||||
inline void zero(stereo_sample<T> &v) {
|
||||
dsp::zero(v.left);
|
||||
dsp::zero(v.right);
|
||||
}
|
||||
|
||||
/// 'Small value' for integer and other types
|
||||
template<typename T>
|
||||
inline T small_value() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// 'Small value' for floats (2^-24) - used for primitive underrun prevention. The value is pretty much arbitrary (allowing for 24-bit signals normalized to 1.0).
|
||||
template<>
|
||||
inline float small_value<float>() {
|
||||
return (1.0/16777216.0); // allows for 2^-24, should be enough for 24-bit DACs at least :)
|
||||
}
|
||||
|
||||
/// 'Small value' for doubles (2^-24) - used for primitive underrun prevention. The value is pretty much arbitrary.
|
||||
template<>
|
||||
inline double small_value<double>() {
|
||||
return (1.0/16777216.0);
|
||||
}
|
||||
|
||||
/// Convert a single value to single value = do nothing :) (but it's a generic with specialisation for stereo_sample)
|
||||
template<typename T>
|
||||
inline float mono(T v) {
|
||||
return v;
|
||||
}
|
||||
|
||||
/// Convert a stereo_sample to single value by averaging two channels
|
||||
template<typename T>
|
||||
inline T mono(stereo_sample<T> v) {
|
||||
return shr(v.left+v.right);
|
||||
}
|
||||
|
||||
/// Clip a value to [min, max]
|
||||
template<typename T>
|
||||
inline T clip(T value, T min, T max) {
|
||||
if (value < min) return min;
|
||||
if (value > max) return max;
|
||||
return value;
|
||||
}
|
||||
|
||||
/// Clip a double to [-1.0, +1.0]
|
||||
inline double clip11(double value) {
|
||||
double a = fabs(value);
|
||||
if (a<=1) return value;
|
||||
return (a<0) ? -1.0 : 1.0;
|
||||
}
|
||||
|
||||
/// Clip a float to [-1.0f, +1.0f]
|
||||
inline float clip11(float value) {
|
||||
float a = fabsf(value);
|
||||
if (a<=1) return value;
|
||||
return (a<0) ? -1.0f : 1.0f;
|
||||
}
|
||||
|
||||
/// Clip a double to [0.0, +1.0]
|
||||
inline double clip01(double value) {
|
||||
double a = fabs(value-0.5);
|
||||
if (a<=0.5) return value;
|
||||
return (a<0) ? -0.0 : 1.0;
|
||||
}
|
||||
|
||||
/// Clip a float to [0.0f, +1.0f]
|
||||
inline float clip01(float value) {
|
||||
float a = fabsf(value-0.5f);
|
||||
if (a<=0.5f) return value;
|
||||
return (a<0) ? -0.0f : 1.0f;
|
||||
}
|
||||
|
||||
// Linear interpolation (mix-way between v1 and v2).
|
||||
template<typename T, typename U>
|
||||
inline T lerp(T v1, T v2, U mix) {
|
||||
return v1+(v2-v1)*mix;
|
||||
}
|
||||
|
||||
// Linear interpolation for stereo values (mix-way between v1 and v2).
|
||||
template<typename T>
|
||||
inline stereo_sample<T> lerp(stereo_sample<T> &v1, stereo_sample<T> &v2, float mix) {
|
||||
return stereo_sample<T>(v1.left+(v2.left-v1.left)*mix, v1.right+(v2.right-v1.right)*mix);
|
||||
}
|
||||
|
||||
/**
|
||||
* decay-only envelope (linear or exponential); deactivates itself when it goes below a set point (epsilon)
|
||||
*/
|
||||
class decay
|
||||
{
|
||||
double value, initial;
|
||||
unsigned int age, mask;
|
||||
bool active;
|
||||
public:
|
||||
decay() {
|
||||
active = false;
|
||||
mask = 127;
|
||||
initial = value = 0.0;
|
||||
}
|
||||
inline bool get_active() {
|
||||
return active;
|
||||
}
|
||||
inline double get() {
|
||||
return active ? value : 0.0;
|
||||
}
|
||||
inline void set(double v) {
|
||||
initial = value = v;
|
||||
active = true;
|
||||
age = 0;
|
||||
}
|
||||
/// reinitialise envelope (must be called if shape changes from linear to exponential or vice versa in the middle of envelope)
|
||||
inline void reinit()
|
||||
{
|
||||
initial = value;
|
||||
age = 1;
|
||||
}
|
||||
inline void add(double v) {
|
||||
if (active)
|
||||
value += v;
|
||||
else
|
||||
value = v;
|
||||
initial = value;
|
||||
age = 0;
|
||||
active = true;
|
||||
}
|
||||
static inline double calc_exp_constant(double times, double cycles)
|
||||
{
|
||||
if (cycles < 1.0)
|
||||
cycles = 1.0;
|
||||
return pow(times, 1.0 / cycles);
|
||||
}
|
||||
inline void age_exp(double constant, double epsilon) {
|
||||
if (active) {
|
||||
if (!(age & mask))
|
||||
value = initial * pow(constant, (double)age);
|
||||
else
|
||||
value *= constant;
|
||||
if (value < epsilon)
|
||||
active = false;
|
||||
age++;
|
||||
}
|
||||
}
|
||||
inline void age_lin(double constant, double epsilon) {
|
||||
if (active) {
|
||||
if (!(age & mask))
|
||||
value = initial - constant * age;
|
||||
else
|
||||
value -= constant;
|
||||
if (value < epsilon)
|
||||
active = false;
|
||||
age++;
|
||||
}
|
||||
}
|
||||
inline void deactivate() {
|
||||
active = false;
|
||||
value = 0;
|
||||
}
|
||||
};
|
||||
|
||||
class scheduler;
|
||||
|
||||
class task {
|
||||
public:
|
||||
virtual void execute(scheduler *s)=0;
|
||||
virtual void dispose() { delete this; }
|
||||
virtual ~task() {}
|
||||
};
|
||||
|
||||
/// this scheduler is based on std::multimap, so it isn't very fast, I guess
|
||||
/// maybe some day it should be rewritten to use heapsort or something
|
||||
/// work in progress, don't use!
|
||||
class scheduler {
|
||||
std::multimap<unsigned int, task *> timeline;
|
||||
unsigned int time, next_task;
|
||||
bool eob;
|
||||
class end_buf_task: public task {
|
||||
public:
|
||||
scheduler *p;
|
||||
end_buf_task(scheduler *_p) : p(_p) {}
|
||||
virtual void execute(scheduler *s) { p->eob = true; }
|
||||
virtual void dispose() { }
|
||||
} eobt;
|
||||
public:
|
||||
|
||||
scheduler()
|
||||
: time(0)
|
||||
, next_task((unsigned)-1)
|
||||
, eob(true)
|
||||
, eobt (this)
|
||||
{
|
||||
time = 0;
|
||||
next_task = (unsigned)-1;
|
||||
eob = false;
|
||||
}
|
||||
inline bool is_next_tick() {
|
||||
if (time < next_task)
|
||||
return true;
|
||||
do_tasks();
|
||||
}
|
||||
inline void next_tick() {
|
||||
time++;
|
||||
}
|
||||
void set(int pos, task *t) {
|
||||
timeline.insert(std::pair<unsigned int, task *>(time+pos, t));
|
||||
next_task = timeline.begin()->first;
|
||||
}
|
||||
void do_tasks() {
|
||||
std::multimap<unsigned int, task *>::iterator i = timeline.begin();
|
||||
while(i != timeline.end() && i->first == time) {
|
||||
i->second->execute(this);
|
||||
i->second->dispose();
|
||||
timeline.erase(i);
|
||||
}
|
||||
}
|
||||
bool is_eob() {
|
||||
return eob;
|
||||
}
|
||||
void set_buffer_size(int count) {
|
||||
set(count, &eobt);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Force "small enough" float value to zero
|
||||
*/
|
||||
inline void sanitize(float &value)
|
||||
{
|
||||
if (std::abs(value) < small_value<float>())
|
||||
value = 0.f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Force "small enough" double value to zero
|
||||
*/
|
||||
inline void sanitize(double &value)
|
||||
{
|
||||
if (std::abs(value) < small_value<double>())
|
||||
value = 0.f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Force "small enough" stereo value to zero
|
||||
*/
|
||||
template<class T>
|
||||
inline void sanitize(stereo_sample<T> &value)
|
||||
{
|
||||
sanitize(value.left);
|
||||
sanitize(value.right);
|
||||
}
|
||||
|
||||
inline float fract16(unsigned int value)
|
||||
{
|
||||
return (value & 0xFFFF) * (1.0 / 65536.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* typical precalculated sine table
|
||||
*/
|
||||
template<class T, int N, int Multiplier>
|
||||
class sine_table
|
||||
{
|
||||
public:
|
||||
static bool initialized;
|
||||
static T data[N+1];
|
||||
sine_table() {
|
||||
if (initialized)
|
||||
return;
|
||||
initialized = true;
|
||||
for (int i=0; i<N+1; i++)
|
||||
data[i] = (T)(Multiplier*sin(i*2*M_PI*(1.0/N)));
|
||||
}
|
||||
};
|
||||
|
||||
template<class T, int N, int Multiplier>
|
||||
bool sine_table<T,N,Multiplier>::initialized = false;
|
||||
|
||||
template<class T, int N, int Multiplier>
|
||||
T sine_table<T,N,Multiplier>::data[N+1];
|
||||
|
||||
/// fast float to int conversion using default rounding mode
|
||||
inline int fastf2i_drm(float f)
|
||||
{
|
||||
#ifdef __X86__
|
||||
volatile int v;
|
||||
__asm ( "flds %1; fistpl %0" : "=m"(v) : "m"(f));
|
||||
return v;
|
||||
#else
|
||||
return (int)nearbyintf(f);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Convert MIDI note to frequency in Hz.
|
||||
inline float note_to_hz(double note, double detune_cents = 0.0)
|
||||
{
|
||||
return 440 * pow(2.0, (note - 69 + detune_cents/100.0) / 12.0);
|
||||
}
|
||||
|
||||
/// Hermite interpolation between two points and slopes in normalized range (written after Wikipedia article)
|
||||
/// @arg t normalized x coordinate (0-1 over the interval in question)
|
||||
/// @arg p0 first point
|
||||
/// @arg p1 second point
|
||||
/// @arg m0 first slope (multiply by interval width when using over non-1-wide interval)
|
||||
/// @arg m1 second slope (multiply by interval width when using over non-1-wide interval)
|
||||
inline float normalized_hermite(float t, float p0, float p1, float m0, float m1)
|
||||
{
|
||||
float t2 = t*t;
|
||||
float t3 = t2*t;
|
||||
return (2*t3 - 3*t2 + 1) * p0 + (t3 - 2*t2 + t) * m0 + (-2*t3 + 3*t2) * p1 + (t3-t2) * m1;
|
||||
}
|
||||
|
||||
/// Hermite interpolation between two points and slopes
|
||||
/// @arg x point within interval (x0 <= x <= x1)
|
||||
/// @arg x0 interval start
|
||||
/// @arg x1 interval end
|
||||
/// @arg p0 value at x0
|
||||
/// @arg p1 value at x1
|
||||
/// @arg m0 slope (steepness, tangent) at x0
|
||||
/// @arg m1 slope at x1
|
||||
inline float hermite_interpolation(float x, float x0, float x1, float p0, float p1, float m0, float m1)
|
||||
{
|
||||
float width = x1 - x0;
|
||||
float t = (x - x0) / width;
|
||||
m0 *= width;
|
||||
m1 *= width;
|
||||
float t2 = t*t;
|
||||
float t3 = t2*t;
|
||||
|
||||
float ct0 = p0;
|
||||
float ct1 = m0;
|
||||
float ct2 = -3 * p0 - 2 * m0 + 3 * p1 - m1;
|
||||
float ct3 = 2 * p0 + m0 - 2 * p1 + m1;
|
||||
|
||||
return ct3 * t3 + ct2 * t2 + ct1 * t + ct0;
|
||||
//return (2*t3 - 3*t2 + 1) * p0 + (t3 - 2*t2 + t) * m0 + (-2*t3 + 3*t2) * p1 + (t3-t2) * m1;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
228
plugins/ladspa_effect/calf/calf/synth.h
Normal file
228
plugins/ladspa_effect/calf/calf/synth.h
Normal file
@@ -0,0 +1,228 @@
|
||||
/* Calf DSP Library
|
||||
* Framework for synthesizer-like plugins. This is based
|
||||
* on my earlier work on Drawbar electric organ emulator.
|
||||
*
|
||||
* Copyright (C) 2007 Krzysztof Foltman
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __CALF_SYNTH_H
|
||||
#define __CALF_SYNTH_H
|
||||
|
||||
#include <list>
|
||||
#include <stack>
|
||||
#include <bitset>
|
||||
#include "primitives.h"
|
||||
#include "audio_fx.h"
|
||||
|
||||
namespace dsp {
|
||||
|
||||
/**
|
||||
* A kind of set with fast non-ordered iteration, used for storing lists of pressed keys.
|
||||
*/
|
||||
class keystack {
|
||||
private:
|
||||
int dcount;
|
||||
uint8_t active[128];
|
||||
uint8_t states[128];
|
||||
public:
|
||||
keystack() {
|
||||
memset(states, 0xFF, sizeof(states));
|
||||
dcount = 0;
|
||||
}
|
||||
void clear() {
|
||||
for (int i=0; i<dcount; i++)
|
||||
states[active[i]] = 0xFF;
|
||||
dcount = 0;
|
||||
}
|
||||
bool push(int key) {
|
||||
assert(key >= 0 && key <= 127);
|
||||
if (states[key] != 0xFF) {
|
||||
return true;
|
||||
}
|
||||
states[key] = dcount;
|
||||
active[dcount++] = key;
|
||||
return false;
|
||||
}
|
||||
bool pop(int key) {
|
||||
if (states[key] == 0xFF)
|
||||
return false;
|
||||
int pos = states[key];
|
||||
if (pos != dcount-1) {
|
||||
// reuse the popped item's stack position for stack top
|
||||
int last = active[dcount-1];
|
||||
active[pos] = last;
|
||||
// mark that position's new place on stack
|
||||
states[last] = pos;
|
||||
}
|
||||
states[key] = 0xFF;
|
||||
dcount--;
|
||||
return true;
|
||||
}
|
||||
inline bool has(int key) {
|
||||
return states[key] != 0xFF;
|
||||
}
|
||||
inline int count() {
|
||||
return dcount;
|
||||
}
|
||||
inline bool empty() {
|
||||
return (dcount == 0);
|
||||
}
|
||||
inline int nth(int n) {
|
||||
return active[n];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert MIDI note number to normalized UINT phase (where 1<<32 is full cycle).
|
||||
* @param MIDI note number
|
||||
* @param cents detune in cents (1/100 of a semitone)
|
||||
* @param sr sample rate
|
||||
*/
|
||||
inline unsigned int midi_note_to_phase(int note, double cents, int sr) {
|
||||
double incphase = 440*pow(2.0, (note-69)/12.0 + cents/1200.0)/sr;
|
||||
if (incphase >= 1.0) incphase = fmod(incphase, 1.0);
|
||||
incphase *= 65536.0*65536.0;
|
||||
return (unsigned int)incphase;
|
||||
}
|
||||
|
||||
// Base class for all voice objects
|
||||
class voice {
|
||||
public:
|
||||
int sample_rate;
|
||||
bool released, sostenuto, stolen;
|
||||
|
||||
voice() : sample_rate(-1), released(false), sostenuto(false), stolen(false) {}
|
||||
|
||||
/// reset voice to default state (used when a voice is to be reused)
|
||||
virtual void setup(int sr) { sample_rate = sr; }
|
||||
/// reset voice to default state (used when a voice is to be reused)
|
||||
virtual void reset()=0;
|
||||
/// a note was pressed
|
||||
virtual void note_on(int note, int vel)=0;
|
||||
/// a note was released
|
||||
virtual void note_off(int vel)=0;
|
||||
/// check if voice can be removed from active voice list
|
||||
virtual bool get_active()=0;
|
||||
/// render voice data to buffer
|
||||
virtual void render_to(float (*buf)[2], int nsamples)=0;
|
||||
/// very fast note off
|
||||
virtual void steal()=0;
|
||||
/// return the note used by this voice
|
||||
virtual int get_current_note()=0;
|
||||
virtual float get_priority() { return stolen ? 20000 : (released ? 1 : (sostenuto ? 200 : 100)); }
|
||||
/// empty virtual destructor
|
||||
virtual ~voice() {}
|
||||
};
|
||||
|
||||
/// An "optimized" voice class using fixed-size processing units
|
||||
/// and fixed number of channels. The drawback is that voice
|
||||
/// control is not sample-accurate, and no modulation input
|
||||
/// is possible, but it should be good enough for most cases
|
||||
/// (like Calf Organ).
|
||||
template<class Base>
|
||||
class block_voice: public Base {
|
||||
public:
|
||||
// derived from Base
|
||||
// enum { Channels = 2 };
|
||||
using Base::Channels;
|
||||
// enum { BlockSize = 16 };
|
||||
using Base::BlockSize;
|
||||
// float output_buffer[BlockSize][Channels];
|
||||
using Base::output_buffer;
|
||||
// void render_block();
|
||||
using Base::render_block;
|
||||
unsigned int read_ptr;
|
||||
|
||||
block_voice()
|
||||
{
|
||||
read_ptr = BlockSize;
|
||||
}
|
||||
virtual void reset()
|
||||
{
|
||||
Base::reset();
|
||||
read_ptr = BlockSize;
|
||||
}
|
||||
virtual void render_to(float (*buf)[2], int nsamples)
|
||||
{
|
||||
int p = 0;
|
||||
while(p < nsamples)
|
||||
{
|
||||
if (read_ptr == BlockSize)
|
||||
{
|
||||
render_block();
|
||||
read_ptr = 0;
|
||||
}
|
||||
int ncopy = std::min<int>(BlockSize - read_ptr, nsamples - p);
|
||||
for (int i = 0; i < ncopy; i++)
|
||||
for (int c = 0; c < Channels; c++)
|
||||
buf[p + i][c] += output_buffer[read_ptr + i][c];
|
||||
p += ncopy;
|
||||
read_ptr += ncopy;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// Base class for all kinds of polyphonic instruments, provides
|
||||
/// somewhat reasonable voice management, pedal support - and
|
||||
/// little else. It's implemented as a base class with virtual
|
||||
/// functions, so there's some performance loss, but it shouldn't
|
||||
/// be horrible.
|
||||
/// @todo it would make sense to support all notes off controller too
|
||||
struct basic_synth {
|
||||
protected:
|
||||
/// Current sample rate
|
||||
int sample_rate;
|
||||
/// Hold pedal state
|
||||
bool hold;
|
||||
/// Sostenuto pedal state
|
||||
bool sostenuto;
|
||||
/// Voices currently playing
|
||||
std::list<dsp::voice *> active_voices;
|
||||
/// Voices allocated, but not used
|
||||
std::stack<dsp::voice *> unused_voices;
|
||||
/// Gate values for all 128 MIDI notes
|
||||
std::bitset<128> gate;
|
||||
/// Maximum allocated number of channels
|
||||
unsigned int polyphony_limit;
|
||||
|
||||
void kill_note(int note, int vel, bool just_one);
|
||||
public:
|
||||
virtual void setup(int sr) {
|
||||
sample_rate = sr;
|
||||
hold = false;
|
||||
sostenuto = false;
|
||||
polyphony_limit = (unsigned)-1;
|
||||
}
|
||||
virtual void trim_voices();
|
||||
virtual dsp::voice *give_voice();
|
||||
virtual dsp::voice *alloc_voice()=0;
|
||||
virtual dsp::voice *steal_voice();
|
||||
virtual void render_to(float (*output)[2], int nsamples);
|
||||
virtual void note_on(int note, int vel);
|
||||
virtual void percussion_note_on(int note, int vel) {}
|
||||
virtual void control_change(int ctl, int val);
|
||||
virtual void note_off(int note, int vel);
|
||||
/// amt = -8192 to 8191
|
||||
virtual void pitch_bend(int amt) {}
|
||||
virtual void on_pedal_release();
|
||||
virtual bool check_percussion() { return active_voices.empty(); }
|
||||
virtual ~basic_synth();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
161
plugins/ladspa_effect/calf/calf/utils.h
Normal file
161
plugins/ladspa_effect/calf/calf/utils.h
Normal file
@@ -0,0 +1,161 @@
|
||||
/* Calf DSP Library
|
||||
* Utilities
|
||||
*
|
||||
* Copyright (C) 2008 Krzysztof Foltman
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __CALF_UTILS_H
|
||||
#define __CALF_UTILS_H
|
||||
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
namespace calf_utils
|
||||
{
|
||||
|
||||
/// Pthreads based mutex class
|
||||
class ptmutex
|
||||
{
|
||||
public:
|
||||
pthread_mutex_t pm;
|
||||
|
||||
ptmutex(int type = PTHREAD_MUTEX_RECURSIVE)
|
||||
{
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_init(&attr);
|
||||
pthread_mutexattr_settype(&attr, type);
|
||||
pthread_mutex_init(&pm, &attr);
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
}
|
||||
|
||||
bool lock()
|
||||
{
|
||||
return pthread_mutex_lock(&pm) == 0;
|
||||
}
|
||||
|
||||
bool trylock()
|
||||
{
|
||||
return pthread_mutex_trylock(&pm) == 0;
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
pthread_mutex_unlock(&pm);
|
||||
}
|
||||
|
||||
~ptmutex()
|
||||
{
|
||||
pthread_mutex_destroy(&pm);
|
||||
}
|
||||
};
|
||||
|
||||
/// Exception-safe mutex lock
|
||||
class ptlock
|
||||
{
|
||||
ptmutex &mutex;
|
||||
bool locked;
|
||||
|
||||
public:
|
||||
ptlock(ptmutex &_m) : mutex(_m), locked(true)
|
||||
{
|
||||
mutex.lock();
|
||||
}
|
||||
void unlock()
|
||||
{
|
||||
mutex.unlock();
|
||||
locked = false;
|
||||
}
|
||||
void unlocked()
|
||||
{
|
||||
locked = false;
|
||||
}
|
||||
~ptlock()
|
||||
{
|
||||
if (locked)
|
||||
mutex.unlock();
|
||||
}
|
||||
};
|
||||
|
||||
/// Exception-safe temporary assignment
|
||||
template<class T>
|
||||
class scope_assign
|
||||
{
|
||||
T &data, old_value;
|
||||
public:
|
||||
scope_assign(T &_data, T new_value)
|
||||
: data(_data), old_value(_data)
|
||||
{
|
||||
data = new_value;
|
||||
}
|
||||
~scope_assign()
|
||||
{
|
||||
data = old_value;
|
||||
}
|
||||
};
|
||||
|
||||
struct text_exception: public std::exception
|
||||
{
|
||||
const char *text;
|
||||
std::string container;
|
||||
public:
|
||||
text_exception(const std::string &t) : container(t) { text = container.c_str(); }
|
||||
virtual const char *what() const throw () { return text; }
|
||||
virtual ~text_exception() throw () {}
|
||||
};
|
||||
|
||||
struct file_exception: public std::exception
|
||||
{
|
||||
const char *text;
|
||||
std::string message, filename, container;
|
||||
public:
|
||||
file_exception(const std::string &f) : message(strerror(errno)), filename(f), container(filename + ":" + message) { text = container.c_str(); }
|
||||
file_exception(const std::string &f, const std::string &t) : message(t), filename(f), container(filename + ":" + message) { text = container.c_str(); }
|
||||
virtual const char *what() const throw () { return text; }
|
||||
virtual ~file_exception() throw () {}
|
||||
};
|
||||
|
||||
/// String-to-string mapping
|
||||
typedef std::map<std::string, std::string> dictionary;
|
||||
|
||||
/// Serialize a dictonary to a string
|
||||
extern std::string encode_map(const dictionary &data);
|
||||
/// Deserialize a dictonary from a string
|
||||
extern void decode_map(dictionary &data, const std::string &src);
|
||||
|
||||
/// int-to-string
|
||||
extern std::string i2s(int value);
|
||||
|
||||
/// float-to-string
|
||||
extern std::string f2s(double value);
|
||||
|
||||
/// float-to-string-that-doesn't-resemble-an-int
|
||||
extern std::string ff2s(double value);
|
||||
|
||||
/// Escape a string to be used in XML file
|
||||
std::string xml_escape(const std::string &src);
|
||||
|
||||
/// Load file from disk into a std::string blob, or throw file_exception
|
||||
std::string load_file(const std::string &src);
|
||||
|
||||
/// Indent a string by another string (prefix each line)
|
||||
std::string indent(const std::string &src, const std::string &indent);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
325
plugins/ladspa_effect/calf/giface.cpp
Normal file
325
plugins/ladspa_effect/calf/giface.cpp
Normal file
@@ -0,0 +1,325 @@
|
||||
/* Calf DSP Library
|
||||
* Module wrapper methods.
|
||||
*
|
||||
* Copyright (C) 2001-2007 Krzysztof Foltman
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <memory.h>
|
||||
#include <calf/giface.h>
|
||||
#include <stdio.h>
|
||||
using namespace std;
|
||||
using namespace calf_utils;
|
||||
using namespace calf_plugins;
|
||||
|
||||
float parameter_properties::from_01(double value01) const
|
||||
{
|
||||
double value = dsp::clip(value01, 0., 1.);
|
||||
switch(flags & PF_SCALEMASK)
|
||||
{
|
||||
case PF_SCALE_DEFAULT:
|
||||
case PF_SCALE_LINEAR:
|
||||
case PF_SCALE_PERC:
|
||||
default:
|
||||
value = min + (max - min) * value01;
|
||||
break;
|
||||
case PF_SCALE_QUAD:
|
||||
value = min + (max - min) * value01 * value01;
|
||||
break;
|
||||
case PF_SCALE_LOG:
|
||||
value = min * pow(double(max / min), value01);
|
||||
break;
|
||||
case PF_SCALE_GAIN:
|
||||
if (value01 < 0.00001)
|
||||
value = min;
|
||||
else {
|
||||
float rmin = std::max(1.0f / 1024.0f, min);
|
||||
value = rmin * pow(double(max / rmin), value01);
|
||||
}
|
||||
break;
|
||||
case PF_SCALE_LOG_INF:
|
||||
assert(step);
|
||||
if (value01 > (step - 1.0) / step)
|
||||
value = FAKE_INFINITY;
|
||||
else
|
||||
value = min * pow(double(max / min), value01 * step / (step - 1.0));
|
||||
break;
|
||||
}
|
||||
switch(flags & PF_TYPEMASK)
|
||||
{
|
||||
case PF_INT:
|
||||
case PF_BOOL:
|
||||
case PF_ENUM:
|
||||
case PF_ENUM_MULTI:
|
||||
if (value > 0)
|
||||
value = (int)(value + 0.5);
|
||||
else
|
||||
value = (int)(value - 0.5);
|
||||
break;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
double parameter_properties::to_01(float value) const
|
||||
{
|
||||
switch(flags & PF_SCALEMASK)
|
||||
{
|
||||
case PF_SCALE_DEFAULT:
|
||||
case PF_SCALE_LINEAR:
|
||||
case PF_SCALE_PERC:
|
||||
default:
|
||||
return double(value - min) / (max - min);
|
||||
case PF_SCALE_QUAD:
|
||||
return sqrt(double(value - min) / (max - min));
|
||||
case PF_SCALE_LOG:
|
||||
value /= min;
|
||||
return log((double)value) / log((double)max / min);
|
||||
case PF_SCALE_LOG_INF:
|
||||
if (IS_FAKE_INFINITY(value))
|
||||
return max;
|
||||
value /= min;
|
||||
assert(step);
|
||||
return (step - 1.0) * log((double)value) / (step * log((double)max / min));
|
||||
case PF_SCALE_GAIN:
|
||||
if (value < 1.0 / 1024.0) // new bottom limit - 60 dB
|
||||
return 0;
|
||||
double rmin = std::max(1.0f / 1024.0f, min);
|
||||
value /= rmin;
|
||||
return log((double)value) / log(max / rmin);
|
||||
}
|
||||
}
|
||||
|
||||
float parameter_properties::get_increment() const
|
||||
{
|
||||
float increment = 0.01;
|
||||
if (step > 1)
|
||||
increment = 1.0 / (step - 1);
|
||||
else
|
||||
if (step > 0 && step < 1)
|
||||
increment = step;
|
||||
else
|
||||
if ((flags & PF_TYPEMASK) != PF_FLOAT)
|
||||
increment = 1.0 / (max - min);
|
||||
return increment;
|
||||
}
|
||||
|
||||
int parameter_properties::get_char_count() const
|
||||
{
|
||||
if ((flags & PF_SCALEMASK) == PF_SCALE_PERC)
|
||||
return 6;
|
||||
if ((flags & PF_SCALEMASK) == PF_SCALE_GAIN) {
|
||||
char buf[256];
|
||||
size_t len = 0;
|
||||
sprintf(buf, "%0.0f dB", 6.0 * log(min) / log(2));
|
||||
len = strlen(buf);
|
||||
sprintf(buf, "%0.0f dB", 6.0 * log(max) / log(2));
|
||||
len = std::max(len, strlen(buf)) + 2;
|
||||
return (int)len;
|
||||
}
|
||||
return std::max(to_string(min).length(), std::max(to_string(max).length(), to_string(min + (max-min) * 0.987654).length()));
|
||||
}
|
||||
|
||||
std::string parameter_properties::to_string(float value) const
|
||||
{
|
||||
char buf[32];
|
||||
if ((flags & PF_SCALEMASK) == PF_SCALE_PERC) {
|
||||
sprintf(buf, "%0.f%%", 100.0 * value);
|
||||
return string(buf);
|
||||
}
|
||||
if ((flags & PF_SCALEMASK) == PF_SCALE_GAIN) {
|
||||
if (value < 1.0 / 1024.0) // new bottom limit - 60 dB
|
||||
return "-inf dB"; // XXXKF change to utf-8 infinity
|
||||
sprintf(buf, "%0.1f dB", 6.0 * log(value) / log(2));
|
||||
return string(buf);
|
||||
}
|
||||
switch(flags & PF_TYPEMASK)
|
||||
{
|
||||
case PF_STRING:
|
||||
return "N/A";
|
||||
case PF_INT:
|
||||
case PF_BOOL:
|
||||
case PF_ENUM:
|
||||
case PF_ENUM_MULTI:
|
||||
value = (int)value;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((flags & PF_SCALEMASK) == PF_SCALE_LOG_INF && IS_FAKE_INFINITY(value))
|
||||
sprintf(buf, "+inf"); // XXXKF change to utf-8 infinity
|
||||
else
|
||||
sprintf(buf, "%g", value);
|
||||
|
||||
switch(flags & PF_UNITMASK) {
|
||||
case PF_UNIT_DB: return string(buf) + " dB";
|
||||
case PF_UNIT_HZ: return string(buf) + " Hz";
|
||||
case PF_UNIT_SEC: return string(buf) + " s";
|
||||
case PF_UNIT_MSEC: return string(buf) + " ms";
|
||||
case PF_UNIT_CENTS: return string(buf) + " ct";
|
||||
case PF_UNIT_SEMITONES: return string(buf) + "#";
|
||||
case PF_UNIT_BPM: return string(buf) + " bpm";
|
||||
case PF_UNIT_RPM: return string(buf) + " rpm";
|
||||
case PF_UNIT_DEG: return string(buf) + " deg";
|
||||
case PF_UNIT_NOTE:
|
||||
{
|
||||
static const char *notes = "C C#D D#E F F#G G#A A#B ";
|
||||
int note = (int)value;
|
||||
if (note < 0 || note > 127)
|
||||
return "---";
|
||||
return string(notes + 2 * (note % 12), 2) + i2s(note / 12 - 2);
|
||||
}
|
||||
}
|
||||
|
||||
return string(buf);
|
||||
}
|
||||
|
||||
void calf_plugins::plugin_ctl_iface::clear_preset() {
|
||||
int param_count = get_param_count();
|
||||
for (int i=0; i < param_count; i++)
|
||||
{
|
||||
parameter_properties &pp = *get_param_props(i);
|
||||
if ((pp.flags & PF_TYPEMASK) == PF_STRING)
|
||||
{
|
||||
configure(pp.short_name, pp.choices ? pp.choices[0] : "");
|
||||
}
|
||||
else
|
||||
set_param_value(i, pp.def_value);
|
||||
}
|
||||
}
|
||||
|
||||
const char *calf_plugins::load_gui_xml(const std::string &plugin_id)
|
||||
{
|
||||
#if 0
|
||||
try {
|
||||
return strdup(calf_utils::load_file((std::string(PKGLIBDIR) + "/gui-" + plugin_id + ".xml").c_str()).c_str());
|
||||
}
|
||||
catch(file_exception e)
|
||||
#endif
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool calf_plugins::check_for_message_context_ports(parameter_properties *parameters, int count)
|
||||
{
|
||||
for (int i = count - 1; i >= 0; i--)
|
||||
{
|
||||
if (parameters[i].flags & PF_PROP_MSGCONTEXT)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool calf_plugins::check_for_string_ports(parameter_properties *parameters, int count)
|
||||
{
|
||||
for (int i = count - 1; i >= 0; i--)
|
||||
{
|
||||
if ((parameters[i].flags & PF_TYPEMASK) == PF_STRING)
|
||||
return true;
|
||||
if ((parameters[i].flags & PF_TYPEMASK) < PF_STRING)
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#if USE_DSSI
|
||||
struct osc_cairo_control: public cairo_iface
|
||||
{
|
||||
osctl::osc_inline_typed_strstream &os;
|
||||
|
||||
osc_cairo_control(osctl::osc_inline_typed_strstream &_os) : os(_os) {}
|
||||
virtual void set_source_rgba(float r, float g, float b, float a = 1.f)
|
||||
{
|
||||
os << (uint32_t)LGI_SET_RGBA << r << g << b << a;
|
||||
}
|
||||
virtual void set_line_width(float width)
|
||||
{
|
||||
os << (uint32_t)LGI_SET_WIDTH << width;
|
||||
}
|
||||
};
|
||||
|
||||
static void send_graph_via_osc(osctl::osc_client &client, const std::string &address, line_graph_iface *graph, std::vector<int> ¶ms)
|
||||
{
|
||||
osctl::osc_inline_typed_strstream os;
|
||||
osc_cairo_control cairoctl(os);
|
||||
for (size_t i = 0; i < params.size(); i++)
|
||||
{
|
||||
int index = params[i];
|
||||
os << (uint32_t)LGI_GRAPH;
|
||||
os << (uint32_t)index;
|
||||
for (int j = 0; ; j++)
|
||||
{
|
||||
float data[128];
|
||||
if (graph->get_graph(index, j, data, 128, &cairoctl))
|
||||
{
|
||||
os << (uint32_t)LGI_SUBGRAPH;
|
||||
os << (uint32_t)128;
|
||||
for (int p = 0; p < 128; p++)
|
||||
os << data[p];
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
for (int j = 0; ; j++)
|
||||
{
|
||||
float x, y;
|
||||
int size = 3;
|
||||
if (graph->get_dot(index, j, x, y, size, &cairoctl))
|
||||
os << (uint32_t)LGI_DOT << x << y << (uint32_t)size;
|
||||
else
|
||||
break;
|
||||
}
|
||||
for (int j = 0; ; j++)
|
||||
{
|
||||
float pos = 0;
|
||||
bool vertical = false;
|
||||
string legend;
|
||||
if (graph->get_gridline(index, j, pos, vertical, legend, &cairoctl))
|
||||
os << (uint32_t)LGI_LEGEND << pos << (uint32_t)(vertical ? 1 : 0) << legend;
|
||||
else
|
||||
break;
|
||||
}
|
||||
os << (uint32_t)LGI_END_ITEM;
|
||||
}
|
||||
os << (uint32_t)LGI_END;
|
||||
client.send(address, os);
|
||||
}
|
||||
|
||||
calf_plugins::dssi_feedback_sender::dssi_feedback_sender(const char *URI, line_graph_iface *_graph, calf_plugins::parameter_properties *props, int num_params)
|
||||
{
|
||||
graph = _graph;
|
||||
client = new osctl::osc_client;
|
||||
client->bind("0.0.0.0", 0);
|
||||
client->set_url(URI);
|
||||
for (int i = 0; i < num_params; i++)
|
||||
{
|
||||
if (props[i].flags & PF_PROP_GRAPH)
|
||||
indices.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
void calf_plugins::dssi_feedback_sender::update()
|
||||
{
|
||||
send_graph_via_osc(*client, "/lineGraph", graph, indices);
|
||||
}
|
||||
|
||||
calf_plugins::dssi_feedback_sender::~dssi_feedback_sender()
|
||||
{
|
||||
// this would not be received by GUI's main loop because it's already been terminated
|
||||
// client->send("/iQuit");
|
||||
delete client;
|
||||
}
|
||||
#endif
|
||||
136
plugins/ladspa_effect/calf/modmatrix.cpp
Normal file
136
plugins/ladspa_effect/calf/modmatrix.cpp
Normal file
@@ -0,0 +1,136 @@
|
||||
/* Calf DSP Library
|
||||
* Modulation matrix boilerplate code.
|
||||
*
|
||||
* Copyright (C) 2001-2007 Krzysztof Foltman
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <sstream>
|
||||
#include <calf/modmatrix.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace dsp;
|
||||
using namespace calf_plugins;
|
||||
|
||||
const char *mod_mapping_names[] = { "0..1", "-1..1", "-1..0", "x^2", "2x^2-1", "ASqr", "ASqrBip", "Para", NULL };
|
||||
|
||||
const float mod_matrix::scaling_coeffs[dsp::map_type_count][3] = {
|
||||
{ 0, 1, 0 },
|
||||
{ -1, 2, 0 },
|
||||
{ -1, 1, 0 },
|
||||
{ 0, 0, 1 },
|
||||
{ -1, 0, 1 },
|
||||
{ 0, 2, -1 },
|
||||
{ -1, 4, -2 },
|
||||
{ 0, 4, -4 },
|
||||
};
|
||||
|
||||
mod_matrix::mod_matrix(modulation_entry *_matrix, unsigned int _rows, const char **_src_names, const char **_dest_names)
|
||||
: matrix(_matrix)
|
||||
, matrix_rows(_rows)
|
||||
, mod_src_names(_src_names)
|
||||
, mod_dest_names(_dest_names)
|
||||
{
|
||||
table_column_info tci[6] = {
|
||||
{ "Source", TCT_ENUM, 0, 0, 0, mod_src_names },
|
||||
{ "Mapping", TCT_ENUM, 0, 0, 0, mod_mapping_names },
|
||||
{ "Modulator", TCT_ENUM, 0, 0, 0, mod_src_names },
|
||||
{ "Amount", TCT_FLOAT, 0, 1, 1, NULL},
|
||||
{ "Destination", TCT_ENUM, 0, 0, 0, mod_dest_names },
|
||||
{ NULL }
|
||||
};
|
||||
assert(sizeof(table_columns) == sizeof(tci));
|
||||
memcpy(table_columns, tci, sizeof(table_columns));
|
||||
for (unsigned int i = 0; i < matrix_rows; i++)
|
||||
matrix[i].reset();
|
||||
}
|
||||
|
||||
const table_column_info *mod_matrix::get_table_columns(int param)
|
||||
{
|
||||
return table_columns;
|
||||
}
|
||||
|
||||
uint32_t mod_matrix::get_table_rows(int param)
|
||||
{
|
||||
return matrix_rows;
|
||||
}
|
||||
|
||||
std::string mod_matrix::get_cell(int param, int row, int column)
|
||||
{
|
||||
assert(row >= 0 && row < (int)matrix_rows);
|
||||
modulation_entry &slot = matrix[row];
|
||||
switch(column) {
|
||||
case 0: // source 1
|
||||
return mod_src_names[slot.src1];
|
||||
case 1: // mapping mode
|
||||
return mod_mapping_names[slot.mapping];
|
||||
case 2: // source 2
|
||||
return mod_src_names[slot.src2];
|
||||
case 3: // amount
|
||||
return calf_utils::f2s(slot.amount);
|
||||
case 4: // destination
|
||||
return mod_dest_names[slot.dest];
|
||||
default:
|
||||
assert(0);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
void mod_matrix::set_cell(int param, int row, int column, const std::string &src, std::string &error)
|
||||
{
|
||||
assert(row >= 0 && row < (int)matrix_rows);
|
||||
modulation_entry &slot = matrix[row];
|
||||
const char **arr = mod_src_names;
|
||||
if (column == 1)
|
||||
arr = mod_mapping_names;
|
||||
if (column == 4)
|
||||
arr = mod_dest_names;
|
||||
switch(column) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 4:
|
||||
{
|
||||
for (int i = 0; arr[i]; i++)
|
||||
{
|
||||
if (src == arr[i])
|
||||
{
|
||||
if (column == 0)
|
||||
slot.src1 = i;
|
||||
else if (column == 1)
|
||||
slot.mapping = (mapping_mode)i;
|
||||
else if (column == 2)
|
||||
slot.src2 = i;
|
||||
else if (column == 4)
|
||||
slot.dest = i;
|
||||
error.clear();
|
||||
return;
|
||||
}
|
||||
}
|
||||
error = "Invalid name: " + src;
|
||||
return;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
stringstream ss(src);
|
||||
ss >> slot.amount;
|
||||
error.clear();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
614
plugins/ladspa_effect/calf/modules.cpp
Normal file
614
plugins/ladspa_effect/calf/modules.cpp
Normal file
@@ -0,0 +1,614 @@
|
||||
/* Calf DSP Library
|
||||
* Example audio modules - parameters and LADSPA wrapper instantiation
|
||||
*
|
||||
* Copyright (C) 2001-2008 Krzysztof Foltman
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <memory.h>
|
||||
#if USE_JACK
|
||||
#include <jack/jack.h>
|
||||
#endif
|
||||
#include <calf/giface.h>
|
||||
#include <calf/metadata.h>
|
||||
#include <calf/audio_fx.h>
|
||||
|
||||
using namespace dsp;
|
||||
using namespace calf_plugins;
|
||||
|
||||
const char *calf_plugins::calf_copyright_info = "(C) 2001-2008 Krzysztof Foltman, license: LGPL";
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CALF_PORT_NAMES(flanger) = {"In L", "In R", "Out L", "Out R"};
|
||||
|
||||
CALF_PORT_PROPS(flanger) = {
|
||||
{ 0.1, 0.1, 10, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC | PF_PROP_GRAPH, NULL, "min_delay", "Minimum delay" },
|
||||
{ 0.5, 0.1, 10, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "mod_depth", "Modulation depth" },
|
||||
{ 0.25, 0.01, 20, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ, NULL, "mod_rate", "Modulation rate" },
|
||||
{ 0.90, -0.99, 0.99, 0, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "feedback", "Feedback" },
|
||||
{ 0, 0, 360, 9, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_DEG, NULL, "stereo", "Stereo phase" },
|
||||
{ 0, 0, 1, 2, PF_BOOL | PF_CTL_BUTTON , NULL, "reset", "Reset" },
|
||||
{ 1, 0, 4, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_NOBOUNDS, NULL, "amount", "Amount" },
|
||||
{ 1.0, 0, 4, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_NOBOUNDS, NULL, "dry", "Dry Amount" },
|
||||
};
|
||||
|
||||
CALF_PLUGIN_INFO(flanger) = { 0x847d, "Flanger", "Calf Flanger", "Krzysztof Foltman", calf_plugins::calf_copyright_info, "FlangerPlugin" };
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CALF_PORT_NAMES(phaser) = {"In L", "In R", "Out L", "Out R"};
|
||||
|
||||
CALF_PORT_PROPS(phaser) = {
|
||||
{ 1000, 20, 20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ | PF_PROP_GRAPH, NULL, "base_freq", "Center Freq" },
|
||||
{ 4000, 0, 10800, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "mod_depth", "Modulation depth" },
|
||||
{ 0.25, 0.01, 20, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ, NULL, "mod_rate", "Modulation rate" },
|
||||
{ 0.25, -0.99, 0.99, 0, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "feedback", "Feedback" },
|
||||
{ 6, 1, 12, 12, PF_INT | PF_SCALE_LINEAR | PF_CTL_KNOB, NULL, "stages", "# Stages" },
|
||||
{ 180, 0, 360, 9, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_DEG, NULL, "stereo", "Stereo phase" },
|
||||
{ 0, 0, 1, 2, PF_BOOL | PF_CTL_BUTTON , NULL, "reset", "Reset" },
|
||||
{ 1, 0, 4, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_NOBOUNDS, NULL, "amount", "Amount" },
|
||||
{ 1.0, 0, 4, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_NOBOUNDS, NULL, "dry", "Dry Amount" },
|
||||
};
|
||||
|
||||
CALF_PLUGIN_INFO(phaser) = { 0x8484, "Phaser", "Calf Phaser", "Krzysztof Foltman", calf_plugins::calf_copyright_info, "PhaserPlugin" };
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CALF_PORT_NAMES(reverb) = {"In L", "In R", "Out L", "Out R"};
|
||||
|
||||
const char *reverb_room_sizes[] = { "Small", "Medium", "Large", "Tunnel-like", "Large/smooth", "Experimental" };
|
||||
|
||||
CALF_PORT_PROPS(reverb) = {
|
||||
{ 1.5, 0.4, 15.0, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_SEC, NULL, "decay_time", "Decay time" },
|
||||
{ 5000, 2000,20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ, NULL, "hf_damp", "High Frq Damp" },
|
||||
{ 2, 0, 5, 0, PF_ENUM | PF_CTL_COMBO , reverb_room_sizes, "room_size", "Room size", },
|
||||
{ 0.5, 0, 1, 0, PF_FLOAT | PF_CTL_KNOB | PF_SCALE_PERC, NULL, "diffusion", "Diffusion" },
|
||||
{ 0.25, 0, 2, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_NOBOUNDS, NULL, "amount", "Wet Amount" },
|
||||
{ 1.0, 0, 2, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_NOBOUNDS, NULL, "dry", "Dry Amount" },
|
||||
{ 0, 0, 50, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "predelay", "Pre Delay" },
|
||||
{ 300, 20, 20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ, NULL, "bass_cut", "Bass Cut" },
|
||||
{ 5000, 20, 20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ, NULL, "treble_cut", "Treble Cut" },
|
||||
};
|
||||
|
||||
CALF_PLUGIN_INFO(reverb) = { 0x847e, "Reverb", "Calf Reverb", "Krzysztof Foltman", calf_plugins::calf_copyright_info, "ReverbPlugin" };
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CALF_PORT_NAMES(filter) = {"In L", "In R", "Out L", "Out R"};
|
||||
|
||||
const char *filter_choices[] = {
|
||||
"12dB/oct Lowpass",
|
||||
"24dB/oct Lowpass",
|
||||
"36dB/oct Lowpass",
|
||||
"12dB/oct Highpass",
|
||||
"24dB/oct Highpass",
|
||||
"36dB/oct Highpass",
|
||||
"6dB/oct Bandpass",
|
||||
"12dB/oct Bandpass",
|
||||
"18dB/oct Bandpass",
|
||||
"6dB/oct Bandreject",
|
||||
"12dB/oct Bandreject",
|
||||
"18dB/oct Bandreject",
|
||||
};
|
||||
|
||||
CALF_PORT_PROPS(filter) = {
|
||||
{ 2000, 10,20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ | PF_PROP_GRAPH, NULL, "freq", "Frequency" },
|
||||
{ 0.707, 0.707, 32, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "res", "Resonance" },
|
||||
{ biquad_filter_module::mode_12db_lp,
|
||||
biquad_filter_module::mode_12db_lp,
|
||||
biquad_filter_module::mode_count - 1,
|
||||
1, PF_ENUM | PF_CTL_COMBO, filter_choices, "mode", "Mode" },
|
||||
{ 20, 5, 100, 20, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "inertia", "Inertia"},
|
||||
};
|
||||
|
||||
CALF_PLUGIN_INFO(filter) = { 0x847f, "Filter", "Calf Filter", "Krzysztof Foltman", calf_plugins::calf_copyright_info, "FilterPlugin" };
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CALF_PORT_NAMES(filterclavier) = {"In L", "In R", "Out L", "Out R"};
|
||||
|
||||
CALF_PORT_PROPS(filterclavier) = {
|
||||
{ 0, -48, 48, 48*2+1, PF_INT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_SEMITONES, NULL, "transpose", "Transpose" },
|
||||
{ 0, -100, 100, 0, PF_INT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "detune", "Detune" },
|
||||
{ 32, 0.707, 32, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "maxres", "Max. Resonance" },
|
||||
{ biquad_filter_module::mode_6db_bp,
|
||||
biquad_filter_module::mode_12db_lp,
|
||||
biquad_filter_module::mode_count - 1,
|
||||
1, PF_ENUM | PF_CTL_COMBO | PF_PROP_GRAPH, filter_choices, "mode", "Mode" },
|
||||
{ 20, 1, 2000, 20, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "inertia", "Portamento time"}
|
||||
};
|
||||
|
||||
CALF_PLUGIN_INFO(filterclavier) = { 0x849f, "Filterclavier", "Calf Filterclavier", "Krzysztof Foltman / Hans Baier", calf_plugins::calf_copyright_info, "FilterclavierPlugin" };
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CALF_PORT_NAMES(vintage_delay) = {"In L", "In R", "Out L", "Out R"};
|
||||
|
||||
const char *vintage_delay_mixmodes[] = {
|
||||
"Stereo",
|
||||
"Ping-Pong",
|
||||
};
|
||||
|
||||
const char *vintage_delay_fbmodes[] = {
|
||||
"Plain",
|
||||
"Tape",
|
||||
"Old Tape",
|
||||
};
|
||||
|
||||
CALF_PORT_PROPS(vintage_delay) = {
|
||||
{ 120, 30, 300,2701, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_BPM, NULL, "bpm", "Tempo" },
|
||||
{ 4, 1, 16, 1, PF_INT | PF_SCALE_LINEAR | PF_CTL_FADER, NULL, "subdiv", "Subdivide"},
|
||||
{ 3, 1, 16, 1, PF_INT | PF_SCALE_LINEAR | PF_CTL_FADER, NULL, "time_l", "Time L"},
|
||||
{ 5, 1, 16, 1, PF_INT | PF_SCALE_LINEAR | PF_CTL_FADER, NULL, "time_r", "Time R"},
|
||||
{ 0.5, 0, 1, 0, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB, NULL, "feedback", "Feedback" },
|
||||
{ 0.25, 0, 4, 100, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "amount", "Amount" },
|
||||
{ 1, 0, 1, 0, PF_ENUM | PF_CTL_COMBO, vintage_delay_mixmodes, "mix_mode", "Mix mode" },
|
||||
{ 1, 0, 2, 0, PF_ENUM | PF_CTL_COMBO, vintage_delay_fbmodes, "medium", "Medium" },
|
||||
{ 1.0, 0, 4, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_NOBOUNDS, NULL, "dry", "Dry Amount" },
|
||||
};
|
||||
|
||||
CALF_PLUGIN_INFO(vintage_delay) = { 0x8482, "VintageDelay", "Calf Vintage Delay", "Krzysztof Foltman", calf_plugins::calf_copyright_info, "DelayPlugin" };
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CALF_PORT_NAMES(rotary_speaker) = {"In L", "In R", "Out L", "Out R"};
|
||||
|
||||
const char *rotary_speaker_speed_names[] = { "Off", "Chorale", "Tremolo", "HoldPedal", "ModWheel", "Manual" };
|
||||
|
||||
CALF_PORT_PROPS(rotary_speaker) = {
|
||||
{ 2, 0, 5, 1.01, PF_ENUM | PF_CTL_COMBO, rotary_speaker_speed_names, "vib_speed", "Speed Mode" },
|
||||
{ 0.5, 0, 1, 0, PF_FLOAT | PF_CTL_KNOB | PF_SCALE_PERC, NULL, "spacing", "Tap Spacing" },
|
||||
{ 0.5, 0, 1, 0, PF_FLOAT | PF_CTL_KNOB | PF_SCALE_PERC, NULL, "shift", "Tap Offset" },
|
||||
{ 0.10, 0, 1, 0, PF_FLOAT | PF_CTL_KNOB | PF_SCALE_PERC, NULL, "mod_depth", "Mod Depth" },
|
||||
{ 390, 10, 600, 0, PF_FLOAT | PF_CTL_KNOB | PF_SCALE_LOG | PF_UNIT_RPM, NULL, "treble_speed", "Treble Motor" },
|
||||
{ 410, 10, 600, 0, PF_FLOAT | PF_CTL_KNOB | PF_SCALE_LOG | PF_UNIT_RPM, NULL, "bass_speed", "Bass Motor" },
|
||||
{ 0.7, 0, 1, 101, PF_FLOAT | PF_CTL_KNOB | PF_SCALE_PERC, NULL, "mic_distance", "Mic Distance" },
|
||||
{ 0.3, 0, 1, 101, PF_FLOAT | PF_CTL_KNOB | PF_SCALE_PERC, NULL, "reflection", "Reflection" },
|
||||
};
|
||||
|
||||
CALF_PLUGIN_INFO(rotary_speaker) = { 0x8483, "RotarySpeaker", "Calf Rotary Speaker", "Krzysztof Foltman", calf_plugins::calf_copyright_info, "SimulationPlugin" };
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CALF_PORT_NAMES(multichorus) = {"In L", "In R", "Out L", "Out R"};
|
||||
|
||||
CALF_PORT_PROPS(multichorus) = {
|
||||
{ 5, 0.1, 10, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC | PF_PROP_GRAPH, NULL, "min_delay", "Minimum delay" },
|
||||
{ 6, 0.1, 10, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC| PF_PROP_GRAPH, NULL, "mod_depth", "Modulation depth" },
|
||||
{ 0.5, 0.01, 20, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ| PF_PROP_GRAPH, NULL, "mod_rate", "Modulation rate" },
|
||||
{ 180, 0, 360, 91, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_DEG, NULL, "stereo", "Stereo phase" },
|
||||
{ 4, 1, 8, 8, PF_INT | PF_SCALE_LINEAR | PF_CTL_FADER, NULL, "voices", "Voices"},
|
||||
{ 64, 0, 360, 91, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_DEG, NULL, "vphase", "Inter-voice phase" },
|
||||
{ 2, 0, 4, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_NOBOUNDS, NULL, "amount", "Amount" },
|
||||
{ 1.0, 0, 4, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_NOBOUNDS, NULL, "dry", "Dry Amount" },
|
||||
{ 100, 10,20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ | PF_PROP_GRAPH, NULL, "freq", "Center Frq 1" },
|
||||
{ 5000, 10,20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ | PF_PROP_GRAPH, NULL, "freq2", "Center Frq 2" },
|
||||
{ 0.125, 0.125, 8, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "q", "Q" },
|
||||
{ 1, 0, 1, 0, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB, NULL, "overlap", "Overlap" },
|
||||
};
|
||||
|
||||
CALF_PLUGIN_INFO(multichorus) = { 0x8501, "MultiChorus", "Calf MultiChorus", "Krzysztof Foltman", calf_plugins::calf_copyright_info, "ChorusPlugin" };
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CALF_PORT_NAMES(compressor) = {"In L", "In R", "Out L", "Out R"};
|
||||
|
||||
const char *compressor_detection_names[] = { "RMS", "Peak" };
|
||||
const char *compressor_stereo_link_names[] = { "Average", "Maximum" };
|
||||
const char *compressor_weighting_names[] = { "Normal", "A-weighted", "Deesser (low)", "Deesser (med)", "Deesser (high)" };
|
||||
|
||||
CALF_PORT_PROPS(compressor) = {
|
||||
{ 0.125, 0.000976563, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "threshold", "Threshold" },
|
||||
{ 2, 1, 20, 21, PF_FLOAT | PF_SCALE_LOG_INF | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "ratio", "Ratio" },
|
||||
{ 20, 0.01, 2000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "attack", "Attack" },
|
||||
{ 250, 0.01, 2000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "release", "Release" },
|
||||
{ 2, 1, 64, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "makeup", "Makeup Gain" },
|
||||
{ 2.828427125, 1, 8, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "knee", "Knee" },
|
||||
{ 0, 0, 1, 0, PF_ENUM | PF_CTL_COMBO, compressor_detection_names, "detection", "Detection" },
|
||||
{ 0, 0, 1, 0, PF_ENUM | PF_CTL_COMBO, compressor_stereo_link_names, "stereo_link", "Stereo Link" },
|
||||
{ 0, 0, 4, 0, PF_ENUM | PF_CTL_COMBO, compressor_weighting_names, "aweighting", "Weighting" },
|
||||
{ 0, 0.03125, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_CTLO_REVERSE | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL| PF_PROP_GRAPH, NULL, "compression", "Compression" },
|
||||
{ 0, 0, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "peak", "Peak Output" },
|
||||
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip", "0dB" },
|
||||
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "bypass", "Bypass" },
|
||||
// { 2000, 10,20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ | PF_PROP_GRAPH, NULL, "deess_freq", "Frequency" },
|
||||
// { 0.707, 0.707, 32, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "deess_res", "Q" },
|
||||
};
|
||||
|
||||
CALF_PLUGIN_INFO(compressor) = { 0x8502, "Compressor", "Calf Compressor", "Thor Harald Johansen", calf_plugins::calf_copyright_info, "CompressorPlugin" };
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CALF_PORT_NAMES(monosynth) = {
|
||||
"Out L", "Out R",
|
||||
};
|
||||
|
||||
const char *monosynth_waveform_names[] = { "Sawtooth", "Square", "Pulse", "Sine", "Triangle", "Varistep", "Skewed Saw", "Skewed Square",
|
||||
"Smooth Brass", "Bass", "Dark FM", "Multiwave", "Bell FM", "Dark Pad", "DCO Saw", "DCO Maze" };
|
||||
const char *monosynth_mode_names[] = { "0 : 0", "0 : 180", "0 : 90", "90 : 90", "90 : 270", "Random" };
|
||||
const char *monosynth_legato_names[] = { "Retrig", "Legato", "Fng Retrig", "Fng Legato" };
|
||||
|
||||
const char *monosynth_filter_choices[] = {
|
||||
"12dB/oct Lowpass",
|
||||
"24dB/oct Lowpass",
|
||||
"2x12dB/oct Lowpass",
|
||||
"12dB/oct Highpass",
|
||||
"Lowpass+Notch",
|
||||
"Highpass+Notch",
|
||||
"6dB/oct Bandpass",
|
||||
"2x6dB/oct Bandpass",
|
||||
};
|
||||
|
||||
CALF_PLUGIN_INFO(monosynth) = { 0x8480, "Monosynth", "Calf Monosynth", "Krzysztof Foltman", calf_plugins::calf_copyright_info, "SynthesizerPlugin" };
|
||||
|
||||
CALF_PORT_PROPS(monosynth) = {
|
||||
{ monosynth_metadata::wave_saw, 0, monosynth_metadata::wave_count - 1, 1, PF_ENUM | PF_CTL_COMBO | PF_PROP_GRAPH, monosynth_waveform_names, "o1_wave", "Osc1 Wave" },
|
||||
{ monosynth_metadata::wave_sqr, 0, monosynth_metadata::wave_count - 1, 1, PF_ENUM | PF_CTL_COMBO | PF_PROP_GRAPH, monosynth_waveform_names, "o2_wave", "Osc2 Wave" },
|
||||
|
||||
{ 0, -1, 1, 0.1, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB, NULL, "o1_pw", "Osc1 PW" },
|
||||
{ 0, -1, 1, 0.1, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB, NULL, "o2_pw", "Osc2 PW" },
|
||||
|
||||
{ 10, 0, 100, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "o12_detune", "O1<>2 Detune" },
|
||||
{ 12, -24, 24, 0, PF_INT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_SEMITONES, NULL, "o2_xpose", "Osc 2 transpose" },
|
||||
{ 0, 0, 5, 0, PF_ENUM | PF_CTL_COMBO, monosynth_mode_names, "phase_mode", "Phase mode" },
|
||||
{ 0.5, 0, 1, 0, PF_FLOAT | PF_SCALE_PERC, NULL, "o12_mix", "O1<>2 Mix" },
|
||||
{ 1, 0, 7, 0, PF_ENUM | PF_CTL_COMBO | PF_PROP_GRAPH, monosynth_filter_choices, "filter", "Filter" },
|
||||
{ 33, 10,16000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ, NULL, "cutoff", "Cutoff" },
|
||||
{ 2, 0.7, 8, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB, NULL, "res", "Resonance" },
|
||||
{ 0, -2400, 2400, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "filter_sep", "Separation" },
|
||||
{ 8000, -10800,10800, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "env2cutoff", "Env->Cutoff" },
|
||||
{ 1, 0, 1, 0, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB, NULL, "env2res", "Env->Res" },
|
||||
{ 1, 0, 1, 0, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB, NULL, "env2amp", "Env->Amp" },
|
||||
|
||||
{ 1, 1,20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "adsr_a", "Attack" },
|
||||
{ 350, 10,20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "adsr_d", "Decay" },
|
||||
{ 0.5, 0, 1, 0, PF_FLOAT | PF_SCALE_PERC, NULL, "adsr_s", "Sustain" },
|
||||
{ 0, -10000,10000, 21, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "adsr_f", "Fade" },
|
||||
{ 50, 10,20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "adsr_r", "Release" },
|
||||
|
||||
{ 0, 0, 2, 0, PF_FLOAT | PF_SCALE_PERC, NULL, "key_follow", "Key Follow" },
|
||||
{ 0, 0, 3, 0, PF_ENUM | PF_CTL_COMBO, monosynth_legato_names, "legato", "Legato Mode" },
|
||||
{ 1, 1, 2000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "portamento", "Portamento" },
|
||||
|
||||
{ 0.5, 0, 1, 0.1, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB, NULL, "vel2filter", "Vel->Filter" },
|
||||
{ 0, 0, 1, 0.1, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB, NULL, "vel2amp", "Vel->Amp" },
|
||||
|
||||
{ 0.5, 0, 1, 100, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_PROP_OUTPUT_GAIN, NULL, "master", "Volume" },
|
||||
|
||||
{ 200, 0, 2400, 25, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "pbend_range", "PBend Range" },
|
||||
|
||||
{ 5, 0.01, 20, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ, NULL, "lfo_rate", "LFO Rate" },
|
||||
{ 0.5, 0.1, 5, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_SEC, NULL, "lfo_delay", "LFO Delay" },
|
||||
{ 0, -4800, 4800, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "lfo2filter", "LFO->Filter" },
|
||||
{ 100, 0, 1200, 0, PF_FLOAT | PF_SCALE_QUAD | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "lfo2pitch", "LFO->Pitch" },
|
||||
{ 0, 0, 1, 0.1, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB, NULL, "lfo2pw", "LFO->PW" },
|
||||
{ 1, 0, 1, 0.1, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB, NULL, "mwhl2lfo", "ModWheel->LFO" },
|
||||
|
||||
{ 1, 0, 1, 0, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB, NULL, "scale_detune", "Scale Detune" },
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CALF_PLUGIN_INFO(organ) = { 0x8481, "Organ", "Calf Organ", "Krzysztof Foltman", calf_plugins::calf_copyright_info, "SynthesizerPlugin" };
|
||||
|
||||
const char **organ_metadata::get_default_configure_vars()
|
||||
{
|
||||
static const char *data[] = { "map_curve", "2\n0 1\n1 1\n", NULL };
|
||||
return data;
|
||||
}
|
||||
|
||||
plugin_command_info *organ_metadata::get_commands()
|
||||
{
|
||||
static plugin_command_info cmds[] = {
|
||||
{ "cmd_panic", "Panic!", "Stop all sounds and reset all controllers" },
|
||||
{ NULL }
|
||||
};
|
||||
return cmds;
|
||||
}
|
||||
|
||||
CALF_PORT_NAMES(organ) = {"Out L", "Out R"};
|
||||
|
||||
const char *organ_percussion_trigger_names[] = { "First note", "Each note", "Each, no retrig", "Polyphonic" };
|
||||
|
||||
const char *organ_wave_names[] = {
|
||||
"Sin",
|
||||
"S0", "S00", "S000",
|
||||
"SSaw", "SSqr", "SPls",
|
||||
"Saw", "Sqr", "Pls",
|
||||
"S(", "Sq(", "S+", "Clvg",
|
||||
"Bell", "Bell2",
|
||||
"W1", "W2", "W3", "W4", "W5", "W6", "W7", "W8", "W9",
|
||||
"DSaw", "DSqr", "DPls",
|
||||
"P:SynStr","P:WideStr","P:Sine","P:Bell","P:Space","P:Voice","P:Hiss","P:Chant",
|
||||
};
|
||||
|
||||
const char *organ_routing_names[] = { "Out", "Flt 1", "Flt 2" };
|
||||
|
||||
const char *organ_ampctl_names[] = { "None", "Direct", "Flt 1", "Flt 2", "All" };
|
||||
|
||||
const char *organ_vibrato_mode_names[] = { "None", "Direct", "Flt 1", "Flt 2", "Voice", "Global" };
|
||||
|
||||
const char *organ_filter_type_names[] = { "12dB/oct LP", "12dB/oct HP" };
|
||||
|
||||
const char *organ_filter_send_names[] = { "Output", "Filter 2" };
|
||||
|
||||
const char *organ_init_map_curve = "2\n0 1\n1 1\n";
|
||||
|
||||
CALF_PORT_PROPS(organ) = {
|
||||
{ 8, 0, 8, 80, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_FADER, NULL, "l1", "16'" },
|
||||
{ 8, 0, 8, 80, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_FADER, NULL, "l2", "5 1/3'" },
|
||||
{ 8, 0, 8, 80, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_FADER, NULL, "l3", "8'" },
|
||||
{ 0, 0, 8, 80, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_FADER, NULL, "l4", "4'" },
|
||||
{ 0, 0, 8, 80, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_FADER, NULL, "l5", "2 2/3'" },
|
||||
{ 0, 0, 8, 80, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_FADER, NULL, "l6", "2'" },
|
||||
{ 0, 0, 8, 80, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_FADER, NULL, "l7", "1 3/5'" },
|
||||
{ 0, 0, 8, 80, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_FADER, NULL, "l8", "1 1/3'" },
|
||||
{ 8, 0, 8, 80, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_FADER, NULL, "l9", "1'" },
|
||||
|
||||
{ 1, 1, 32, 32, PF_INT | PF_SCALE_LINEAR | PF_CTL_KNOB, NULL, "f1", "Freq 1" },
|
||||
{ 3, 1, 32, 32, PF_INT | PF_SCALE_LINEAR | PF_CTL_KNOB, NULL, "f2", "Freq 2" },
|
||||
{ 2, 1, 32, 32, PF_INT | PF_SCALE_LINEAR | PF_CTL_KNOB, NULL, "f3", "Freq 3" },
|
||||
{ 4, 1, 32, 32, PF_INT | PF_SCALE_LINEAR | PF_CTL_KNOB, NULL, "f4", "Freq 4" },
|
||||
{ 6, 1, 32, 32, PF_INT | PF_SCALE_LINEAR | PF_CTL_KNOB, NULL, "f5", "Freq 5" },
|
||||
{ 8, 1, 32, 32, PF_INT | PF_SCALE_LINEAR | PF_CTL_KNOB, NULL, "f6", "Freq 6" },
|
||||
{ 10, 1, 32, 32, PF_INT | PF_SCALE_LINEAR | PF_CTL_KNOB, NULL, "f7", "Freq 7" },
|
||||
{ 12, 1, 32, 32, PF_INT | PF_SCALE_LINEAR | PF_CTL_KNOB, NULL, "f8", "Freq 8" },
|
||||
{ 16, 1, 32, 32, PF_INT | PF_SCALE_LINEAR | PF_CTL_KNOB, NULL, "f9", "Freq 9" },
|
||||
|
||||
{ 0, 0, organ_enums::wave_count - 1, 0, PF_ENUM | PF_SCALE_LINEAR | PF_CTL_COMBO, organ_wave_names, "w1", "Wave 1" },
|
||||
{ 0, 0, organ_enums::wave_count - 1, 0, PF_ENUM | PF_SCALE_LINEAR | PF_CTL_COMBO, organ_wave_names, "w2", "Wave 2" },
|
||||
{ 0, 0, organ_enums::wave_count - 1, 0, PF_ENUM | PF_SCALE_LINEAR | PF_CTL_COMBO, organ_wave_names, "w3", "Wave 3" },
|
||||
{ 0, 0, organ_enums::wave_count - 1, 0, PF_ENUM | PF_SCALE_LINEAR | PF_CTL_COMBO, organ_wave_names, "w4", "Wave 4" },
|
||||
{ 0, 0, organ_enums::wave_count - 1, 0, PF_ENUM | PF_SCALE_LINEAR | PF_CTL_COMBO, organ_wave_names, "w5", "Wave 5" },
|
||||
{ 0, 0, organ_enums::wave_count - 1, 0, PF_ENUM | PF_SCALE_LINEAR | PF_CTL_COMBO, organ_wave_names, "w6", "Wave 6" },
|
||||
{ 0, 0, organ_enums::wave_count - 1, 0, PF_ENUM | PF_SCALE_LINEAR | PF_CTL_COMBO, organ_wave_names, "w7", "Wave 7" },
|
||||
{ 0, 0, organ_enums::wave_count - 1, 0, PF_ENUM | PF_SCALE_LINEAR | PF_CTL_COMBO, organ_wave_names, "w8", "Wave 8" },
|
||||
{ 0, 0, organ_enums::wave_count - 1, 0, PF_ENUM | PF_SCALE_LINEAR | PF_CTL_COMBO, organ_wave_names, "w9", "Wave 9" },
|
||||
|
||||
{ 0, -100,100, 401, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "detune1", "Detune 1" },
|
||||
{ 0, -100,100, 401, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "detune2", "Detune 2" },
|
||||
{ 0, -100,100, 401, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "detune3", "Detune 3" },
|
||||
{ 0, -100,100, 401, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "detune4", "Detune 4" },
|
||||
{ 0, -100,100, 401, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "detune5", "Detune 5" },
|
||||
{ 0, -100,100, 401, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "detune6", "Detune 6" },
|
||||
{ 0, -100,100, 401, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "detune7", "Detune 7" },
|
||||
{ 0, -100,100, 401, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "detune8", "Detune 8" },
|
||||
{ 0, -100,100, 401, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "detune9", "Detune 9" },
|
||||
|
||||
{ 0, 0,360, 361, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_DEG, NULL, "phase1", "Phase 1" },
|
||||
{ 0, 0,360, 361, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_DEG, NULL, "phase2", "Phase 2" },
|
||||
{ 0, 0,360, 361, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_DEG, NULL, "phase3", "Phase 3" },
|
||||
{ 0, 0,360, 361, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_DEG, NULL, "phase4", "Phase 4" },
|
||||
{ 0, 0,360, 361, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_DEG, NULL, "phase5", "Phase 5" },
|
||||
{ 0, 0,360, 361, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_DEG, NULL, "phase6", "Phase 6" },
|
||||
{ 0, 0,360, 361, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_DEG, NULL, "phase7", "Phase 7" },
|
||||
{ 0, 0,360, 361, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_DEG, NULL, "phase8", "Phase 8" },
|
||||
{ 0, 0,360, 361, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_DEG, NULL, "phase9", "Phase 9" },
|
||||
|
||||
{ 0, -1, 1, 201, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB, NULL, "pan1", "Pan 1" },
|
||||
{ 0, -1, 1, 201, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB, NULL, "pan2", "Pan 2" },
|
||||
{ 0, -1, 1, 201, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB, NULL, "pan3", "Pan 3" },
|
||||
{ 0, -1, 1, 201, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB, NULL, "pan4", "Pan 4" },
|
||||
{ 0, -1, 1, 201, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB, NULL, "pan5", "Pan 5" },
|
||||
{ 0, -1, 1, 201, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB, NULL, "pan6", "Pan 6" },
|
||||
{ 0, -1, 1, 201, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB, NULL, "pan7", "Pan 7" },
|
||||
{ 0, -1, 1, 201, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB, NULL, "pan8", "Pan 8" },
|
||||
{ 0, -1, 1, 201, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB, NULL, "pan9", "Pan 9" },
|
||||
|
||||
{ 0, 0, 2, 0, PF_ENUM | PF_SCALE_LINEAR | PF_CTL_COMBO, organ_routing_names, "routing1", "Routing 1" },
|
||||
{ 0, 0, 2, 0, PF_ENUM | PF_SCALE_LINEAR | PF_CTL_COMBO, organ_routing_names, "routing2", "Routing 2" },
|
||||
{ 0, 0, 2, 0, PF_ENUM | PF_SCALE_LINEAR | PF_CTL_COMBO, organ_routing_names, "routing3", "Routing 3" },
|
||||
{ 0, 0, 2, 0, PF_ENUM | PF_SCALE_LINEAR | PF_CTL_COMBO, organ_routing_names, "routing4", "Routing 4" },
|
||||
{ 0, 0, 2, 0, PF_ENUM | PF_SCALE_LINEAR | PF_CTL_COMBO, organ_routing_names, "routing5", "Routing 5" },
|
||||
{ 0, 0, 2, 0, PF_ENUM | PF_SCALE_LINEAR | PF_CTL_COMBO, organ_routing_names, "routing6", "Routing 6" },
|
||||
{ 0, 0, 2, 0, PF_ENUM | PF_SCALE_LINEAR | PF_CTL_COMBO, organ_routing_names, "routing7", "Routing 7" },
|
||||
{ 0, 0, 2, 0, PF_ENUM | PF_SCALE_LINEAR | PF_CTL_COMBO, organ_routing_names, "routing8", "Routing 8" },
|
||||
{ 0, 0, 2, 0, PF_ENUM | PF_SCALE_LINEAR | PF_CTL_COMBO, organ_routing_names, "routing9", "Routing 9" },
|
||||
|
||||
{ 96, 0, 127, 128, PF_INT | PF_CTL_KNOB | PF_UNIT_NOTE, NULL, "foldnote", "Foldover" },
|
||||
|
||||
{ 200, 10, 3000, 100, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "perc_decay", "P: Carrier Decay" },
|
||||
{ 0.25, 0, 1, 100, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB, NULL, "perc_level", "P: Level" },
|
||||
{ 0, 0, organ_enums::wave_count_small - 1, 1, PF_ENUM | PF_CTL_COMBO, organ_wave_names, "perc_waveform", "P: Carrier Wave" },
|
||||
{ 6, 1, 32, 32, PF_INT | PF_SCALE_LINEAR | PF_CTL_KNOB, NULL, "perc_harmonic", "P: Carrier Frq" },
|
||||
{ 0, 0, 1, 0, PF_FLOAT | PF_SCALE_PERC, NULL, "perc_vel2amp", "P: Vel->Amp" },
|
||||
|
||||
{ 200, 10, 3000, 100, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "perc_fm_decay", "P: Modulator Decay" },
|
||||
{ 0, 0, 4, 0, PF_FLOAT | PF_SCALE_PERC, NULL, "perc_fm_depth", "P: FM Depth" },
|
||||
{ 0, 0, organ_enums::wave_count_small - 1, 1, PF_ENUM | PF_CTL_COMBO, organ_wave_names, "perc_fm_waveform", "P: Modulator Wave" },
|
||||
{ 6, 1, 32, 32, PF_INT | PF_SCALE_LINEAR | PF_CTL_KNOB, NULL, "perc_fm_harmonic", "P: Modulator Frq" },
|
||||
{ 0, 0, 1, 0, PF_FLOAT | PF_SCALE_PERC, NULL, "perc_vel2fm", "P: Vel->FM" },
|
||||
|
||||
{ 0, 0, organ_enums::perctrig_count - 1, 0, PF_ENUM | PF_CTL_COMBO, organ_percussion_trigger_names, "perc_trigger", "P: Trigger" },
|
||||
{ 90, 0,360, 361, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_DEG, NULL, "perc_stereo", "P: Stereo Phase" },
|
||||
|
||||
{ 0, 0, 1, 0, PF_ENUM | PF_CTL_COMBO, organ_filter_send_names, "filter_chain", "Filter 1 To" },
|
||||
{ 0, 0, 1, 0, PF_ENUM | PF_CTL_COMBO, organ_filter_type_names, "filter1_type", "Filter 1 Type" },
|
||||
{ 0.1, 0, 1, 100, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_PROP_OUTPUT_GAIN | PF_PROP_GRAPH, NULL, "master", "Volume" },
|
||||
|
||||
{ 2000, 20, 20000, 100, PF_FLOAT | PF_SCALE_LOG | PF_UNIT_HZ | PF_CTL_KNOB, NULL, "f1_cutoff", "F1 Cutoff" },
|
||||
{ 2, 0.7, 8, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB, NULL, "f1_res", "F1 Res" },
|
||||
{ 8000, -10800,10800, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "f1_env1", "F1 Env1" },
|
||||
{ 0, -10800,10800, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "f1_env2", "F1 Env2" },
|
||||
{ 0, -10800,10800, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "f1_env3", "F1 Env3" },
|
||||
{ 0, 0, 2, 0, PF_FLOAT | PF_SCALE_PERC, NULL, "f1_keyf", "F1 KeyFollow" },
|
||||
|
||||
{ 2000, 20, 20000, 100, PF_FLOAT | PF_SCALE_LOG | PF_UNIT_HZ | PF_CTL_KNOB, NULL, "f2_cutoff", "F2 Cutoff" },
|
||||
{ 2, 0.7, 8, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB, NULL, "f2_res", "F2 Res" },
|
||||
{ 0, -10800,10800, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "f2_env1", "F2 Env1" },
|
||||
{ 8000, -10800,10800, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "f2_env2", "F2 Env2" },
|
||||
{ 0, -10800,10800, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "f2_env3", "F2 Env3" },
|
||||
{ 0, 0, 2, 0, PF_FLOAT | PF_SCALE_PERC, NULL, "f2_keyf", "F2 KeyFollow" },
|
||||
|
||||
{ 1, 1,20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "adsr_a", "EG1 Attack" },
|
||||
{ 350, 10,20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "adsr_d", "EG1 Decay" },
|
||||
{ 0.5, 0, 1, 0, PF_FLOAT | PF_SCALE_PERC, NULL, "adsr_s", "EG1 Sustain" },
|
||||
{ 50, 10,20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "adsr_r", "EG1 Release" },
|
||||
{ 0, 0, 1, 0, PF_FLOAT | PF_SCALE_PERC, NULL, "adsr_v", "EG1 VelMod" },
|
||||
{ 0, 0, organ_enums::ampctl_count - 1,
|
||||
0, PF_INT | PF_CTL_COMBO, organ_ampctl_names, "eg1_amp_ctl", "EG1 To Amp"},
|
||||
|
||||
{ 1, 1,20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "adsr2_a", "EG2 Attack" },
|
||||
{ 350, 10,20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "adsr2_d", "EG2 Decay" },
|
||||
{ 0.5, 0, 1, 0, PF_FLOAT | PF_SCALE_PERC, NULL, "adsr2_s", "EG2 Sustain" },
|
||||
{ 50, 10,20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "adsr2_r", "EG2 Release" },
|
||||
{ 0, 0, 1, 0, PF_FLOAT | PF_SCALE_PERC, NULL, "adsr2_v", "EG2 VelMod" },
|
||||
{ 0, 0, organ_enums::ampctl_count - 1,
|
||||
0, PF_INT | PF_CTL_COMBO, organ_ampctl_names, "eg2_amp_ctl", "EG2 To Amp"},
|
||||
|
||||
{ 1, 1,20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "adsr3_a", "EG3 Attack" },
|
||||
{ 350, 10,20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "adsr3_d", "EG3 Decay" },
|
||||
{ 0.5, 0, 1, 0, PF_FLOAT | PF_SCALE_PERC, NULL, "adsr3_s", "EG3 Sustain" },
|
||||
{ 50, 10,20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "adsr3_r", "EG3 Release" },
|
||||
{ 0, 0, 1, 0, PF_FLOAT | PF_SCALE_PERC, NULL, "adsr3_v", "EG3 VelMod" },
|
||||
{ 0, 0, organ_enums::ampctl_count - 1,
|
||||
0, PF_INT | PF_CTL_COMBO, organ_ampctl_names, "eg3_amp_ctl", "EG3 To Amp"},
|
||||
|
||||
{ 6.6, 0.01, 80, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ, NULL, "vib_rate", "Vib Rate" },
|
||||
{ 0.5, 0, 1, 0, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB , NULL, "vib_amt", "Vib Mod Amt" },
|
||||
{ 0.5, 0, 1, 0, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB , NULL, "vib_wet", "Vib Wet" },
|
||||
{ 180, 0, 360, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_DEG, NULL, "vib_phase", "Vib Stereo" },
|
||||
{ organ_enums::lfomode_global, 0, organ_enums::lfomode_count - 1, 0, PF_ENUM | PF_CTL_COMBO, organ_vibrato_mode_names, "vib_mode", "Vib Mode" },
|
||||
// { 0, 0, organ_enums::ampctl_count - 1,
|
||||
// 0, PF_INT | PF_CTL_COMBO, organ_ampctl_names, "vel_amp_ctl", "Vel To Amp"},
|
||||
|
||||
{ -12, -24, 24, 49, PF_INT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_SEMITONES, NULL, "transpose", "Transpose" },
|
||||
{ 0, -100, 100, 201, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "detune", "Detune" },
|
||||
|
||||
{ 16, 1, 32, 32, PF_INT | PF_SCALE_LINEAR | PF_CTL_KNOB, NULL, "polyphony", "Polyphony" },
|
||||
|
||||
{ 1, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "quad_env", "Quadratic AmpEnv" },
|
||||
|
||||
{ 200, 0, 2400, 25, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "pbend_range", "PBend Range" },
|
||||
|
||||
{ 80, 20, 20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ, NULL, "bass_freq", "Bass Freq" },
|
||||
{ 1, 0.1, 10, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_NOBOUNDS, NULL, "bass_gain", "Bass Gain" },
|
||||
{ 12000, 20, 20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ, NULL, "treble_freq", "Treble Freq" },
|
||||
{ 1, 0.1, 10, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_NOBOUNDS, NULL, "treble_gain", "Treble Gain" },
|
||||
|
||||
{ 0, 0, 0, 0, PF_STRING | PF_PROP_MSGCONTEXT, &organ_init_map_curve, "map_curve", "Key mapping curve" },
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const char *fluidsynth_init_soundfont = "";
|
||||
|
||||
const char *fluidsynth_interpolation_names[] = { "None (zero-hold)", "Linear", "Cubic", "7-point" };
|
||||
|
||||
CALF_PORT_NAMES(fluidsynth) = {
|
||||
"Out L", "Out R",
|
||||
};
|
||||
|
||||
CALF_PLUGIN_INFO(fluidsynth) = { 0x8700, "Fluidsynth", "Calf Fluidsynth", "FluidSynth Team / Krzysztof Foltman", calf_plugins::calf_copyright_info, "SynthesizerPlugin" };
|
||||
|
||||
CALF_PORT_PROPS(fluidsynth) = {
|
||||
{ 0.5, 0, 1, 100, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_PROP_OUTPUT_GAIN, NULL, "master", "Volume" },
|
||||
{ 0, 0, 0, 0, PF_STRING | PF_PROP_MSGCONTEXT, &fluidsynth_init_soundfont, "soundfont", "Soundfont" },
|
||||
{ 2, 0, 3, 0, PF_ENUM | PF_CTL_COMBO, fluidsynth_interpolation_names, "interpolation", "Interpolation" },
|
||||
{ 1, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "reverb", "Enable Reverb" },
|
||||
{ 1, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "chorus", "Enable Chorus" },
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const char *wavetable_names[] = {
|
||||
"Shiny1",
|
||||
"Shiny2",
|
||||
"Rezo",
|
||||
"Metal",
|
||||
"Bell",
|
||||
"Blah",
|
||||
"Pluck",
|
||||
"Stretch",
|
||||
"Stretch 2",
|
||||
"Hard Sync",
|
||||
"Hard Sync 2",
|
||||
"Soft Sync",
|
||||
"Bell 2",
|
||||
"Bell 3",
|
||||
"Tine",
|
||||
"Tine 2",
|
||||
"Clav",
|
||||
"Clav 2",
|
||||
"Gtr",
|
||||
"Gtr 2",
|
||||
"Gtr 3",
|
||||
"Gtr 4",
|
||||
"Gtr 5",
|
||||
"Reed",
|
||||
"Reed 2",
|
||||
"Silver",
|
||||
"Brass",
|
||||
"Multi",
|
||||
"Multi 2",
|
||||
};
|
||||
|
||||
const char *wavetable_init_soundfont = "";
|
||||
|
||||
CALF_PORT_NAMES(wavetable) = {
|
||||
"Out L", "Out R",
|
||||
};
|
||||
|
||||
CALF_PLUGIN_INFO(wavetable) = { 0x8701, "Wavetable", "Calf Wavetable", "Krzysztof Foltman", calf_plugins::calf_copyright_info, "SynthesizerPlugin" };
|
||||
|
||||
CALF_PORT_PROPS(wavetable) = {
|
||||
{ wavetable_metadata::wt_count - 1, 0, wavetable_metadata::wt_count - 1, 0, PF_ENUM | PF_SCALE_LINEAR | PF_CTL_COMBO, wavetable_names, "o1wave", "Osc1 Wave" },
|
||||
{ 0.2, -1, 1, 0, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "o1offset", "Osc1 Ctl"},
|
||||
{ 0, -48, 48, 48*2+1, PF_INT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_SEMITONES, NULL, "o1trans", "Osc1 Transpose" },
|
||||
{ 6, -100, 100, 0, PF_INT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "o1detune", "Osc1 Detune" },
|
||||
{ 0.1, 0, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_NOBOUNDS, NULL, "o1level", "Osc1 Level" },
|
||||
|
||||
{ 0, 0, wavetable_metadata::wt_count - 1, 0, PF_ENUM | PF_SCALE_LINEAR | PF_CTL_COMBO, wavetable_names, "o2wave", "Osc2 Wave" },
|
||||
{ 0.4, -1, 1, 0, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "o2offset", "Osc2 Ctl"},
|
||||
{ 0, -48, 48, 48*2+1, PF_INT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_SEMITONES, NULL, "o2trans", "Osc2 Transpose" },
|
||||
{ -6, -100, 100, 0, PF_INT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "o2detune", "Osc2 Detune" },
|
||||
{ 0, 0, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_NOBOUNDS, NULL, "o2level", "Osc2 Level" },
|
||||
|
||||
{ 1, 1,20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "adsr_a", "EG1 Attack" },
|
||||
{ 350, 10,20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "adsr_d", "EG1 Decay" },
|
||||
{ 0.5, 0, 1, 0, PF_FLOAT | PF_SCALE_PERC, NULL, "adsr_s", "EG1 Sustain" },
|
||||
{ 0, -10000,10000, 21, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "adsr_f", "EG1 Fade" },
|
||||
{ 50, 10,20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "adsr_r", "EG1 Release" },
|
||||
{ 1, 0, 1, 0, PF_FLOAT | PF_SCALE_PERC, NULL, "adsr_v", "EG1 VelMod" },
|
||||
|
||||
{ 1, 1,20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "adsr2_a", "EG2 Attack" },
|
||||
{ 350, 10,20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "adsr2_d", "EG2 Decay" },
|
||||
{ 0.5, 0, 1, 0, PF_FLOAT | PF_SCALE_PERC, NULL, "adsr2_s", "EG2 Sustain" },
|
||||
{ 0, -10000,10000, 21, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "adsr2_f", "EG2 Fade" },
|
||||
{ 50, 10,20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "adsr2_r", "EG2 Release" },
|
||||
{ 1, 0, 1, 0, PF_FLOAT | PF_SCALE_PERC, NULL, "adsr2_v", "EG2 VelMod" },
|
||||
|
||||
{ 1, 1,20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "adsr3_a", "EG3 Attack" },
|
||||
{ 350, 10,20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "adsr3_d", "EG3 Decay" },
|
||||
{ 0.5, 0, 1, 0, PF_FLOAT | PF_SCALE_PERC, NULL, "adsr3_s", "EG3 Sustain" },
|
||||
{ 0, -10000,10000, 21, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "adsr3_f", "EG3 Fade" },
|
||||
{ 50, 10, 20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "adsr3_r", "EG3 Release" },
|
||||
{ 0, 0, 1, 0, PF_FLOAT | PF_SCALE_PERC, NULL, "adsr3_v", "EG3 VelMod" },
|
||||
|
||||
{ 200, 0, 2400, 25, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "pbend_range", "PBend Range" },
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void calf_plugins::get_all_plugins(std::vector<plugin_metadata_iface *> &plugins)
|
||||
{
|
||||
#define PER_MODULE_ITEM(name, isSynth, jackname) plugins.push_back(new name##_metadata);
|
||||
#define PER_SMALL_MODULE_ITEM(...)
|
||||
#include <calf/modulelist.h>
|
||||
}
|
||||
|
||||
638
plugins/ladspa_effect/calf/modules_dsp.cpp
Normal file
638
plugins/ladspa_effect/calf/modules_dsp.cpp
Normal file
@@ -0,0 +1,638 @@
|
||||
/* Calf DSP Library
|
||||
* Example audio modules - DSP code
|
||||
*
|
||||
* Copyright (C) 2001-2008 Krzysztof Foltman
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <memory.h>
|
||||
#if USE_JACK
|
||||
#include <jack/jack.h>
|
||||
#endif
|
||||
#include <calf/giface.h>
|
||||
#include <calf/modules.h>
|
||||
#include <calf/modules_dev.h>
|
||||
|
||||
using namespace dsp;
|
||||
using namespace calf_plugins;
|
||||
|
||||
/// convert amplitude value to normalized grid-ish value (0dB = 0.5, 30dB = 1.0, -30 dB = 0.0, -60dB = -0.5, -90dB = -1.0)
|
||||
static inline float dB_grid(float amp)
|
||||
{
|
||||
return log(amp) * (1.0 / log(256.0)) + 0.4;
|
||||
}
|
||||
|
||||
template<class Fx>
|
||||
static bool get_graph(Fx &fx, int subindex, float *data, int points)
|
||||
{
|
||||
for (int i = 0; i < points; i++)
|
||||
{
|
||||
typedef std::complex<double> cfloat;
|
||||
double freq = 20.0 * pow (20000.0 / 20.0, i * 1.0 / points);
|
||||
data[i] = dB_grid(fx.freq_gain(subindex, freq, fx.srate));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// convert normalized grid-ish value back to amplitude value
|
||||
static inline float dB_grid_inv(float pos)
|
||||
{
|
||||
return pow(256.0, pos - 0.4);
|
||||
}
|
||||
|
||||
static void set_channel_color(cairo_iface *context, int channel)
|
||||
{
|
||||
if (channel & 1)
|
||||
context->set_source_rgba(0.75, 1, 0);
|
||||
else
|
||||
context->set_source_rgba(0, 1, 0.75);
|
||||
context->set_line_width(1.5);
|
||||
}
|
||||
|
||||
static bool get_freq_gridline(int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context, bool use_frequencies = true)
|
||||
{
|
||||
if (subindex < 0 )
|
||||
return false;
|
||||
if (use_frequencies)
|
||||
{
|
||||
if (subindex < 28)
|
||||
{
|
||||
vertical = true;
|
||||
if (subindex == 9) legend = "100 Hz";
|
||||
if (subindex == 18) legend = "1 kHz";
|
||||
if (subindex == 27) legend = "10 kHz";
|
||||
float freq = 100;
|
||||
if (subindex < 9)
|
||||
freq = 10 * (subindex + 1);
|
||||
else if (subindex < 18)
|
||||
freq = 100 * (subindex - 9 + 1);
|
||||
else if (subindex < 27)
|
||||
freq = 1000 * (subindex - 18 + 1);
|
||||
else
|
||||
freq = 10000 * (subindex - 27 + 1);
|
||||
pos = log(freq / 20.0) / log(1000);
|
||||
if (!legend.empty())
|
||||
context->set_source_rgba(0.25, 0.25, 0.25, 0.75);
|
||||
else
|
||||
context->set_source_rgba(0.25, 0.25, 0.25, 0.5);
|
||||
return true;
|
||||
}
|
||||
subindex -= 28;
|
||||
}
|
||||
if (subindex >= 32)
|
||||
return false;
|
||||
float gain = 16.0 / (1 << subindex);
|
||||
pos = dB_grid(gain);
|
||||
if (pos < -1)
|
||||
return false;
|
||||
if (subindex != 4)
|
||||
context->set_source_rgba(0.25, 0.25, 0.25, subindex & 1 ? 0.5 : 0.75);
|
||||
if (!(subindex & 1))
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << (24 - 6 * subindex) << " dB";
|
||||
legend = ss.str();
|
||||
}
|
||||
vertical = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool frequency_response_line_graph::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context)
|
||||
{
|
||||
return get_freq_gridline(subindex, pos, vertical, legend, context);
|
||||
}
|
||||
|
||||
int frequency_response_line_graph::get_changed_offsets(int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline)
|
||||
{
|
||||
subindex_graph = 0;
|
||||
subindex_dot = 0;
|
||||
subindex_gridline = generation ? INT_MAX : 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void flanger_audio_module::activate() {
|
||||
left.reset();
|
||||
right.reset();
|
||||
last_r_phase = *params[par_stereo] * (1.f / 360.f);
|
||||
left.reset_phase(0.f);
|
||||
right.reset_phase(last_r_phase);
|
||||
is_active = true;
|
||||
}
|
||||
|
||||
void flanger_audio_module::set_sample_rate(uint32_t sr) {
|
||||
srate = sr;
|
||||
left.setup(sr);
|
||||
right.setup(sr);
|
||||
}
|
||||
|
||||
void flanger_audio_module::deactivate() {
|
||||
is_active = false;
|
||||
}
|
||||
|
||||
bool flanger_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context)
|
||||
{
|
||||
if (!is_active)
|
||||
return false;
|
||||
if (index == par_delay && subindex < 2)
|
||||
{
|
||||
set_channel_color(context, subindex);
|
||||
return ::get_graph(*this, subindex, data, points);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
float flanger_audio_module::freq_gain(int subindex, float freq, float srate)
|
||||
{
|
||||
return (subindex ? right : left).freq_gain(freq, srate);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void phaser_audio_module::set_sample_rate(uint32_t sr)
|
||||
{
|
||||
srate = sr;
|
||||
left.setup(sr);
|
||||
right.setup(sr);
|
||||
}
|
||||
|
||||
void phaser_audio_module::activate()
|
||||
{
|
||||
is_active = true;
|
||||
left.reset();
|
||||
right.reset();
|
||||
last_r_phase = *params[par_stereo] * (1.f / 360.f);
|
||||
left.reset_phase(0.f);
|
||||
right.reset_phase(last_r_phase);
|
||||
}
|
||||
|
||||
void phaser_audio_module::deactivate()
|
||||
{
|
||||
is_active = false;
|
||||
}
|
||||
|
||||
bool phaser_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context)
|
||||
{
|
||||
if (!is_active)
|
||||
return false;
|
||||
if (subindex < 2)
|
||||
{
|
||||
set_channel_color(context, subindex);
|
||||
return ::get_graph(*this, subindex, data, points);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
float phaser_audio_module::freq_gain(int subindex, float freq, float srate)
|
||||
{
|
||||
return (subindex ? right : left).freq_gain(freq, srate);
|
||||
}
|
||||
|
||||
bool phaser_audio_module::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context)
|
||||
{
|
||||
return get_freq_gridline(subindex, pos, vertical, legend, context);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void reverb_audio_module::activate()
|
||||
{
|
||||
reverb.reset();
|
||||
}
|
||||
|
||||
void reverb_audio_module::deactivate()
|
||||
{
|
||||
}
|
||||
|
||||
void reverb_audio_module::set_sample_rate(uint32_t sr)
|
||||
{
|
||||
srate = sr;
|
||||
reverb.setup(sr);
|
||||
amount.set_sample_rate(sr);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool filter_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context)
|
||||
{
|
||||
if (!is_active)
|
||||
return false;
|
||||
if (index == par_cutoff && !subindex) {
|
||||
context->set_line_width(1.5);
|
||||
return ::get_graph(*this, subindex, data, points);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int filter_audio_module::get_changed_offsets(int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline)
|
||||
{
|
||||
if (fabs(inertia_cutoff.get_last() - old_cutoff) + 100 * fabs(inertia_resonance.get_last() - old_resonance) + fabs(*params[par_mode] - old_mode) > 0.1f)
|
||||
{
|
||||
old_cutoff = inertia_cutoff.get_last();
|
||||
old_resonance = inertia_resonance.get_last();
|
||||
old_mode = *params[par_mode];
|
||||
last_generation++;
|
||||
subindex_graph = 0;
|
||||
subindex_dot = INT_MAX;
|
||||
subindex_gridline = INT_MAX;
|
||||
}
|
||||
else {
|
||||
subindex_graph = 0;
|
||||
subindex_dot = subindex_gridline = generation ? INT_MAX : 0;
|
||||
}
|
||||
if (generation == last_calculated_generation)
|
||||
subindex_graph = INT_MAX;
|
||||
return last_generation;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool filterclavier_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context)
|
||||
{
|
||||
if (!is_active || index != par_mode) {
|
||||
return false;
|
||||
}
|
||||
if (!subindex) {
|
||||
context->set_line_width(1.5);
|
||||
return ::get_graph(*this, subindex, data, points);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
rotary_speaker_audio_module::rotary_speaker_audio_module()
|
||||
{
|
||||
mwhl_value = hold_value = 0.f;
|
||||
phase_h = phase_l = 0.f;
|
||||
aspeed_l = 1.f;
|
||||
aspeed_h = 1.f;
|
||||
dspeed = 0.f;
|
||||
}
|
||||
|
||||
void rotary_speaker_audio_module::set_sample_rate(uint32_t sr)
|
||||
{
|
||||
srate = sr;
|
||||
setup();
|
||||
}
|
||||
|
||||
void rotary_speaker_audio_module::setup()
|
||||
{
|
||||
crossover1l.set_lp_rbj(800.f, 0.7, (float)srate);
|
||||
crossover1r.set_lp_rbj(800.f, 0.7, (float)srate);
|
||||
crossover2l.set_hp_rbj(800.f, 0.7, (float)srate);
|
||||
crossover2r.set_hp_rbj(800.f, 0.7, (float)srate);
|
||||
}
|
||||
|
||||
void rotary_speaker_audio_module::activate()
|
||||
{
|
||||
phase_h = phase_l = 0.f;
|
||||
maspeed_h = maspeed_l = 0.f;
|
||||
setup();
|
||||
}
|
||||
|
||||
void rotary_speaker_audio_module::deactivate()
|
||||
{
|
||||
}
|
||||
|
||||
void rotary_speaker_audio_module::control_change(int ctl, int val)
|
||||
{
|
||||
if (vibrato_mode == 3 && ctl == 64)
|
||||
{
|
||||
hold_value = val / 127.f;
|
||||
set_vibrato();
|
||||
return;
|
||||
}
|
||||
if (vibrato_mode == 4 && ctl == 1)
|
||||
{
|
||||
mwhl_value = val / 127.f;
|
||||
set_vibrato();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void multichorus_audio_module::activate()
|
||||
{
|
||||
is_active = true;
|
||||
params_changed();
|
||||
}
|
||||
|
||||
void multichorus_audio_module::deactivate()
|
||||
{
|
||||
is_active = false;
|
||||
}
|
||||
|
||||
void multichorus_audio_module::set_sample_rate(uint32_t sr) {
|
||||
srate = sr;
|
||||
left.setup(sr);
|
||||
right.setup(sr);
|
||||
}
|
||||
|
||||
bool multichorus_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context)
|
||||
{
|
||||
if (!is_active)
|
||||
return false;
|
||||
int nvoices = (int)*params[par_voices];
|
||||
if (index == par_delay && subindex < 3)
|
||||
{
|
||||
if (subindex < 2)
|
||||
set_channel_color(context, subindex);
|
||||
else {
|
||||
context->set_source_rgba(0, 1, 0);
|
||||
context->set_line_width(1.0);
|
||||
}
|
||||
return ::get_graph(*this, subindex, data, points);
|
||||
}
|
||||
if (index == par_rate && subindex < nvoices) {
|
||||
sine_multi_lfo<float, 8> &lfo = left.lfo;
|
||||
for (int i = 0; i < points; i++) {
|
||||
float phase = i * 2 * M_PI / points;
|
||||
// original -65536 to 65535 value
|
||||
float orig = subindex * lfo.voice_offset + ((lfo.voice_depth >> (30-13)) * 65536.0 * (0.95 * sin(phase) + 1)/ 8192.0) - 65536;
|
||||
// scale to -1..1
|
||||
data[i] = orig / 65536.0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool multichorus_audio_module::get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context)
|
||||
{
|
||||
int voice = subindex >> 1;
|
||||
int nvoices = (int)*params[par_voices];
|
||||
if ((index != par_rate && index != par_depth) || voice >= nvoices)
|
||||
return false;
|
||||
|
||||
float unit = (1 - *params[par_overlap]);
|
||||
float scw = 1 + unit * (nvoices - 1);
|
||||
set_channel_color(context, subindex);
|
||||
sine_multi_lfo<float, 8> &lfo = (subindex & 1 ? right : left).lfo;
|
||||
if (index == par_rate)
|
||||
{
|
||||
x = (double)(lfo.phase + lfo.vphase * voice) / 4096.0;
|
||||
y = 0.95 * sin(x * 2 * M_PI);
|
||||
y = (voice * unit + (y + 1) / 2) / scw * 2 - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
double ph = (double)(lfo.phase + lfo.vphase * voice) / 4096.0;
|
||||
x = 0.5 + 0.5 * sin(ph * 2 * M_PI);
|
||||
y = subindex & 1 ? -0.75 : 0.75;
|
||||
x = (voice * unit + x) / scw;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool multichorus_audio_module::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context)
|
||||
{
|
||||
if (index == par_rate && !subindex)
|
||||
{
|
||||
pos = 0;
|
||||
vertical = false;
|
||||
return true;
|
||||
}
|
||||
if (index == par_delay)
|
||||
return get_freq_gridline(subindex, pos, vertical, legend, context);
|
||||
return false;
|
||||
}
|
||||
|
||||
float multichorus_audio_module::freq_gain(int subindex, float freq, float srate)
|
||||
{
|
||||
if (subindex == 2)
|
||||
return *params[par_amount] * left.post.freq_gain(freq, srate);
|
||||
return (subindex ? right : left).freq_gain(freq, srate);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
compressor_audio_module::compressor_audio_module()
|
||||
{
|
||||
is_active = false;
|
||||
srate = 0;
|
||||
last_generation = 0;
|
||||
}
|
||||
|
||||
void compressor_audio_module::activate()
|
||||
{
|
||||
is_active = true;
|
||||
linSlope = 0.f;
|
||||
peak = 0.f;
|
||||
clip = 0.f;
|
||||
}
|
||||
|
||||
void compressor_audio_module::deactivate()
|
||||
{
|
||||
is_active = false;
|
||||
}
|
||||
|
||||
void compressor_audio_module::set_sample_rate(uint32_t sr)
|
||||
{
|
||||
srate = sr;
|
||||
awL.set(sr);
|
||||
awR.set(sr);
|
||||
}
|
||||
|
||||
bool compressor_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context)
|
||||
{
|
||||
if (!is_active)
|
||||
return false;
|
||||
if (subindex > 1) // 1
|
||||
return false;
|
||||
for (int i = 0; i < points; i++)
|
||||
{
|
||||
float input = dB_grid_inv(-1.0 + i * 2.0 / (points - 1));
|
||||
float output = output_level(input);
|
||||
if (subindex == 0)
|
||||
data[i] = dB_grid(input);
|
||||
else
|
||||
data[i] = dB_grid(output);
|
||||
}
|
||||
if (subindex == (*params[param_bypass] > 0.5f ? 1 : 0))
|
||||
context->set_source_rgba(0.5, 0.5, 0.5, 0.5);
|
||||
else {
|
||||
context->set_source_rgba(0, 1, 0, 1);
|
||||
context->set_line_width(2);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool compressor_audio_module::get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context)
|
||||
{
|
||||
if (!is_active)
|
||||
return false;
|
||||
if (!subindex)
|
||||
{
|
||||
bool rms = *params[param_detection] == 0;
|
||||
float det = rms ? sqrt(detected) : detected;
|
||||
x = 0.5 + 0.5 * dB_grid(det);
|
||||
y = dB_grid(*params[param_bypass] > 0.5f ? det : output_level(det));
|
||||
return *params[param_bypass] > 0.5f ? false : true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool compressor_audio_module::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context)
|
||||
{
|
||||
bool tmp;
|
||||
vertical = (subindex & 1) != 0;
|
||||
bool result = get_freq_gridline(subindex >> 1, pos, tmp, legend, context, false);
|
||||
if (result && vertical) {
|
||||
if ((subindex & 4) && !legend.empty()) {
|
||||
legend = "";
|
||||
}
|
||||
else {
|
||||
size_t pos = legend.find(" dB");
|
||||
if (pos != std::string::npos)
|
||||
legend.erase(pos);
|
||||
}
|
||||
pos = 0.5 + 0.5 * pos;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// In case of doubt: this function is written by Thor. I just moved it to this file, damaging
|
||||
// the output of "git annotate" in the process.
|
||||
uint32_t compressor_audio_module::process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask)
|
||||
{
|
||||
bool bypass = *params[param_bypass] > 0.5f;
|
||||
|
||||
if(bypass) {
|
||||
int count = numsamples * sizeof(float);
|
||||
memcpy(outs[0], ins[0], count);
|
||||
memcpy(outs[1], ins[1], count);
|
||||
|
||||
if(params[param_compression] != NULL) {
|
||||
*params[param_compression] = 1.f;
|
||||
}
|
||||
|
||||
if(params[param_clip] != NULL) {
|
||||
*params[param_clip] = 0.f;
|
||||
}
|
||||
|
||||
if(params[param_peak] != NULL) {
|
||||
*params[param_peak] = 0.f;
|
||||
}
|
||||
|
||||
return inputs_mask;
|
||||
}
|
||||
|
||||
bool rms = *params[param_detection] == 0;
|
||||
bool average = *params[param_stereo_link] == 0;
|
||||
int aweighting = fastf2i_drm(*params[param_aweighting]);
|
||||
float linThreshold = *params[param_threshold];
|
||||
ratio = *params[param_ratio];
|
||||
float attack = *params[param_attack];
|
||||
float attack_coeff = std::min(1.f, 1.f / (attack * srate / 4000.f));
|
||||
float release = *params[param_release];
|
||||
float release_coeff = std::min(1.f, 1.f / (release * srate / 4000.f));
|
||||
makeup = *params[param_makeup];
|
||||
knee = *params[param_knee];
|
||||
|
||||
float linKneeSqrt = sqrt(knee);
|
||||
linKneeStart = linThreshold / linKneeSqrt;
|
||||
adjKneeStart = linKneeStart*linKneeStart;
|
||||
float linKneeStop = linThreshold * linKneeSqrt;
|
||||
|
||||
threshold = log(linThreshold);
|
||||
kneeStart = log(linKneeStart);
|
||||
kneeStop = log(linKneeStop);
|
||||
compressedKneeStop = (kneeStop - threshold) / ratio + threshold;
|
||||
|
||||
if (aweighting >= 2)
|
||||
{
|
||||
bpL.set_highshelf_rbj(5000, 0.707, 10 << (aweighting - 2), srate);
|
||||
bpR.copy_coeffs(bpL);
|
||||
bpL.sanitize();
|
||||
bpR.sanitize();
|
||||
}
|
||||
|
||||
numsamples += offset;
|
||||
|
||||
float compression = 1.f;
|
||||
|
||||
peak -= peak * 5.f * numsamples / srate;
|
||||
|
||||
clip -= std::min(clip, numsamples);
|
||||
|
||||
while(offset < numsamples) {
|
||||
float left = ins[0][offset];
|
||||
float right = ins[1][offset];
|
||||
|
||||
if(aweighting == 1) {
|
||||
left = awL.process(left);
|
||||
right = awR.process(right);
|
||||
}
|
||||
else if(aweighting >= 2) {
|
||||
left = bpL.process(left);
|
||||
right = bpR.process(right);
|
||||
}
|
||||
|
||||
float absample = average ? (fabs(left) + fabs(right)) * 0.5f : std::max(fabs(left), fabs(right));
|
||||
if(rms) absample *= absample;
|
||||
|
||||
linSlope += (absample - linSlope) * (absample > linSlope ? attack_coeff : release_coeff);
|
||||
|
||||
float gain = 1.f;
|
||||
|
||||
if(linSlope > 0.f) {
|
||||
gain = output_gain(linSlope, rms);
|
||||
}
|
||||
|
||||
compression = gain;
|
||||
gain *= makeup;
|
||||
|
||||
float outL = ins[0][offset] * gain;
|
||||
float outR = ins[1][offset] * gain;
|
||||
|
||||
outs[0][offset] = outL;
|
||||
outs[1][offset] = outR;
|
||||
|
||||
++offset;
|
||||
|
||||
float maxLR = std::max(fabs(outL), fabs(outR));
|
||||
|
||||
if(maxLR > 1.f) clip = srate >> 3; /* blink clip LED for 125 ms */
|
||||
|
||||
if(maxLR > peak) {
|
||||
peak = maxLR;
|
||||
}
|
||||
}
|
||||
|
||||
detected = linSlope;
|
||||
|
||||
if(params[param_compression] != NULL) {
|
||||
*params[param_compression] = compression;
|
||||
}
|
||||
|
||||
if(params[param_clip] != NULL) {
|
||||
*params[param_clip] = clip;
|
||||
}
|
||||
|
||||
if(params[param_peak] != NULL) {
|
||||
*params[param_peak] = peak;
|
||||
}
|
||||
|
||||
return inputs_mask;
|
||||
}
|
||||
1916
plugins/ladspa_effect/calf/modules_small.cpp
Normal file
1916
plugins/ladspa_effect/calf/modules_small.cpp
Normal file
File diff suppressed because it is too large
Load Diff
663
plugins/ladspa_effect/calf/monosynth.cpp
Normal file
663
plugins/ladspa_effect/calf/monosynth.cpp
Normal file
@@ -0,0 +1,663 @@
|
||||
/* Calf DSP Library
|
||||
* Example audio modules - monosynth
|
||||
*
|
||||
* Copyright (C) 2001-2007 Krzysztof Foltman
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <memory.h>
|
||||
#include <complex>
|
||||
#if USE_JACK
|
||||
#include <jack/jack.h>
|
||||
#endif
|
||||
#include <calf/giface.h>
|
||||
#include <calf/modules_synths.h>
|
||||
|
||||
using namespace dsp;
|
||||
using namespace calf_plugins;
|
||||
using namespace std;
|
||||
|
||||
float silence[4097];
|
||||
|
||||
static const char *monosynth_mod_src_names[] = {
|
||||
"None",
|
||||
"Velocity",
|
||||
"Pressure",
|
||||
"ModWheel",
|
||||
"Envelope",
|
||||
"LFO",
|
||||
NULL
|
||||
};
|
||||
|
||||
static const char *monosynth_mod_dest_names[] = {
|
||||
"None",
|
||||
"Attenuation",
|
||||
"Osc Mix Ratio (%)",
|
||||
"Cutoff [ct]",
|
||||
"Resonance",
|
||||
"O1: Detune [ct]",
|
||||
"O2: Detune [ct]",
|
||||
"O1: PW (%)",
|
||||
"O2: PW (%)",
|
||||
NULL
|
||||
};
|
||||
|
||||
monosynth_audio_module::monosynth_audio_module()
|
||||
: mod_matrix(mod_matrix_data, mod_matrix_slots, monosynth_mod_src_names, monosynth_mod_dest_names)
|
||||
, inertia_cutoff(1)
|
||||
, inertia_pitchbend(1)
|
||||
, inertia_pressure(64)
|
||||
{
|
||||
}
|
||||
|
||||
void monosynth_audio_module::activate() {
|
||||
running = false;
|
||||
output_pos = 0;
|
||||
queue_note_on = -1;
|
||||
stop_count = 0;
|
||||
inertia_pitchbend.set_now(1.f);
|
||||
lfo_bend = 1.0;
|
||||
modwheel_value = 0.f;
|
||||
modwheel_value_int = 0;
|
||||
inertia_cutoff.set_now(*params[par_cutoff]);
|
||||
inertia_pressure.set_now(0);
|
||||
filter.reset();
|
||||
filter2.reset();
|
||||
stack.clear();
|
||||
last_pwshift1 = last_pwshift2 = 0;
|
||||
}
|
||||
|
||||
waveform_family<MONOSYNTH_WAVE_BITS> *monosynth_audio_module::waves;
|
||||
|
||||
void monosynth_audio_module::precalculate_waves(progress_report_iface *reporter)
|
||||
{
|
||||
float data[1 << MONOSYNTH_WAVE_BITS];
|
||||
bandlimiter<MONOSYNTH_WAVE_BITS> bl;
|
||||
|
||||
if (waves)
|
||||
return;
|
||||
|
||||
static waveform_family<MONOSYNTH_WAVE_BITS> waves_data[wave_count];
|
||||
waves = waves_data;
|
||||
|
||||
enum { S = 1 << MONOSYNTH_WAVE_BITS, HS = S / 2, QS = S / 4, QS3 = 3 * QS };
|
||||
float iQS = 1.0 / QS;
|
||||
|
||||
if (reporter)
|
||||
reporter->report_progress(0, "Precalculating waveforms");
|
||||
|
||||
// yes these waves don't have really perfect 1/x spectrum because of aliasing
|
||||
// (so what?)
|
||||
for (int i = 0 ; i < HS; i++)
|
||||
data[i] = (float)(i * 1.0 / HS),
|
||||
data[i + HS] = (float)(i * 1.0 / HS - 1.0f);
|
||||
waves[wave_saw].make(bl, data);
|
||||
|
||||
// this one is dummy, fake and sham, we're using a difference of two sawtooths for square wave due to PWM
|
||||
for (int i = 0 ; i < S; i++)
|
||||
data[i] = (float)(i < HS ? -1.f : 1.f);
|
||||
waves[wave_sqr].make(bl, data, 4);
|
||||
|
||||
for (int i = 0 ; i < S; i++)
|
||||
data[i] = (float)(i < (64 * S / 2048)? -1.f : 1.f);
|
||||
waves[wave_pulse].make(bl, data);
|
||||
|
||||
for (int i = 0 ; i < S; i++)
|
||||
data[i] = (float)sin(i * M_PI / HS);
|
||||
waves[wave_sine].make(bl, data);
|
||||
|
||||
for (int i = 0 ; i < QS; i++) {
|
||||
data[i] = i * iQS,
|
||||
data[i + QS] = 1 - i * iQS,
|
||||
data[i + HS] = - i * iQS,
|
||||
data[i + QS3] = -1 + i * iQS;
|
||||
}
|
||||
waves[wave_triangle].make(bl, data);
|
||||
|
||||
for (int i = 0, j = 1; i < S; i++) {
|
||||
data[i] = -1 + j * 1.0 / HS;
|
||||
if (i == j)
|
||||
j *= 2;
|
||||
}
|
||||
waves[wave_varistep].make(bl, data);
|
||||
|
||||
for (int i = 0; i < S; i++) {
|
||||
data[i] = (min(1.f, (float)(i / 64.f))) * (1.0 - i * 1.0 / S) * (-1 + fmod (i * i * 8/ (S * S * 1.0), 2.0));
|
||||
}
|
||||
waves[wave_skewsaw].make(bl, data);
|
||||
for (int i = 0; i < S; i++) {
|
||||
data[i] = (min(1.f, (float)(i / 64.f))) * (1.0 - i * 1.0 / S) * (fmod (i * i * 8/ (S * S * 1.0), 2.0) < 1.0 ? -1.0 : +1.0);
|
||||
}
|
||||
waves[wave_skewsqr].make(bl, data);
|
||||
|
||||
if (reporter)
|
||||
reporter->report_progress(50, "Precalculating waveforms");
|
||||
|
||||
for (int i = 0; i < S; i++) {
|
||||
if (i < QS3) {
|
||||
float p = i * 1.0 / QS3;
|
||||
data[i] = sin(M_PI * p * p * p);
|
||||
} else {
|
||||
float p = (i - QS3 * 1.0) / QS;
|
||||
data[i] = -0.5 * sin(3 * M_PI * p * p);
|
||||
}
|
||||
}
|
||||
waves[wave_test1].make(bl, data);
|
||||
for (int i = 0; i < S; i++) {
|
||||
data[i] = exp(-i * 1.0 / HS) * sin(i * M_PI / HS) * cos(2 * M_PI * i / HS);
|
||||
}
|
||||
normalize_waveform(data, S);
|
||||
waves[wave_test2].make(bl, data);
|
||||
for (int i = 0; i < S; i++) {
|
||||
//int ii = (i < HS) ? i : S - i;
|
||||
int ii = HS;
|
||||
data[i] = (ii * 1.0 / HS) * sin(i * 3 * M_PI / HS + 2 * M_PI * sin(M_PI / 4 + i * 4 * M_PI / HS)) * sin(i * 5 * M_PI / HS + 2 * M_PI * sin(M_PI / 8 + i * 6 * M_PI / HS));
|
||||
}
|
||||
waves[wave_test3].make(bl, data);
|
||||
for (int i = 0; i < S; i++) {
|
||||
data[i] = sin(i * 2 * M_PI / HS + sin(i * 2 * M_PI / HS + 0.5 * M_PI * sin(i * 18 * M_PI / HS)) * sin(i * 1 * M_PI / HS + 0.5 * M_PI * sin(i * 11 * M_PI / HS)));
|
||||
}
|
||||
waves[wave_test4].make(bl, data);
|
||||
for (int i = 0; i < S; i++) {
|
||||
data[i] = sin(i * 2 * M_PI / HS + 0.2 * M_PI * sin(i * 13 * M_PI / HS) + 0.1 * M_PI * sin(i * 37 * M_PI / HS)) * sin(i * M_PI / HS + 0.2 * M_PI * sin(i * 15 * M_PI / HS));
|
||||
}
|
||||
waves[wave_test5].make(bl, data);
|
||||
for (int i = 0; i < S; i++) {
|
||||
if (i < HS)
|
||||
data[i] = sin(i * 2 * M_PI / HS);
|
||||
else
|
||||
if (i < 3 * S / 4)
|
||||
data[i] = sin(i * 4 * M_PI / HS);
|
||||
else
|
||||
if (i < 7 * S / 8)
|
||||
data[i] = sin(i * 8 * M_PI / HS);
|
||||
else
|
||||
data[i] = sin(i * 8 * M_PI / HS) * (S - i) / (S / 8);
|
||||
}
|
||||
waves[wave_test6].make(bl, data);
|
||||
for (int i = 0; i < S; i++) {
|
||||
int j = i >> (MONOSYNTH_WAVE_BITS - 11);
|
||||
data[i] = (j ^ 0x1D0) * 1.0 / HS - 1;
|
||||
}
|
||||
waves[wave_test7].make(bl, data);
|
||||
for (int i = 0; i < S; i++) {
|
||||
int j = i >> (MONOSYNTH_WAVE_BITS - 11);
|
||||
data[i] = -1 + 0.66 * (3 & ((j >> 8) ^ (j >> 10) ^ (j >> 6)));
|
||||
}
|
||||
waves[wave_test8].make(bl, data);
|
||||
if (reporter)
|
||||
reporter->report_progress(100, "");
|
||||
|
||||
}
|
||||
|
||||
bool monosynth_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context)
|
||||
{
|
||||
monosynth_audio_module::precalculate_waves(NULL);
|
||||
// printf("get_graph %d %p %d wave1=%d wave2=%d\n", index, data, points, wave1, wave2);
|
||||
if (index == par_wave1 || index == par_wave2) {
|
||||
if (subindex)
|
||||
return false;
|
||||
enum { S = 1 << MONOSYNTH_WAVE_BITS };
|
||||
float value = *params[index];
|
||||
int wave = dsp::clip(dsp::fastf2i_drm(value), 0, (int)wave_count - 1);
|
||||
|
||||
uint32_t shift = index == par_wave1 ? last_pwshift1 : last_pwshift2;
|
||||
if (!running)
|
||||
shift = (int32_t)(0x78000000 * (*params[index == par_wave1 ? par_pw1 : par_pw2]));
|
||||
int flag = (wave == wave_sqr);
|
||||
|
||||
shift = (flag ? S/2 : 0) + (shift >> (32 - MONOSYNTH_WAVE_BITS));
|
||||
int sign = flag ? -1 : 1;
|
||||
if (wave == wave_sqr)
|
||||
wave = wave_saw;
|
||||
float *waveform = waves[wave].original;
|
||||
for (int i = 0; i < points; i++)
|
||||
data[i] = (sign * waveform[i * S / points] + waveform[(i * S / points + shift) & (S - 1)]) / (sign == -1 ? 1 : 2);
|
||||
return true;
|
||||
}
|
||||
if (index == par_filtertype) {
|
||||
if (!running)
|
||||
return false;
|
||||
if (subindex > (is_stereo_filter() ? 1 : 0))
|
||||
return false;
|
||||
for (int i = 0; i < points; i++)
|
||||
{
|
||||
typedef complex<double> cfloat;
|
||||
double freq = 20.0 * pow (20000.0 / 20.0, i * 1.0 / points);
|
||||
|
||||
dsp::biquad_d1_lerp<float> &f = subindex ? filter2 : filter;
|
||||
float level = f.freq_gain(freq, srate);
|
||||
if (!is_stereo_filter())
|
||||
level *= filter2.freq_gain(freq, srate);
|
||||
level *= fgain;
|
||||
|
||||
data[i] = log(level) / log(1024.0) + 0.5;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return get_static_graph(index, subindex, *params[index], data, points, context);
|
||||
}
|
||||
|
||||
void monosynth_audio_module::calculate_buffer_oscs(float lfo)
|
||||
{
|
||||
int flag1 = (wave1 == wave_sqr);
|
||||
int flag2 = (wave2 == wave_sqr);
|
||||
int32_t shift1 = last_pwshift1;
|
||||
int32_t shift2 = last_pwshift2;
|
||||
int32_t shift_target1 = (int32_t)(0x78000000 * dsp::clip11(*params[par_pw1] + lfo * *params[par_lfopw] + 0.01f * moddest[moddest_o1pw]));
|
||||
int32_t shift_target2 = (int32_t)(0x78000000 * dsp::clip11(*params[par_pw2] + lfo * *params[par_lfopw] + 0.01f * moddest[moddest_o2pw]));
|
||||
int32_t shift_delta1 = ((shift_target1 >> 1) - (last_pwshift1 >> 1)) >> (step_shift - 1);
|
||||
int32_t shift_delta2 = ((shift_target2 >> 1) - (last_pwshift2 >> 1)) >> (step_shift - 1);
|
||||
last_lfov = lfo;
|
||||
last_pwshift1 = shift_target1;
|
||||
last_pwshift2 = shift_target2;
|
||||
|
||||
shift1 += (flag1 << 31);
|
||||
shift2 += (flag2 << 31);
|
||||
float mix1 = 1 - 2 * flag1, mix2 = 1 - 2 * flag2;
|
||||
|
||||
float new_xfade = dsp::clip<float>(xfade + 0.01f * moddest[moddest_oscmix], 0.f, 1.f);
|
||||
float cur_xfade = last_xfade;
|
||||
float xfade_step = (new_xfade - cur_xfade) * (1.0 / step_size);
|
||||
|
||||
for (uint32_t i = 0; i < step_size; i++)
|
||||
{
|
||||
float osc1val = osc1.get_phaseshifted(shift1, mix1);
|
||||
float osc2val = osc2.get_phaseshifted(shift2, mix2);
|
||||
float wave = osc1val + (osc2val - osc1val) * cur_xfade;
|
||||
buffer[i] = wave;
|
||||
shift1 += shift_delta1;
|
||||
shift2 += shift_delta2;
|
||||
cur_xfade += xfade_step;
|
||||
}
|
||||
last_xfade = new_xfade;
|
||||
}
|
||||
|
||||
void monosynth_audio_module::calculate_buffer_ser()
|
||||
{
|
||||
filter.big_step(1.0 / step_size);
|
||||
filter2.big_step(1.0 / step_size);
|
||||
for (uint32_t i = 0; i < step_size; i++)
|
||||
{
|
||||
float wave = buffer[i] * fgain;
|
||||
wave = filter.process(wave);
|
||||
wave = filter2.process(wave);
|
||||
buffer[i] = wave;
|
||||
fgain += fgain_delta;
|
||||
}
|
||||
}
|
||||
|
||||
void monosynth_audio_module::calculate_buffer_single()
|
||||
{
|
||||
filter.big_step(1.0 / step_size);
|
||||
for (uint32_t i = 0; i < step_size; i++)
|
||||
{
|
||||
float wave = buffer[i] * fgain;
|
||||
wave = filter.process(wave);
|
||||
buffer[i] = wave;
|
||||
fgain += fgain_delta;
|
||||
}
|
||||
}
|
||||
|
||||
void monosynth_audio_module::calculate_buffer_stereo()
|
||||
{
|
||||
filter.big_step(1.0 / step_size);
|
||||
filter2.big_step(1.0 / step_size);
|
||||
for (uint32_t i = 0; i < step_size; i++)
|
||||
{
|
||||
float wave1 = buffer[i] * fgain;
|
||||
float wave2 = phaseshifter.process_ap(wave1);
|
||||
buffer[i] = fgain * filter.process(wave1);
|
||||
buffer2[i] = fgain * filter2.process(wave2);
|
||||
fgain += fgain_delta;
|
||||
}
|
||||
}
|
||||
|
||||
void monosynth_audio_module::lookup_waveforms()
|
||||
{
|
||||
osc1.waveform = waves[wave1 == wave_sqr ? wave_saw : wave1].get_level(osc1.phasedelta);
|
||||
osc2.waveform = waves[wave2 == wave_sqr ? wave_saw : wave2].get_level(osc2.phasedelta);
|
||||
if (!osc1.waveform) osc1.waveform = silence;
|
||||
if (!osc2.waveform) osc2.waveform = silence;
|
||||
prev_wave1 = wave1;
|
||||
prev_wave2 = wave2;
|
||||
}
|
||||
|
||||
void monosynth_audio_module::delayed_note_on()
|
||||
{
|
||||
force_fadeout = false;
|
||||
stop_count = 0;
|
||||
porta_time = 0.f;
|
||||
start_freq = freq;
|
||||
target_freq = freq = 440 * pow(2.0, (queue_note_on - 69) / 12.0);
|
||||
velocity = queue_vel;
|
||||
ampctl = 1.0 + (queue_vel - 1.0) * *params[par_vel2amp];
|
||||
fltctl = 1.0 + (queue_vel - 1.0) * *params[par_vel2filter];
|
||||
set_frequency();
|
||||
lookup_waveforms();
|
||||
lfo_clock = 0.f;
|
||||
|
||||
if (!running)
|
||||
{
|
||||
if (legato >= 2)
|
||||
porta_time = -1.f;
|
||||
last_xfade = xfade;
|
||||
osc1.reset();
|
||||
osc2.reset();
|
||||
filter.reset();
|
||||
filter2.reset();
|
||||
lfo.reset();
|
||||
switch((int)*params[par_oscmode])
|
||||
{
|
||||
case 1:
|
||||
osc2.phase = 0x80000000;
|
||||
break;
|
||||
case 2:
|
||||
osc2.phase = 0x40000000;
|
||||
break;
|
||||
case 3:
|
||||
osc1.phase = osc2.phase = 0x40000000;
|
||||
break;
|
||||
case 4:
|
||||
osc1.phase = 0x40000000;
|
||||
osc2.phase = 0xC0000000;
|
||||
break;
|
||||
case 5:
|
||||
// rand() is crap, but I don't have any better RNG in Calf yet
|
||||
osc1.phase = rand() << 16;
|
||||
osc2.phase = rand() << 16;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
envelope.note_on();
|
||||
running = true;
|
||||
}
|
||||
if (legato >= 2 && !gate)
|
||||
porta_time = -1.f;
|
||||
gate = true;
|
||||
stopping = false;
|
||||
if (!(legato & 1) || envelope.released()) {
|
||||
envelope.note_on();
|
||||
}
|
||||
envelope.advance();
|
||||
queue_note_on = -1;
|
||||
float modsrc[modsrc_count] = { 1, velocity, inertia_pressure.get_last(), modwheel_value, 0, 0.5+0.5*last_lfov};
|
||||
calculate_modmatrix(moddest, moddest_count, modsrc);
|
||||
}
|
||||
|
||||
void monosynth_audio_module::set_sample_rate(uint32_t sr) {
|
||||
srate = sr;
|
||||
crate = sr / step_size;
|
||||
odcr = (float)(1.0 / crate);
|
||||
phaseshifter.set_ap(1000.f, sr);
|
||||
fgain = 0.f;
|
||||
fgain_delta = 0.f;
|
||||
inertia_cutoff.ramp.set_length(crate / 30); // 1/30s
|
||||
inertia_pitchbend.ramp.set_length(crate / 30); // 1/30s
|
||||
}
|
||||
|
||||
void monosynth_audio_module::calculate_step()
|
||||
{
|
||||
if (queue_note_on != -1)
|
||||
delayed_note_on();
|
||||
else if (stopping)
|
||||
{
|
||||
running = false;
|
||||
dsp::zero(buffer, step_size);
|
||||
if (is_stereo_filter())
|
||||
dsp::zero(buffer2, step_size);
|
||||
envelope.advance();
|
||||
return;
|
||||
}
|
||||
lfo.set_freq(*params[par_lforate], crate);
|
||||
float porta_total_time = *params[par_portamento] * 0.001f;
|
||||
|
||||
if (porta_total_time >= 0.00101f && porta_time >= 0) {
|
||||
// XXXKF this is criminal, optimize!
|
||||
float point = porta_time / porta_total_time;
|
||||
if (point >= 1.0f) {
|
||||
freq = target_freq;
|
||||
porta_time = -1;
|
||||
} else {
|
||||
freq = start_freq + (target_freq - start_freq) * point;
|
||||
// freq = start_freq * pow(target_freq / start_freq, point);
|
||||
porta_time += odcr;
|
||||
}
|
||||
}
|
||||
float lfov = lfo.get() * std::min(1.0f, lfo_clock / *params[par_lfodelay]);
|
||||
lfov = lfov * dsp::lerp(1.f, modwheel_value, *params[par_mwhl_lfo]);
|
||||
lfo_clock += odcr;
|
||||
if (fabs(*params[par_lfopitch]) > small_value<float>())
|
||||
lfo_bend = pow(2.0f, *params[par_lfopitch] * lfov * (1.f / 1200.0f));
|
||||
inertia_pitchbend.step();
|
||||
set_frequency();
|
||||
envelope.advance();
|
||||
float env = envelope.value;
|
||||
|
||||
// mod matrix
|
||||
// this should be optimized heavily; I think I'll do it when MIDI in Ardour 3 gets stable :>
|
||||
float modsrc[modsrc_count] = { 1, velocity, inertia_pressure.get(), modwheel_value, env, 0.5+0.5*lfov};
|
||||
calculate_modmatrix(moddest, moddest_count, modsrc);
|
||||
|
||||
inertia_cutoff.set_inertia(*params[par_cutoff]);
|
||||
cutoff = inertia_cutoff.get() * pow(2.0f, (lfov * *params[par_lfofilter] + env * fltctl * *params[par_envmod] + moddest[moddest_cutoff]) * (1.f / 1200.f));
|
||||
if (*params[par_keyfollow] > 0.01f)
|
||||
cutoff *= pow(freq / 264.f, *params[par_keyfollow]);
|
||||
cutoff = dsp::clip(cutoff , 10.f, 18000.f);
|
||||
float resonance = *params[par_resonance];
|
||||
float e2r = *params[par_envtores];
|
||||
float e2a = *params[par_envtoamp];
|
||||
resonance = resonance * (1 - e2r) + (0.7 + (resonance - 0.7) * env * env) * e2r + moddest[moddest_resonance];
|
||||
float cutoff2 = dsp::clip(cutoff * separation, 10.f, 18000.f);
|
||||
float newfgain = 0.f;
|
||||
if (filter_type != last_filter_type)
|
||||
{
|
||||
filter.y2 = filter.y1 = filter.x2 = filter.x1 = filter.y1;
|
||||
filter2.y2 = filter2.y1 = filter2.x2 = filter2.x1 = filter2.y1;
|
||||
last_filter_type = filter_type;
|
||||
}
|
||||
switch(filter_type)
|
||||
{
|
||||
case flt_lp12:
|
||||
filter.set_lp_rbj(cutoff, resonance, srate);
|
||||
filter2.set_null();
|
||||
newfgain = min(0.7f, 0.7f / resonance) * ampctl;
|
||||
break;
|
||||
case flt_hp12:
|
||||
filter.set_hp_rbj(cutoff, resonance, srate);
|
||||
filter2.set_null();
|
||||
newfgain = min(0.7f, 0.7f / resonance) * ampctl;
|
||||
break;
|
||||
case flt_lp24:
|
||||
filter.set_lp_rbj(cutoff, resonance, srate);
|
||||
filter2.set_lp_rbj(cutoff2, resonance, srate);
|
||||
newfgain = min(0.5f, 0.5f / resonance) * ampctl;
|
||||
break;
|
||||
case flt_lpbr:
|
||||
filter.set_lp_rbj(cutoff, resonance, srate);
|
||||
filter2.set_br_rbj(cutoff2, 1.0 / resonance, srate);
|
||||
newfgain = min(0.5f, 0.5f / resonance) * ampctl;
|
||||
break;
|
||||
case flt_hpbr:
|
||||
filter.set_hp_rbj(cutoff, resonance, srate);
|
||||
filter2.set_br_rbj(cutoff2, 1.0 / resonance, srate);
|
||||
newfgain = min(0.5f, 0.5f / resonance) * ampctl;
|
||||
break;
|
||||
case flt_2lp12:
|
||||
filter.set_lp_rbj(cutoff, resonance, srate);
|
||||
filter2.set_lp_rbj(cutoff2, resonance, srate);
|
||||
newfgain = min(0.7f, 0.7f / resonance) * ampctl;
|
||||
break;
|
||||
case flt_bp6:
|
||||
filter.set_bp_rbj(cutoff, resonance, srate);
|
||||
filter2.set_null();
|
||||
newfgain = ampctl;
|
||||
break;
|
||||
case flt_2bp6:
|
||||
filter.set_bp_rbj(cutoff, resonance, srate);
|
||||
filter2.set_bp_rbj(cutoff2, resonance, srate);
|
||||
newfgain = ampctl;
|
||||
break;
|
||||
}
|
||||
float aenv = env;
|
||||
if (*params[par_envtoamp] > 0.f)
|
||||
newfgain *= 1.0 - (1.0 - aenv) * e2a;
|
||||
if (moddest[moddest_attenuation] != 0.f)
|
||||
newfgain *= dsp::clip<float>(1 - moddest[moddest_attenuation] * moddest[moddest_attenuation], 0.f, 1.f);
|
||||
fgain_delta = (newfgain - fgain) * (1.0 / step_size);
|
||||
calculate_buffer_oscs(lfov);
|
||||
switch(filter_type)
|
||||
{
|
||||
case flt_lp24:
|
||||
case flt_lpbr:
|
||||
case flt_hpbr: // Oomek's wish
|
||||
calculate_buffer_ser();
|
||||
break;
|
||||
case flt_lp12:
|
||||
case flt_hp12:
|
||||
case flt_bp6:
|
||||
calculate_buffer_single();
|
||||
break;
|
||||
case flt_2lp12:
|
||||
case flt_2bp6:
|
||||
calculate_buffer_stereo();
|
||||
break;
|
||||
}
|
||||
if ((envelope.state == adsr::STOP && !gate) || force_fadeout || (envelope.state == adsr::RELEASE && *params[par_envtoamp] <= 0.f))
|
||||
{
|
||||
enum { ramp = step_size * 4 };
|
||||
for (int i = 0; i < step_size; i++)
|
||||
buffer[i] *= (ramp - i - stop_count) * (1.0f / ramp);
|
||||
if (is_stereo_filter())
|
||||
for (int i = 0; i < step_size; i++)
|
||||
buffer2[i] *= (ramp - i - stop_count) * (1.0f / ramp);
|
||||
stop_count += step_size;
|
||||
if (stop_count >= ramp)
|
||||
stopping = true;
|
||||
}
|
||||
}
|
||||
|
||||
void monosynth_audio_module::note_on(int note, int vel)
|
||||
{
|
||||
queue_note_on = note;
|
||||
last_key = note;
|
||||
queue_vel = vel / 127.f;
|
||||
stack.push(note);
|
||||
}
|
||||
|
||||
void monosynth_audio_module::note_off(int note, int vel)
|
||||
{
|
||||
stack.pop(note);
|
||||
// If releasing the currently played note, try to get another one from note stack.
|
||||
if (note == last_key) {
|
||||
if (stack.count())
|
||||
{
|
||||
last_key = note = stack.nth(stack.count() - 1);
|
||||
start_freq = freq;
|
||||
target_freq = freq = dsp::note_to_hz(note);
|
||||
porta_time = 0;
|
||||
set_frequency();
|
||||
if (!(legato & 1)) {
|
||||
envelope.note_on();
|
||||
stopping = false;
|
||||
running = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
gate = false;
|
||||
envelope.note_off();
|
||||
}
|
||||
}
|
||||
|
||||
void monosynth_audio_module::channel_pressure(int value)
|
||||
{
|
||||
inertia_pressure.set_inertia(value * (1.0 / 127.0));
|
||||
}
|
||||
|
||||
void monosynth_audio_module::control_change(int controller, int value)
|
||||
{
|
||||
switch(controller)
|
||||
{
|
||||
case 1:
|
||||
modwheel_value_int = (modwheel_value_int & 127) | (value << 7);
|
||||
modwheel_value = modwheel_value_int / 16383.0;
|
||||
break;
|
||||
case 33:
|
||||
modwheel_value_int = (modwheel_value_int & (127 << 7)) | value;
|
||||
modwheel_value = modwheel_value_int / 16383.0;
|
||||
break;
|
||||
case 120: // all sounds off
|
||||
force_fadeout = true;
|
||||
// fall through
|
||||
case 123: // all notes off
|
||||
gate = false;
|
||||
queue_note_on = -1;
|
||||
envelope.note_off();
|
||||
stack.clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void monosynth_audio_module::deactivate()
|
||||
{
|
||||
gate = false;
|
||||
running = false;
|
||||
stopping = false;
|
||||
envelope.reset();
|
||||
stack.clear();
|
||||
}
|
||||
|
||||
uint32_t monosynth_audio_module::process(uint32_t offset, uint32_t nsamples, uint32_t inputs_mask, uint32_t outputs_mask) {
|
||||
if (!running && queue_note_on == -1) {
|
||||
for (uint32_t i = 0; i < nsamples / step_size; i++)
|
||||
envelope.advance();
|
||||
return 0;
|
||||
}
|
||||
uint32_t op = offset;
|
||||
uint32_t op_end = offset + nsamples;
|
||||
while(op < op_end) {
|
||||
if (output_pos == 0) {
|
||||
if (running || queue_note_on != -1)
|
||||
calculate_step();
|
||||
else {
|
||||
envelope.advance();
|
||||
dsp::zero(buffer, step_size);
|
||||
}
|
||||
}
|
||||
if(op < op_end) {
|
||||
uint32_t ip = output_pos;
|
||||
uint32_t len = std::min(step_size - output_pos, op_end - op);
|
||||
if (is_stereo_filter())
|
||||
for(uint32_t i = 0 ; i < len; i++) {
|
||||
float vol = master.get();
|
||||
outs[0][op + i] = buffer[ip + i] * vol,
|
||||
outs[1][op + i] = buffer2[ip + i] * vol;
|
||||
}
|
||||
else
|
||||
for(uint32_t i = 0 ; i < len; i++)
|
||||
outs[0][op + i] = outs[1][op + i] = buffer[ip + i] * master.get();
|
||||
op += len;
|
||||
output_pos += len;
|
||||
if (output_pos == step_size)
|
||||
output_pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 3;
|
||||
}
|
||||
|
||||
857
plugins/ladspa_effect/calf/organ.cpp
Normal file
857
plugins/ladspa_effect/calf/organ.cpp
Normal file
@@ -0,0 +1,857 @@
|
||||
/* Calf DSP Library
|
||||
* Example audio modules - organ
|
||||
*
|
||||
* Copyright (C) 2001-2007 Krzysztof Foltman
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <memory.h>
|
||||
#include <complex>
|
||||
#if USE_JACK
|
||||
#include <jack/jack.h>
|
||||
#endif
|
||||
#include <calf/giface.h>
|
||||
#include <calf/modules_synths.h>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace dsp;
|
||||
using namespace calf_plugins;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool organ_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context)
|
||||
{
|
||||
if (index == par_master) {
|
||||
organ_voice_base::precalculate_waves(progress_report);
|
||||
if (subindex)
|
||||
return false;
|
||||
float *waveforms[9];
|
||||
int S[9], S2[9];
|
||||
enum { small_waves = organ_voice_base::wave_count_small};
|
||||
for (int i = 0; i < 9; i++)
|
||||
{
|
||||
int wave = dsp::clip((int)(parameters->waveforms[i]), 0, (int)organ_voice_base::wave_count - 1);
|
||||
if (wave >= small_waves)
|
||||
{
|
||||
waveforms[i] = organ_voice_base::get_big_wave(wave - small_waves).original;
|
||||
S[i] = ORGAN_BIG_WAVE_SIZE;
|
||||
S2[i] = ORGAN_WAVE_SIZE / 64;
|
||||
}
|
||||
else
|
||||
{
|
||||
waveforms[i] = organ_voice_base::get_wave(wave).original;
|
||||
S[i] = S2[i] = ORGAN_WAVE_SIZE;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < points; i++)
|
||||
{
|
||||
float sum = 0.f;
|
||||
for (int j = 0; j < 9; j++)
|
||||
{
|
||||
float shift = parameters->phase[j] * S[j] / 360.0;
|
||||
sum += parameters->drawbars[j] * waveforms[j][int(parameters->harmonics[j] * i * S2[j] / points + shift) & (S[j] - 1)];
|
||||
}
|
||||
data[i] = sum * 2 / (9 * 8);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
organ_voice_base::small_wave_family (*organ_voice_base::waves)[organ_voice_base::wave_count_small];
|
||||
organ_voice_base::big_wave_family (*organ_voice_base::big_waves)[organ_voice_base::wave_count_big];
|
||||
|
||||
static void smoothen(bandlimiter<ORGAN_WAVE_BITS> &bl, float tmp[ORGAN_WAVE_SIZE])
|
||||
{
|
||||
bl.compute_spectrum(tmp);
|
||||
for (int i = 1; i <= ORGAN_WAVE_SIZE / 2; i++) {
|
||||
bl.spectrum[i] *= 1.0 / sqrt(i);
|
||||
bl.spectrum[ORGAN_WAVE_SIZE - i] *= 1.0 / sqrt(i);
|
||||
}
|
||||
bl.compute_waveform(tmp);
|
||||
normalize_waveform(tmp, ORGAN_WAVE_SIZE);
|
||||
}
|
||||
|
||||
static void phaseshift(bandlimiter<ORGAN_WAVE_BITS> &bl, float tmp[ORGAN_WAVE_SIZE])
|
||||
{
|
||||
bl.compute_spectrum(tmp);
|
||||
for (int i = 1; i <= ORGAN_WAVE_SIZE / 2; i++) {
|
||||
float frac = i * 2.0 / ORGAN_WAVE_SIZE;
|
||||
float phase = M_PI / sqrt(frac) ;
|
||||
complex<float> shift = complex<float>(cos(phase), sin(phase));
|
||||
bl.spectrum[i] *= shift;
|
||||
bl.spectrum[ORGAN_WAVE_SIZE - i] *= conj(shift);
|
||||
}
|
||||
bl.compute_waveform(tmp);
|
||||
normalize_waveform(tmp, ORGAN_WAVE_SIZE);
|
||||
}
|
||||
|
||||
static void padsynth(bandlimiter<ORGAN_WAVE_BITS> blSrc, bandlimiter<ORGAN_BIG_WAVE_BITS> &blDest, organ_voice_base::big_wave_family &result, int bwscale = 20, float bell_factor = 0, bool foldover = false)
|
||||
{
|
||||
// kept in a vector to avoid putting large arrays on stack
|
||||
vector<complex<float> >orig_spectrum;
|
||||
orig_spectrum.resize(ORGAN_WAVE_SIZE / 2);
|
||||
for (int i = 0; i < ORGAN_WAVE_SIZE / 2; i++)
|
||||
{
|
||||
orig_spectrum[i] = blSrc.spectrum[i];
|
||||
// printf("@%d = %f\n", i, abs(orig_spectrum[i]));
|
||||
}
|
||||
|
||||
int periods = (1 << ORGAN_BIG_WAVE_SHIFT) * ORGAN_BIG_WAVE_SIZE / ORGAN_WAVE_SIZE;
|
||||
for (int i = 0; i <= ORGAN_BIG_WAVE_SIZE / 2; i++) {
|
||||
blDest.spectrum[i] = 0;
|
||||
}
|
||||
int MAXHARM = (ORGAN_WAVE_SIZE >> (1 + ORGAN_BIG_WAVE_SHIFT));
|
||||
for (int i = 1; i <= MAXHARM; i++) {
|
||||
//float esc = 0.25 * (1 + 0.5 * log(i));
|
||||
float esc = 0.5;
|
||||
float amp = abs(blSrc.spectrum[i]);
|
||||
// fade out starting from half
|
||||
if (i >= MAXHARM / 2) {
|
||||
float pos = (i - MAXHARM/2) * 1.0 / (MAXHARM / 2);
|
||||
amp *= 1.0 - pos;
|
||||
amp *= 1.0 - pos;
|
||||
}
|
||||
int bw = 1 + 20 * i;
|
||||
float sum = 1;
|
||||
int delta = 1;
|
||||
if (bw > 20) delta = bw / 20;
|
||||
for (int j = delta; j <= bw; j+=delta)
|
||||
{
|
||||
float p = j * 1.0 / bw;
|
||||
sum += 2 * exp(-p * p * esc);
|
||||
}
|
||||
if (sum < 0.0001)
|
||||
continue;
|
||||
amp *= (ORGAN_BIG_WAVE_SIZE / ORGAN_WAVE_SIZE);
|
||||
amp /= sum;
|
||||
int orig = i * periods + bell_factor * cos(i);
|
||||
if (orig > 0 && orig < ORGAN_BIG_WAVE_SIZE / 2)
|
||||
blDest.spectrum[orig] += amp;
|
||||
for (int j = delta; j <= bw; j += delta)
|
||||
{
|
||||
float p = j * 1.0 / bw;
|
||||
float val = amp * exp(-p * p * esc);
|
||||
int dist = j * bwscale / 40;
|
||||
int pos = orig + dist;
|
||||
if (pos < 1 || pos >= ORGAN_BIG_WAVE_SIZE / 2)
|
||||
continue;
|
||||
int pos2 = orig - dist;
|
||||
if (pos2 < 1 || pos2 >= ORGAN_BIG_WAVE_SIZE / 2)
|
||||
continue;
|
||||
blDest.spectrum[pos] += val;
|
||||
if (j)
|
||||
blDest.spectrum[pos2] += val;
|
||||
}
|
||||
}
|
||||
for (int i = 1; i <= ORGAN_BIG_WAVE_SIZE / 2; i++) {
|
||||
float phase = M_PI * 2 * (rand() & 255) / 256;
|
||||
complex<float> shift = complex<float>(cos(phase), sin(phase));
|
||||
blDest.spectrum[i] *= shift;
|
||||
// printf("@%d = %f\n", i, abs(blDest.spectrum[i]));
|
||||
|
||||
blDest.spectrum[ORGAN_BIG_WAVE_SIZE - i] = conj(blDest.spectrum[i]);
|
||||
}
|
||||
// same as above - put large array on heap to avoid stack overflow in ingen
|
||||
vector<float> tmp;
|
||||
tmp.resize(ORGAN_BIG_WAVE_SIZE);
|
||||
blDest.compute_waveform(tmp.data());
|
||||
normalize_waveform(tmp.data(), ORGAN_BIG_WAVE_SIZE);
|
||||
blDest.compute_spectrum(tmp.data());
|
||||
|
||||
// limit is 1/2 of the number of harmonics of the original wave
|
||||
result.make_from_spectrum(blDest, foldover, ORGAN_WAVE_SIZE >> (1 + ORGAN_BIG_WAVE_SHIFT));
|
||||
memcpy(result.original, result.begin()->second, sizeof(result.original));
|
||||
#if 0
|
||||
blDest.compute_waveform(result);
|
||||
normalize_waveform(result, ORGAN_BIG_WAVE_SIZE);
|
||||
result[ORGAN_BIG_WAVE_SIZE] = result[0];
|
||||
for (int i =0 ; i<ORGAN_BIG_WAVE_SIZE + 1; i++)
|
||||
printf("%f\n", result[i]);
|
||||
#endif
|
||||
}
|
||||
|
||||
#define LARGE_WAVEFORM_PROGRESS() do { if (reporter) { progress += 100; reporter->report_progress(floor(progress / totalwaves), "Precalculating large waveforms"); } } while(0)
|
||||
|
||||
|
||||
void organ_voice_base::precalculate_waves(progress_report_iface *reporter)
|
||||
{
|
||||
static bool inited = false;
|
||||
if (!inited)
|
||||
{
|
||||
static organ_voice_base::small_wave_family waves[organ_voice_base::wave_count_small];
|
||||
static organ_voice_base::big_wave_family big_waves[organ_voice_base::wave_count_big];
|
||||
organ_voice_base::waves = &waves;
|
||||
organ_voice_base::big_waves = &big_waves;
|
||||
|
||||
float progress = 0.0;
|
||||
int totalwaves = 1 + wave_count_big;
|
||||
if (reporter)
|
||||
reporter->report_progress(0, "Precalculating small waveforms");
|
||||
float tmp[ORGAN_WAVE_SIZE];
|
||||
static bandlimiter<ORGAN_WAVE_BITS> bl;
|
||||
static bandlimiter<ORGAN_BIG_WAVE_BITS> blBig;
|
||||
for (int i = 0; i < ORGAN_WAVE_SIZE; i++)
|
||||
tmp[i] = sin(i * 2 * M_PI / ORGAN_WAVE_SIZE);
|
||||
waves[wave_sine].make(bl, tmp);
|
||||
for (int i = 0; i < ORGAN_WAVE_SIZE; i++)
|
||||
tmp[i] = (i < (ORGAN_WAVE_SIZE / 16)) ? 1 : 0;
|
||||
normalize_waveform(tmp, ORGAN_WAVE_SIZE);
|
||||
waves[wave_pulse].make(bl, tmp);
|
||||
for (int i = 0; i < ORGAN_WAVE_SIZE; i++)
|
||||
tmp[i] = i < (ORGAN_WAVE_SIZE / 2) ? sin(i * 2 * 2 * M_PI / ORGAN_WAVE_SIZE) : 0;
|
||||
waves[wave_sinepl1].make(bl, tmp);
|
||||
for (int i = 0; i < ORGAN_WAVE_SIZE; i++)
|
||||
tmp[i] = i < (ORGAN_WAVE_SIZE / 3) ? sin(i * 3 * 2 * M_PI / ORGAN_WAVE_SIZE) : 0;
|
||||
waves[wave_sinepl2].make(bl, tmp);
|
||||
for (int i = 0; i < ORGAN_WAVE_SIZE; i++)
|
||||
tmp[i] = i < (ORGAN_WAVE_SIZE / 4) ? sin(i * 4 * 2 * M_PI / ORGAN_WAVE_SIZE) : 0;
|
||||
waves[wave_sinepl3].make(bl, tmp);
|
||||
for (int i = 0; i < ORGAN_WAVE_SIZE; i++)
|
||||
tmp[i] = (i < (ORGAN_WAVE_SIZE / 2)) ? 1 : -1;
|
||||
normalize_waveform(tmp, ORGAN_WAVE_SIZE);
|
||||
waves[wave_sqr].make(bl, tmp);
|
||||
for (int i = 0; i < ORGAN_WAVE_SIZE; i++)
|
||||
tmp[i] = -1 + (i * 2.0 / ORGAN_WAVE_SIZE);
|
||||
normalize_waveform(tmp, ORGAN_WAVE_SIZE);
|
||||
waves[wave_saw].make(bl, tmp);
|
||||
|
||||
for (int i = 0; i < ORGAN_WAVE_SIZE; i++)
|
||||
tmp[i] = (i < (ORGAN_WAVE_SIZE / 2)) ? 1 : -1;
|
||||
normalize_waveform(tmp, ORGAN_WAVE_SIZE);
|
||||
smoothen(bl, tmp);
|
||||
waves[wave_ssqr].make(bl, tmp);
|
||||
|
||||
for (int i = 0; i < ORGAN_WAVE_SIZE; i++)
|
||||
tmp[i] = -1 + (i * 2.0 / ORGAN_WAVE_SIZE);
|
||||
normalize_waveform(tmp, ORGAN_WAVE_SIZE);
|
||||
smoothen(bl, tmp);
|
||||
waves[wave_ssaw].make(bl, tmp);
|
||||
|
||||
for (int i = 0; i < ORGAN_WAVE_SIZE; i++)
|
||||
tmp[i] = (i < (ORGAN_WAVE_SIZE / 16)) ? 1 : 0;
|
||||
normalize_waveform(tmp, ORGAN_WAVE_SIZE);
|
||||
smoothen(bl, tmp);
|
||||
waves[wave_spls].make(bl, tmp);
|
||||
for (int i = 0; i < ORGAN_WAVE_SIZE; i++)
|
||||
tmp[i] = i < (ORGAN_WAVE_SIZE / 1.5) ? sin(i * 1.5 * 2 * M_PI / ORGAN_WAVE_SIZE) : 0;
|
||||
normalize_waveform(tmp, ORGAN_WAVE_SIZE);
|
||||
waves[wave_sinepl05].make(bl, tmp);
|
||||
for (int i = 0; i < ORGAN_WAVE_SIZE; i++)
|
||||
tmp[i] = i < (ORGAN_WAVE_SIZE / 1.5) ? (i < ORGAN_WAVE_SIZE / 3 ? -1 : +1) : 0;
|
||||
normalize_waveform(tmp, ORGAN_WAVE_SIZE);
|
||||
waves[wave_sqr05].make(bl, tmp);
|
||||
for (int i = 0; i < ORGAN_WAVE_SIZE; i++)
|
||||
tmp[i] = sin(i * M_PI / ORGAN_WAVE_SIZE);
|
||||
normalize_waveform(tmp, ORGAN_WAVE_SIZE);
|
||||
waves[wave_halfsin].make(bl, tmp);
|
||||
for (int i = 0; i < ORGAN_WAVE_SIZE; i++)
|
||||
tmp[i] = sin(i * 3 * M_PI / ORGAN_WAVE_SIZE);
|
||||
normalize_waveform(tmp, ORGAN_WAVE_SIZE);
|
||||
waves[wave_clvg].make(bl, tmp);
|
||||
for (int i = 0; i < ORGAN_WAVE_SIZE; i++)
|
||||
{
|
||||
float ph = i * 2 * M_PI / ORGAN_WAVE_SIZE;
|
||||
float fm = 0.3 * sin(6*ph) + 0.2 * sin(11*ph) + 0.2 * cos(17*ph) - 0.2 * cos(19*ph);
|
||||
tmp[i] = sin(5*ph + fm) + 0.7 * cos(7*ph - fm);
|
||||
}
|
||||
normalize_waveform(tmp, ORGAN_WAVE_SIZE);
|
||||
waves[wave_bell].make(bl, tmp, true);
|
||||
for (int i = 0; i < ORGAN_WAVE_SIZE; i++)
|
||||
{
|
||||
float ph = i * 2 * M_PI / ORGAN_WAVE_SIZE;
|
||||
float fm = 0.3 * sin(3*ph) + 0.3 * sin(11*ph) + 0.3 * cos(17*ph) - 0.3 * cos(19*ph) + 0.3 * cos(25*ph) - 0.3 * cos(31*ph) + 0.3 * cos(37*ph);
|
||||
tmp[i] = sin(3*ph + fm) + cos(7*ph - fm);
|
||||
}
|
||||
normalize_waveform(tmp, ORGAN_WAVE_SIZE);
|
||||
waves[wave_bell2].make(bl, tmp, true);
|
||||
for (int i = 0; i < ORGAN_WAVE_SIZE; i++)
|
||||
{
|
||||
float ph = i * 2 * M_PI / ORGAN_WAVE_SIZE;
|
||||
float fm = 0.5 * sin(3*ph) + 0.3 * sin(5*ph) + 0.3 * cos(6*ph) - 0.3 * cos(9*ph);
|
||||
tmp[i] = sin(4*ph + fm) + cos(ph - fm);
|
||||
}
|
||||
normalize_waveform(tmp, ORGAN_WAVE_SIZE);
|
||||
waves[wave_w1].make(bl, tmp);
|
||||
for (int i = 0; i < ORGAN_WAVE_SIZE; i++)
|
||||
{
|
||||
float ph = i * 2 * M_PI / ORGAN_WAVE_SIZE;
|
||||
tmp[i] = sin(ph) * sin(2 * ph) * sin(4 * ph) * sin(8 * ph);
|
||||
}
|
||||
normalize_waveform(tmp, ORGAN_WAVE_SIZE);
|
||||
waves[wave_w2].make(bl, tmp);
|
||||
for (int i = 0; i < ORGAN_WAVE_SIZE; i++)
|
||||
{
|
||||
float ph = i * 2 * M_PI / ORGAN_WAVE_SIZE;
|
||||
tmp[i] = sin(ph) * sin(3 * ph) * sin(5 * ph) * sin(7 * ph) * sin(9 * ph);
|
||||
}
|
||||
normalize_waveform(tmp, ORGAN_WAVE_SIZE);
|
||||
waves[wave_w3].make(bl, tmp);
|
||||
for (int i = 0; i < ORGAN_WAVE_SIZE; i++)
|
||||
{
|
||||
float ph = i * 2 * M_PI / ORGAN_WAVE_SIZE;
|
||||
tmp[i] = sin(ph + 2 * sin(ph + 2 * sin(ph)));
|
||||
}
|
||||
normalize_waveform(tmp, ORGAN_WAVE_SIZE);
|
||||
waves[wave_w4].make(bl, tmp);
|
||||
for (int i = 0; i < ORGAN_WAVE_SIZE; i++)
|
||||
{
|
||||
float ph = i * 2 * M_PI / ORGAN_WAVE_SIZE;
|
||||
tmp[i] = ph * sin(ph);
|
||||
}
|
||||
normalize_waveform(tmp, ORGAN_WAVE_SIZE);
|
||||
waves[wave_w5].make(bl, tmp);
|
||||
for (int i = 0; i < ORGAN_WAVE_SIZE; i++)
|
||||
{
|
||||
float ph = i * 2 * M_PI / ORGAN_WAVE_SIZE;
|
||||
tmp[i] = ph * sin(ph) + (2 * M_PI - ph) * sin(2 * ph);
|
||||
}
|
||||
normalize_waveform(tmp, ORGAN_WAVE_SIZE);
|
||||
waves[wave_w6].make(bl, tmp);
|
||||
for (int i = 0; i < ORGAN_WAVE_SIZE; i++)
|
||||
{
|
||||
float ph = i * 1.0 / ORGAN_WAVE_SIZE;
|
||||
tmp[i] = exp(-ph * ph);
|
||||
}
|
||||
normalize_waveform(tmp, ORGAN_WAVE_SIZE);
|
||||
waves[wave_w7].make(bl, tmp);
|
||||
for (int i = 0; i < ORGAN_WAVE_SIZE; i++)
|
||||
{
|
||||
float ph = i * 1.0 / ORGAN_WAVE_SIZE;
|
||||
tmp[i] = exp(-ph * sin(2 * M_PI * ph));
|
||||
}
|
||||
normalize_waveform(tmp, ORGAN_WAVE_SIZE);
|
||||
waves[wave_w8].make(bl, tmp);
|
||||
for (int i = 0; i < ORGAN_WAVE_SIZE; i++)
|
||||
{
|
||||
float ph = i * 1.0 / ORGAN_WAVE_SIZE;
|
||||
tmp[i] = sin(2 * M_PI * ph * ph);
|
||||
}
|
||||
normalize_waveform(tmp, ORGAN_WAVE_SIZE);
|
||||
waves[wave_w9].make(bl, tmp);
|
||||
|
||||
for (int i = 0; i < ORGAN_WAVE_SIZE; i++)
|
||||
tmp[i] = -1 + (i * 2.0 / ORGAN_WAVE_SIZE);
|
||||
normalize_waveform(tmp, ORGAN_WAVE_SIZE);
|
||||
phaseshift(bl, tmp);
|
||||
waves[wave_dsaw].make(bl, tmp);
|
||||
|
||||
for (int i = 0; i < ORGAN_WAVE_SIZE; i++)
|
||||
tmp[i] = (i < (ORGAN_WAVE_SIZE / 2)) ? 1 : -1;
|
||||
normalize_waveform(tmp, ORGAN_WAVE_SIZE);
|
||||
phaseshift(bl, tmp);
|
||||
waves[wave_dsqr].make(bl, tmp);
|
||||
|
||||
for (int i = 0; i < ORGAN_WAVE_SIZE; i++)
|
||||
tmp[i] = (i < (ORGAN_WAVE_SIZE / 16)) ? 1 : 0;
|
||||
normalize_waveform(tmp, ORGAN_WAVE_SIZE);
|
||||
phaseshift(bl, tmp);
|
||||
waves[wave_dpls].make(bl, tmp);
|
||||
|
||||
LARGE_WAVEFORM_PROGRESS();
|
||||
for (int i = 0; i < ORGAN_WAVE_SIZE; i++)
|
||||
tmp[i] = -1 + (i * 2.0 / ORGAN_WAVE_SIZE);
|
||||
normalize_waveform(tmp, ORGAN_WAVE_SIZE);
|
||||
bl.compute_spectrum(tmp);
|
||||
padsynth(bl, blBig, big_waves[wave_strings - wave_count_small], 15);
|
||||
|
||||
LARGE_WAVEFORM_PROGRESS();
|
||||
for (int i = 0; i < ORGAN_WAVE_SIZE; i++)
|
||||
tmp[i] = -1 + (i * 2.0 / ORGAN_WAVE_SIZE);
|
||||
normalize_waveform(tmp, ORGAN_WAVE_SIZE);
|
||||
bl.compute_spectrum(tmp);
|
||||
padsynth(bl, blBig, big_waves[wave_strings2 - wave_count_small], 40);
|
||||
|
||||
LARGE_WAVEFORM_PROGRESS();
|
||||
for (int i = 0; i < ORGAN_WAVE_SIZE; i++)
|
||||
tmp[i] = sin(i * 2 * M_PI / ORGAN_WAVE_SIZE);
|
||||
normalize_waveform(tmp, ORGAN_WAVE_SIZE);
|
||||
bl.compute_spectrum(tmp);
|
||||
padsynth(bl, blBig, big_waves[wave_sinepad - wave_count_small], 20);
|
||||
|
||||
LARGE_WAVEFORM_PROGRESS();
|
||||
for (int i = 0; i < ORGAN_WAVE_SIZE; i++)
|
||||
{
|
||||
float ph = i * 2 * M_PI / ORGAN_WAVE_SIZE;
|
||||
float fm = 0.3 * sin(6*ph) + 0.2 * sin(11*ph) + 0.2 * cos(17*ph) - 0.2 * cos(19*ph);
|
||||
tmp[i] = sin(5*ph + fm) + 0.7 * cos(7*ph - fm);
|
||||
}
|
||||
normalize_waveform(tmp, ORGAN_WAVE_SIZE);
|
||||
bl.compute_spectrum(tmp);
|
||||
padsynth(bl, blBig, big_waves[wave_bellpad - wave_count_small], 30, 30, true);
|
||||
|
||||
LARGE_WAVEFORM_PROGRESS();
|
||||
for (int i = 0; i < ORGAN_WAVE_SIZE; i++)
|
||||
{
|
||||
float ph = i * 2 * M_PI / ORGAN_WAVE_SIZE;
|
||||
float fm = 0.3 * sin(3*ph) + 0.2 * sin(4*ph) + 0.2 * cos(5*ph) - 0.2 * cos(6*ph);
|
||||
tmp[i] = sin(2*ph + fm) + 0.7 * cos(3*ph - fm);
|
||||
}
|
||||
normalize_waveform(tmp, ORGAN_WAVE_SIZE);
|
||||
bl.compute_spectrum(tmp);
|
||||
padsynth(bl, blBig, big_waves[wave_space - wave_count_small], 30, 30);
|
||||
|
||||
LARGE_WAVEFORM_PROGRESS();
|
||||
for (int i = 0; i < ORGAN_WAVE_SIZE; i++)
|
||||
{
|
||||
float ph = i * 2 * M_PI / ORGAN_WAVE_SIZE;
|
||||
float fm = 0.5 * sin(ph) + 0.5 * sin(2*ph) + 0.5 * sin(3*ph);
|
||||
tmp[i] = sin(ph + fm) + 0.5 * cos(7*ph - 2 * fm) + 0.25 * cos(13*ph - 4 * fm);
|
||||
}
|
||||
normalize_waveform(tmp, ORGAN_WAVE_SIZE);
|
||||
bl.compute_spectrum(tmp);
|
||||
padsynth(bl, blBig, big_waves[wave_choir - wave_count_small], 50, 10);
|
||||
|
||||
LARGE_WAVEFORM_PROGRESS();
|
||||
for (int i = 0; i < ORGAN_WAVE_SIZE; i++)
|
||||
{
|
||||
float ph = i * 2 * M_PI / ORGAN_WAVE_SIZE;
|
||||
float fm = sin(ph) ;
|
||||
tmp[i] = sin(ph + fm) + 0.25 * cos(11*ph - 2 * fm) + 0.125 * cos(23*ph - 2 * fm) + 0.0625 * cos(49*ph - 2 * fm);
|
||||
}
|
||||
normalize_waveform(tmp, ORGAN_WAVE_SIZE);
|
||||
bl.compute_spectrum(tmp);
|
||||
padsynth(bl, blBig, big_waves[wave_choir2 - wave_count_small], 50, 10);
|
||||
|
||||
LARGE_WAVEFORM_PROGRESS();
|
||||
for (int i = 0; i < ORGAN_WAVE_SIZE; i++)
|
||||
{
|
||||
float ph = i * 2 * M_PI / ORGAN_WAVE_SIZE;
|
||||
float fm = sin(ph) ;
|
||||
tmp[i] = sin(ph + 4 * fm) + 0.5 * sin(2 * ph + 4 * ph);
|
||||
}
|
||||
normalize_waveform(tmp, ORGAN_WAVE_SIZE);
|
||||
bl.compute_spectrum(tmp);
|
||||
padsynth(bl, blBig, big_waves[wave_choir3 - wave_count_small], 50, 10);
|
||||
LARGE_WAVEFORM_PROGRESS();
|
||||
|
||||
inited = true;
|
||||
}
|
||||
}
|
||||
|
||||
organ_voice_base::organ_voice_base(organ_parameters *_parameters, int &_sample_rate_ref, bool &_released_ref)
|
||||
: parameters(_parameters)
|
||||
, sample_rate_ref(_sample_rate_ref)
|
||||
, released_ref(_released_ref)
|
||||
{
|
||||
note = -1;
|
||||
}
|
||||
|
||||
void organ_voice_base::render_percussion_to(float (*buf)[2], int nsamples)
|
||||
{
|
||||
if (note == -1)
|
||||
return;
|
||||
|
||||
if (!pamp.get_active())
|
||||
return;
|
||||
if (parameters->percussion_level < small_value<float>())
|
||||
return;
|
||||
float level = parameters->percussion_level * 9;
|
||||
static float zeros[ORGAN_WAVE_SIZE];
|
||||
// XXXKF the decay needs work!
|
||||
double age_const = parameters->perc_decay_const;
|
||||
double fm_age_const = parameters->perc_fm_decay_const;
|
||||
int timbre = parameters->get_percussion_wave();
|
||||
if (timbre < 0 || timbre >= wave_count_small)
|
||||
return;
|
||||
int timbre2 = parameters->get_percussion_fm_wave();
|
||||
if (timbre2 < 0 || timbre2 >= wave_count_small)
|
||||
timbre2 = wave_sine;
|
||||
float *fmdata = (*waves)[timbre2].get_level(moddphase.get());
|
||||
if (!fmdata)
|
||||
fmdata = zeros;
|
||||
float *data = (*waves)[timbre].get_level(dpphase.get());
|
||||
if (!data) {
|
||||
pamp.deactivate();
|
||||
return;
|
||||
}
|
||||
float s = parameters->percussion_stereo * ORGAN_WAVE_SIZE * (0.5 / 360.0);
|
||||
for (int i = 0; i < nsamples; i++) {
|
||||
float fm = wave(fmdata, modphase);
|
||||
fm *= ORGAN_WAVE_SIZE * parameters->percussion_fm_depth * fm_amp.get();
|
||||
modphase += moddphase;
|
||||
fm_amp.age_exp(fm_age_const, 1.0 / 32768.0);
|
||||
|
||||
float lamp = level * pamp.get();
|
||||
buf[i][0] += lamp * wave(data, pphase + dsp::fixed_point<int64_t, 52>(fm - s));
|
||||
buf[i][1] += lamp * wave(data, pphase + dsp::fixed_point<int64_t, 52>(fm + s));
|
||||
if (released_ref)
|
||||
pamp.age_lin(rel_age_const,0.0);
|
||||
else
|
||||
pamp.age_exp(age_const, 1.0 / 32768.0);
|
||||
pphase += dpphase;
|
||||
}
|
||||
}
|
||||
|
||||
void organ_vibrato::reset()
|
||||
{
|
||||
for (int i = 0; i < VibratoSize; i++)
|
||||
vibrato_x1[i][0] = vibrato_y1[i][0] = vibrato_x1[i][1] = vibrato_y1[i][1] = 0.f;
|
||||
vibrato[0].a0 = vibrato[1].a0 = 0;
|
||||
lfo_phase = 0.f;
|
||||
}
|
||||
|
||||
void organ_vibrato::process(organ_parameters *parameters, float (*data)[2], unsigned int len, float sample_rate)
|
||||
{
|
||||
float lfo1 = lfo_phase < 0.5 ? 2 * lfo_phase : 2 - 2 * lfo_phase;
|
||||
float lfo_phase2 = lfo_phase + parameters->lfo_phase * (1.0 / 360.0);
|
||||
if (lfo_phase2 >= 1.0)
|
||||
lfo_phase2 -= 1.0;
|
||||
float lfo2 = lfo_phase2 < 0.5 ? 2 * lfo_phase2 : 2 - 2 * lfo_phase2;
|
||||
lfo_phase += parameters->lfo_rate * len / sample_rate;
|
||||
if (lfo_phase >= 1.0)
|
||||
lfo_phase -= 1.0;
|
||||
if (!len)
|
||||
return;
|
||||
float olda0[2] = {vibrato[0].a0, vibrato[1].a0};
|
||||
vibrato[0].set_ap(3000 + 7000 * parameters->lfo_amt * lfo1 * lfo1, sample_rate);
|
||||
vibrato[1].set_ap(3000 + 7000 * parameters->lfo_amt * lfo2 * lfo2, sample_rate);
|
||||
float ilen = 1.0 / len;
|
||||
float deltaa0[2] = {(vibrato[0].a0 - olda0[0])*ilen, (vibrato[1].a0 - olda0[1])*ilen};
|
||||
|
||||
float vib_wet = parameters->lfo_wet;
|
||||
for (int c = 0; c < 2; c++)
|
||||
{
|
||||
for (unsigned int i = 0; i < len; i++)
|
||||
{
|
||||
float v = data[i][c];
|
||||
float v0 = v;
|
||||
float coeff = olda0[c] + deltaa0[c] * i;
|
||||
for (int t = 0; t < VibratoSize; t++)
|
||||
v = vibrato[c].process_ap(v, vibrato_x1[t][c], vibrato_y1[t][c], coeff);
|
||||
|
||||
data[i][c] += (v - v0) * vib_wet;
|
||||
}
|
||||
for (int t = 0; t < VibratoSize; t++)
|
||||
{
|
||||
sanitize(vibrato_x1[t][c]);
|
||||
sanitize(vibrato_y1[t][c]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void organ_voice::update_pitch()
|
||||
{
|
||||
organ_voice_base::update_pitch();
|
||||
dphase.set(dsp::midi_note_to_phase(note, 100 * parameters->global_transpose + parameters->global_detune, sample_rate) * inertia_pitchbend.get_last());
|
||||
}
|
||||
|
||||
void organ_voice::render_block() {
|
||||
if (note == -1)
|
||||
return;
|
||||
|
||||
dsp::zero(&output_buffer[0][0], Channels * BlockSize);
|
||||
dsp::zero(&aux_buffers[1][0][0], 2 * Channels * BlockSize);
|
||||
if (!amp.get_active())
|
||||
{
|
||||
if (use_percussion())
|
||||
render_percussion_to(output_buffer, BlockSize);
|
||||
return;
|
||||
}
|
||||
|
||||
inertia_pitchbend.set_inertia(parameters->pitch_bend);
|
||||
inertia_pitchbend.step();
|
||||
update_pitch();
|
||||
dsp::fixed_point<int, 20> tphase, tdphase;
|
||||
unsigned int foldvalue = parameters->foldvalue * inertia_pitchbend.get_last();
|
||||
int vibrato_mode = fastf2i_drm(parameters->lfo_mode);
|
||||
for (int h = 0; h < 9; h++)
|
||||
{
|
||||
float amp = parameters->drawbars[h];
|
||||
if (amp < small_value<float>())
|
||||
continue;
|
||||
float *data;
|
||||
dsp::fixed_point<int, 24> hm = dsp::fixed_point<int, 24>(parameters->multiplier[h]);
|
||||
int waveid = (int)parameters->waveforms[h];
|
||||
if (waveid < 0 || waveid >= wave_count)
|
||||
waveid = 0;
|
||||
|
||||
uint32_t rate = (dphase * hm).get();
|
||||
if (waveid >= wave_count_small)
|
||||
{
|
||||
float *data = (*big_waves)[waveid - wave_count_small].get_level(rate >> (ORGAN_BIG_WAVE_BITS - ORGAN_WAVE_BITS + ORGAN_BIG_WAVE_SHIFT));
|
||||
if (!data)
|
||||
continue;
|
||||
hm.set(hm.get() >> ORGAN_BIG_WAVE_SHIFT);
|
||||
dsp::fixed_point<int64_t, 20> tphase, tdphase;
|
||||
tphase.set(((phase * hm).get()) + parameters->phaseshift[h]);
|
||||
tdphase.set(rate >> ORGAN_BIG_WAVE_SHIFT);
|
||||
float ampl = amp * 0.5f * (1 - parameters->pan[h]);
|
||||
float ampr = amp * 0.5f * (1 + parameters->pan[h]);
|
||||
float (*out)[Channels] = aux_buffers[dsp::fastf2i_drm(parameters->routing[h])];
|
||||
|
||||
for (int i=0; i < (int)BlockSize; i++) {
|
||||
float wv = big_wave(data, tphase);
|
||||
out[i][0] += wv * ampl;
|
||||
out[i][1] += wv * ampr;
|
||||
tphase += tdphase;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned int foldback = 0;
|
||||
while (rate > foldvalue)
|
||||
{
|
||||
rate >>= 1;
|
||||
foldback++;
|
||||
}
|
||||
hm.set(hm.get() >> foldback);
|
||||
data = (*waves)[waveid].get_level(rate);
|
||||
if (!data)
|
||||
continue;
|
||||
tphase.set((uint32_t)((phase * hm).get()) + parameters->phaseshift[h]);
|
||||
tdphase.set((uint32_t)rate);
|
||||
float ampl = amp * 0.5f * (1 - parameters->pan[h]);
|
||||
float ampr = amp * 0.5f * (1 + parameters->pan[h]);
|
||||
float (*out)[Channels] = aux_buffers[dsp::fastf2i_drm(parameters->routing[h])];
|
||||
|
||||
for (int i=0; i < (int)BlockSize; i++) {
|
||||
float wv = wave(data, tphase);
|
||||
out[i][0] += wv * ampl;
|
||||
out[i][1] += wv * ampr;
|
||||
tphase += tdphase;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool is_quad = parameters->quad_env >= 0.5f;
|
||||
|
||||
expression.set_inertia(parameters->cutoff);
|
||||
phase += dphase * BlockSize;
|
||||
float escl[EnvCount], eval[EnvCount];
|
||||
for (int i = 0; i < EnvCount; i++)
|
||||
escl[i] = (1.f + parameters->envs[i].velscale * (velocity - 1.f));
|
||||
|
||||
if (is_quad)
|
||||
{
|
||||
for (int i = 0; i < EnvCount; i++)
|
||||
eval[i] = envs[i].value * envs[i].value * escl[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < EnvCount; i++)
|
||||
eval[i] = envs[i].value * escl[i];
|
||||
}
|
||||
for (int i = 0; i < FilterCount; i++)
|
||||
{
|
||||
float mod = parameters->filters[i].envmod[0] * eval[0] ;
|
||||
mod += parameters->filters[i].keyf * 100 * (note - 60);
|
||||
for (int j = 1; j < EnvCount; j++)
|
||||
{
|
||||
mod += parameters->filters[i].envmod[j] * eval[j];
|
||||
}
|
||||
if (i) mod += expression.get() * 1200 * 4;
|
||||
float fc = parameters->filters[i].cutoff * pow(2.0f, mod * (1.f / 1200.f));
|
||||
if (i == 0 && parameters->filter1_type >= 0.5f)
|
||||
filterL[i].set_hp_rbj(dsp::clip<float>(fc, 10, 18000), parameters->filters[i].resonance, sample_rate);
|
||||
else
|
||||
filterL[i].set_lp_rbj(dsp::clip<float>(fc, 10, 18000), parameters->filters[i].resonance, sample_rate);
|
||||
filterR[i].copy_coeffs(filterL[i]);
|
||||
}
|
||||
float amp_pre[ampctl_count - 1], amp_post[ampctl_count - 1];
|
||||
for (int i = 0; i < ampctl_count - 1; i++)
|
||||
{
|
||||
amp_pre[i] = 1.f;
|
||||
amp_post[i] = 1.f;
|
||||
}
|
||||
bool any_running = false;
|
||||
for (int i = 0; i < EnvCount; i++)
|
||||
{
|
||||
float pre = eval[i];
|
||||
envs[i].advance();
|
||||
int mode = fastf2i_drm(parameters->envs[i].ampctl);
|
||||
if (!envs[i].stopped())
|
||||
any_running = true;
|
||||
if (mode == ampctl_none)
|
||||
continue;
|
||||
float post = (is_quad ? envs[i].value : 1) * envs[i].value * escl[i];
|
||||
amp_pre[mode - 1] *= pre;
|
||||
amp_post[mode - 1] *= post;
|
||||
}
|
||||
if (vibrato_mode >= lfomode_direct && vibrato_mode <= lfomode_filter2)
|
||||
vibrato.process(parameters, aux_buffers[vibrato_mode - lfomode_direct], BlockSize, sample_rate);
|
||||
if (!any_running)
|
||||
finishing = true;
|
||||
// calculate delta from pre and post
|
||||
for (int i = 0; i < ampctl_count - 1; i++)
|
||||
amp_post[i] = (amp_post[i] - amp_pre[i]) * (1.0 / BlockSize);
|
||||
float a0 = amp_pre[0], a1 = amp_pre[1], a2 = amp_pre[2], a3 = amp_pre[3];
|
||||
float d0 = amp_post[0], d1 = amp_post[1], d2 = amp_post[2], d3 = amp_post[3];
|
||||
if (parameters->filter_chain >= 0.5f)
|
||||
{
|
||||
for (int i=0; i < (int) BlockSize; i++) {
|
||||
output_buffer[i][0] = a3 * (a0 * output_buffer[i][0] + a2 * filterL[1].process(a1 * filterL[0].process(aux_buffers[1][i][0]) + aux_buffers[2][i][0]));
|
||||
output_buffer[i][1] = a3 * (a0 * output_buffer[i][1] + a2 * filterR[1].process(a1 * filterR[0].process(aux_buffers[1][i][1]) + aux_buffers[2][i][1]));
|
||||
a0 += d0, a1 += d1, a2 += d2, a3 += d3;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i=0; i < (int) BlockSize; i++) {
|
||||
output_buffer[i][0] = a3 * (a0 * output_buffer[i][0] + a1 * filterL[0].process(aux_buffers[1][i][0]) + a2 * filterL[1].process(aux_buffers[2][i][0]));
|
||||
output_buffer[i][1] = a3 * (a0 * output_buffer[i][1] + a1 * filterR[0].process(aux_buffers[1][i][1]) + a2 * filterR[1].process(aux_buffers[2][i][1]));
|
||||
a0 += d0, a1 += d1, a2 += d2, a3 += d3;
|
||||
}
|
||||
}
|
||||
filterL[0].sanitize();
|
||||
filterR[0].sanitize();
|
||||
filterL[1].sanitize();
|
||||
filterR[1].sanitize();
|
||||
if (vibrato_mode == lfomode_voice)
|
||||
vibrato.process(parameters, output_buffer, BlockSize, sample_rate);
|
||||
|
||||
if (finishing)
|
||||
{
|
||||
for (int i = 0; i < (int) BlockSize; i++) {
|
||||
output_buffer[i][0] *= amp.get();
|
||||
output_buffer[i][1] *= amp.get();
|
||||
amp.age_lin((1.0/44100.0)/0.03,0.0);
|
||||
}
|
||||
}
|
||||
|
||||
if (use_percussion())
|
||||
render_percussion_to(output_buffer, BlockSize);
|
||||
}
|
||||
|
||||
void drawbar_organ::update_params()
|
||||
{
|
||||
parameters->perc_decay_const = dsp::decay::calc_exp_constant(1.0 / 1024.0, 0.001 * parameters->percussion_time * sample_rate);
|
||||
parameters->perc_fm_decay_const = dsp::decay::calc_exp_constant(1.0 / 1024.0, 0.001 * parameters->percussion_fm_time * sample_rate);
|
||||
for (int i = 0; i < 9; i++)
|
||||
{
|
||||
parameters->multiplier[i] = parameters->harmonics[i] * pow(2.0, parameters->detune[i] * (1.0 / 1200.0));
|
||||
parameters->phaseshift[i] = int(parameters->phase[i] * 65536 / 360) << 16;
|
||||
}
|
||||
double dphase = dsp::midi_note_to_phase((int)parameters->foldover, 0, sample_rate);
|
||||
parameters->foldvalue = (int)(dphase);
|
||||
}
|
||||
|
||||
void drawbar_organ::pitch_bend(int amt)
|
||||
{
|
||||
parameters->pitch_bend = pow(2.0, (amt * parameters->pitch_bend_range) / (1200.0 * 8192.0));
|
||||
for (list<voice *>::iterator i = active_voices.begin(); i != active_voices.end(); i++)
|
||||
{
|
||||
organ_voice *v = dynamic_cast<organ_voice *>(*i);
|
||||
v->update_pitch();
|
||||
}
|
||||
percussion.update_pitch();
|
||||
}
|
||||
|
||||
void organ_audio_module::execute(int cmd_no)
|
||||
{
|
||||
switch(cmd_no)
|
||||
{
|
||||
case 0:
|
||||
panic_flag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void organ_voice_base::perc_note_on(int note, int vel)
|
||||
{
|
||||
perc_reset();
|
||||
released_ref = false;
|
||||
this->note = note;
|
||||
if (parameters->percussion_level > 0)
|
||||
pamp.set(1.0f + (vel - 127) * parameters->percussion_vel2amp / 127.0);
|
||||
update_pitch();
|
||||
float (*kt)[2] = parameters->percussion_keytrack;
|
||||
// assume last point (will be put there by padding)
|
||||
fm_keytrack = kt[ORGAN_KEYTRACK_POINTS - 1][1];
|
||||
// yes binary search would be nice if we had more than those crappy 4 points
|
||||
for (int i = 0; i < ORGAN_KEYTRACK_POINTS - 1; i++)
|
||||
{
|
||||
float &lower = kt[i][0], upper = kt[i + 1][0];
|
||||
if (note >= lower && note < upper)
|
||||
{
|
||||
fm_keytrack = kt[i][1] + (note - lower) * (kt[i + 1][1] - kt[i][1]) / (upper - lower);
|
||||
break;
|
||||
}
|
||||
}
|
||||
fm_amp.set(fm_keytrack * (1.0f + (vel - 127) * parameters->percussion_vel2fm / 127.0));
|
||||
}
|
||||
|
||||
char *organ_audio_module::configure(const char *key, const char *value)
|
||||
{
|
||||
if (!strcmp(key, "map_curve"))
|
||||
{
|
||||
var_map_curve = value;
|
||||
stringstream ss(value);
|
||||
int i = 0;
|
||||
float x = 0, y = 1;
|
||||
if (*value)
|
||||
{
|
||||
int points;
|
||||
ss >> points;
|
||||
for (i = 0; i < points; i++)
|
||||
{
|
||||
static const int whites[] = { 0, 2, 4, 5, 7, 9, 11 };
|
||||
ss >> x >> y;
|
||||
int wkey = (int)(x * 71);
|
||||
x = whites[wkey % 7] + 12 * (wkey / 7);
|
||||
parameters->percussion_keytrack[i][0] = x;
|
||||
parameters->percussion_keytrack[i][1] = y;
|
||||
// cout << "(" << x << ", " << y << ")" << endl;
|
||||
}
|
||||
}
|
||||
// pad with constant Y
|
||||
for (; i < ORGAN_KEYTRACK_POINTS; i++) {
|
||||
parameters->percussion_keytrack[i][0] = x;
|
||||
parameters->percussion_keytrack[i][1] = y;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
cout << "Set unknown configure value " << key << " to " << value << endl;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void organ_audio_module::send_configures(send_configure_iface *sci)
|
||||
{
|
||||
sci->send_configure("map_curve", var_map_curve.c_str());
|
||||
}
|
||||
|
||||
void organ_audio_module::deactivate()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void drawbar_organ::render_separate(float *output[], int nsamples)
|
||||
{
|
||||
float buf[4096][2];
|
||||
dsp::zero(&buf[0][0], 2 * nsamples);
|
||||
basic_synth::render_to(buf, nsamples);
|
||||
if (dsp::fastf2i_drm(parameters->lfo_mode) == organ_voice_base::lfomode_global)
|
||||
{
|
||||
for (int i = 0; i < nsamples; i += 64)
|
||||
global_vibrato.process(parameters, buf + i, std::min(64, nsamples - i), sample_rate);
|
||||
}
|
||||
if (percussion.get_active())
|
||||
percussion.render_percussion_to(buf, nsamples);
|
||||
float gain = parameters->master * (1.0 / 8);
|
||||
eq_l.set(parameters->bass_freq, parameters->bass_gain, parameters->treble_freq, parameters->treble_gain, sample_rate);
|
||||
eq_r.copy_coeffs(eq_l);
|
||||
for (int i=0; i<nsamples; i++) {
|
||||
output[0][i] = gain*eq_l.process(buf[i][0]);
|
||||
output[1][i] = gain*eq_r.process(buf[i][1]);
|
||||
}
|
||||
eq_l.sanitize();
|
||||
eq_r.sanitize();
|
||||
}
|
||||
101
plugins/ladspa_effect/calf/plugin.cpp
Normal file
101
plugins/ladspa_effect/calf/plugin.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
/* Calf DSP Library
|
||||
* Example audio modules - LADSPA/DSSI/LV2 wrapper instantiation
|
||||
*
|
||||
* Copyright (C) 2001-2008 Krzysztof Foltman
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include <calf/ladspa_wrap.h>
|
||||
#include <calf/lv2wrap.h>
|
||||
#include <calf/modules.h>
|
||||
#include <calf/modules_dev.h>
|
||||
#include <calf/modules_small.h>
|
||||
|
||||
using namespace calf_plugins;
|
||||
|
||||
#if USE_LADSPA
|
||||
template<class Module>
|
||||
LADSPA_Descriptor ladspa_wrapper<Module>::descriptor;
|
||||
|
||||
template<class Module>
|
||||
LADSPA_Descriptor ladspa_wrapper<Module>::descriptor_for_dssi;
|
||||
|
||||
#if USE_DSSI
|
||||
|
||||
template<class Module>
|
||||
DSSI_Descriptor ladspa_wrapper<Module>::dssi_descriptor;
|
||||
|
||||
template<class Module>
|
||||
DSSI_Program_Descriptor ladspa_wrapper<Module>::dssi_default_program;
|
||||
|
||||
template<class Module>
|
||||
std::vector<plugin_preset> *ladspa_wrapper<Module>::presets;
|
||||
|
||||
template<class Module>
|
||||
std::vector<DSSI_Program_Descriptor> *ladspa_wrapper<Module>::preset_descs;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if USE_LV2
|
||||
// instantiate descriptor templates
|
||||
template<class Module> LV2_Descriptor calf_plugins::lv2_wrapper<Module>::descriptor;
|
||||
template<class Module> LV2_Calf_Descriptor calf_plugins::lv2_wrapper<Module>::calf_descriptor;
|
||||
template<class Module> LV2MessageContext calf_plugins::lv2_wrapper<Module>::message_context;
|
||||
|
||||
extern "C" {
|
||||
|
||||
const LV2_Descriptor *lv2_descriptor(uint32_t index)
|
||||
{
|
||||
#define PER_MODULE_ITEM(name, isSynth, jackname) if (!(index--)) return &lv2_wrapper<name##_audio_module>::get().descriptor;
|
||||
#include <calf/modulelist.h>
|
||||
#ifdef ENABLE_EXPERIMENTAL
|
||||
return lv2_small_descriptor(index);
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#if USE_LADSPA
|
||||
extern "C" {
|
||||
|
||||
const LADSPA_Descriptor *ladspa_descriptor(unsigned long Index)
|
||||
{
|
||||
#define PER_MODULE_ITEM(name, isSynth, jackname) if (!isSynth && !(Index--)) return &ladspa_wrapper<name##_audio_module>::get().descriptor;
|
||||
#include <calf/modulelist.h>
|
||||
return NULL;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#if USE_DSSI
|
||||
extern "C" {
|
||||
|
||||
const DSSI_Descriptor *dssi_descriptor(unsigned long Index)
|
||||
{
|
||||
#define PER_MODULE_ITEM(name, isSynth, jackname) if (!(Index--)) return &calf_plugins::ladspa_wrapper<name##_audio_module>::get().dssi_descriptor;
|
||||
#include <calf/modulelist.h>
|
||||
return NULL;
|
||||
}
|
||||
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
228
plugins/ladspa_effect/calf/synth.cpp
Normal file
228
plugins/ladspa_effect/calf/synth.cpp
Normal file
@@ -0,0 +1,228 @@
|
||||
/* Calf DSP Library
|
||||
* Generic polyphonic synthesizer framework.
|
||||
*
|
||||
* Copyright (C) 2007 Krzysztof Foltman
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <memory.h>
|
||||
#if USE_JACK
|
||||
#include <jack/jack.h>
|
||||
#endif
|
||||
#include <calf/giface.h>
|
||||
#include <calf/synth.h>
|
||||
|
||||
using namespace dsp;
|
||||
using namespace std;
|
||||
|
||||
void basic_synth::kill_note(int note, int vel, bool just_one)
|
||||
{
|
||||
for (list<dsp::voice *>::iterator it = active_voices.begin(); it != active_voices.end(); it++) {
|
||||
// preserve sostenuto notes
|
||||
if ((*it)->get_current_note() == note && !(sostenuto && (*it)->sostenuto)) {
|
||||
(*it)->note_off(vel);
|
||||
if (just_one)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dsp::voice *basic_synth::give_voice()
|
||||
{
|
||||
if (active_voices.size() >= polyphony_limit)
|
||||
{
|
||||
dsp::voice *stolen = steal_voice();
|
||||
if (stolen)
|
||||
return stolen;
|
||||
}
|
||||
if (unused_voices.empty())
|
||||
return alloc_voice();
|
||||
else {
|
||||
dsp::voice *v = unused_voices.top();
|
||||
unused_voices.pop();
|
||||
v->reset();
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
dsp::voice *basic_synth::steal_voice()
|
||||
{
|
||||
std::list<dsp::voice *>::iterator found = active_voices.end();
|
||||
float priority = 10000;
|
||||
//int idx = 0;
|
||||
for(std::list<dsp::voice *>::iterator i = active_voices.begin(); i != active_voices.end(); i++)
|
||||
{
|
||||
//printf("Voice %d priority %f at %p\n", idx++, (*i)->get_priority(), *i);
|
||||
if ((*i)->get_priority() < priority)
|
||||
{
|
||||
priority = (*i)->get_priority();
|
||||
found = i;
|
||||
}
|
||||
}
|
||||
//printf("Found: %p\n\n", *found);
|
||||
if (found == active_voices.end())
|
||||
return NULL;
|
||||
|
||||
(*found)->steal();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void basic_synth::trim_voices()
|
||||
{
|
||||
// count stealable voices
|
||||
unsigned int count = 0;
|
||||
for(std::list<dsp::voice *>::iterator i = active_voices.begin(); i != active_voices.end(); i++)
|
||||
{
|
||||
if ((*i)->get_priority() < 10000)
|
||||
count++;
|
||||
}
|
||||
// printf("Count=%d limit=%d\n", count, polyphony_limit);
|
||||
// steal any voices above polyphony limit
|
||||
if (count > polyphony_limit) {
|
||||
for (unsigned int i = 0; i < count - polyphony_limit; i++)
|
||||
steal_voice();
|
||||
}
|
||||
}
|
||||
|
||||
void basic_synth::note_on(int note, int vel)
|
||||
{
|
||||
if (!vel) {
|
||||
note_off(note, 0);
|
||||
return;
|
||||
}
|
||||
bool perc = check_percussion();
|
||||
dsp::voice *v = give_voice();
|
||||
v->setup(sample_rate);
|
||||
v->released = false;
|
||||
v->sostenuto = false;
|
||||
gate.set(note);
|
||||
v->note_on(note, vel);
|
||||
active_voices.push_back(v);
|
||||
if (perc) {
|
||||
percussion_note_on(note, vel);
|
||||
}
|
||||
}
|
||||
|
||||
void basic_synth::note_off(int note, int vel)
|
||||
{
|
||||
gate.reset(note);
|
||||
if (!hold)
|
||||
kill_note(note, vel, false);
|
||||
}
|
||||
|
||||
#define for_all_voices(iter) for (std::list<dsp::voice *>::iterator iter = active_voices.begin(); iter != active_voices.end(); iter++)
|
||||
|
||||
void basic_synth::on_pedal_release()
|
||||
{
|
||||
for_all_voices(i)
|
||||
{
|
||||
int note = (*i)->get_current_note();
|
||||
if (note < 0 || note > 127)
|
||||
continue;
|
||||
bool still_held = gate[note];
|
||||
// sostenuto pedal released
|
||||
if ((*i)->sostenuto && !sostenuto)
|
||||
{
|
||||
// mark note as non-sostenuto
|
||||
(*i)->sostenuto = false;
|
||||
// if key still pressed or hold pedal used, hold the note (as non-sostenuto so it can be released later by releasing the key or pedal)
|
||||
// if key has been released and hold pedal is not depressed, release the note
|
||||
if (!still_held && !hold)
|
||||
(*i)->note_off(127);
|
||||
}
|
||||
else if (!hold && !still_held && !(*i)->released)
|
||||
{
|
||||
(*i)->released = true;
|
||||
(*i)->note_off(127);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void basic_synth::control_change(int ctl, int val)
|
||||
{
|
||||
if (ctl == 64) { // HOLD controller
|
||||
bool prev = hold;
|
||||
hold = (val >= 64);
|
||||
if (!hold && prev && !sostenuto) {
|
||||
on_pedal_release();
|
||||
}
|
||||
}
|
||||
if (ctl == 66) { // SOSTENUTO controller
|
||||
bool prev = sostenuto;
|
||||
sostenuto = (val >= 64);
|
||||
if (sostenuto && !prev) {
|
||||
// SOSTENUTO was pressed - move all notes onto sustain stack
|
||||
for_all_voices(i) {
|
||||
(*i)->sostenuto = true;
|
||||
}
|
||||
}
|
||||
if (!sostenuto && prev) {
|
||||
// SOSTENUTO was released - release all keys which were previously held
|
||||
on_pedal_release();
|
||||
}
|
||||
}
|
||||
if (ctl == 123 || ctl == 120) { // all notes off, all sounds off
|
||||
vector<int> notes;
|
||||
notes.reserve(128);
|
||||
if (ctl == 120) { // for "all sounds off", automatically release hold and sostenuto pedal
|
||||
control_change(66, 0);
|
||||
control_change(64, 0);
|
||||
}
|
||||
for_all_voices(i)
|
||||
{
|
||||
if (ctl == 123)
|
||||
(*i)->note_off(127);
|
||||
else
|
||||
(*i)->steal();
|
||||
}
|
||||
}
|
||||
if (ctl == 121) {
|
||||
control_change(1, 0);
|
||||
control_change(7, 100);
|
||||
control_change(10, 64);
|
||||
control_change(11, 127);
|
||||
// release hold..hold2
|
||||
for (int i = 64; i <= 69; i++)
|
||||
control_change(i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void basic_synth::render_to(float (*output)[2], int nsamples)
|
||||
{
|
||||
// render voices, eliminate ones that aren't sounding anymore
|
||||
for (list<dsp::voice *>::iterator i = active_voices.begin(); i != active_voices.end();) {
|
||||
dsp::voice *v = *i;
|
||||
v->render_to(output, nsamples);
|
||||
if (!v->get_active()) {
|
||||
i = active_voices.erase(i);
|
||||
unused_voices.push(v);
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
basic_synth::~basic_synth()
|
||||
{
|
||||
while(!unused_voices.empty()) {
|
||||
delete unused_voices.top();
|
||||
unused_voices.pop();
|
||||
}
|
||||
for (list<voice *>::iterator i = active_voices.begin(); i != active_voices.end(); i++)
|
||||
delete *i;
|
||||
}
|
||||
|
||||
132
plugins/ladspa_effect/calf/utils.cpp
Normal file
132
plugins/ladspa_effect/calf/utils.cpp
Normal file
@@ -0,0 +1,132 @@
|
||||
/* Calf DSP Library
|
||||
* Various utility functions.
|
||||
* Copyright (C) 2007 Krzysztof Foltman
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <calf/osctl.h>
|
||||
#include <calf/utils.h>
|
||||
#include <sstream>
|
||||
|
||||
using namespace std;
|
||||
using namespace osctl;
|
||||
|
||||
namespace calf_utils {
|
||||
|
||||
string encode_map(const dictionary &data)
|
||||
{
|
||||
osctl::string_buffer sb;
|
||||
osc_stream<osctl::string_buffer> str(sb);
|
||||
str << (uint32_t)data.size();
|
||||
for(dictionary::const_iterator i = data.begin(); i != data.end(); i++)
|
||||
{
|
||||
str << i->first << i->second;
|
||||
}
|
||||
return sb.data;
|
||||
}
|
||||
|
||||
void decode_map(dictionary &data, const string &src)
|
||||
{
|
||||
osctl::string_buffer sb(src);
|
||||
osc_stream<osctl::string_buffer> str(sb);
|
||||
uint32_t count = 0;
|
||||
str >> count;
|
||||
string tmp, tmp2;
|
||||
data.clear();
|
||||
for (uint32_t i = 0; i < count; i++)
|
||||
{
|
||||
str >> tmp;
|
||||
str >> tmp2;
|
||||
data[tmp] = tmp2;
|
||||
}
|
||||
}
|
||||
|
||||
std::string xml_escape(const std::string &src)
|
||||
{
|
||||
string dest;
|
||||
for (size_t i = 0; i < src.length(); i++) {
|
||||
// XXXKF take care of string encoding
|
||||
if (src[i] < 0 || src[i] == '"' || src[i] == '<' || src[i] == '>' || src[i] == '&')
|
||||
dest += "&"+i2s((uint8_t)src[i])+";";
|
||||
else
|
||||
dest += src[i];
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
std::string load_file(const std::string &src)
|
||||
{
|
||||
std::string str;
|
||||
FILE *f = fopen(src.c_str(), "rb");
|
||||
#if 0
|
||||
if (!f)
|
||||
throw file_exception(src);
|
||||
#endif
|
||||
while(!feof(f))
|
||||
{
|
||||
char buffer[1024];
|
||||
int len = fread(buffer, 1, sizeof(buffer), f);
|
||||
#if 0
|
||||
if (len < 0)
|
||||
throw file_exception(src);
|
||||
#endif
|
||||
str += string(buffer, len);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string i2s(int value)
|
||||
{
|
||||
char buf[32];
|
||||
sprintf(buf, "%d", value);
|
||||
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
std::string f2s(double value)
|
||||
{
|
||||
stringstream ss;
|
||||
ss << value;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string ff2s(double value)
|
||||
{
|
||||
string s = f2s(value);
|
||||
if (s.find('.') == string::npos)
|
||||
s += ".0";
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string indent(const std::string &src, const std::string &indent)
|
||||
{
|
||||
std::string dest;
|
||||
size_t pos = 0;
|
||||
do {
|
||||
size_t epos = src.find("\n", pos);
|
||||
if (epos == string::npos)
|
||||
break;
|
||||
dest += indent + src.substr(pos, epos - pos) + "\n";
|
||||
pos = epos + 1;
|
||||
} while(pos < src.length());
|
||||
if (pos < src.length())
|
||||
dest += indent + src.substr(pos);
|
||||
return dest;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user