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.
(cherry picked from commit 35ca0aab69)
This commit is contained in:
@@ -33,6 +33,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)
|
||||
@@ -118,6 +119,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")
|
||||
@@ -604,6 +612,7 @@ MESSAGE(
|
||||
"* VST-instrument hoster : ${STATUS_VST}\n"
|
||||
"* VST-effect hoster : ${STATUS_VST}\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