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:
Tobias Doerffel
2009-08-10 13:42:09 +02:00
parent 3e62853de5
commit ad1124cde9
51 changed files with 16288 additions and 0 deletions

View File

@@ -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"

View File

@@ -14,6 +14,10 @@ IF(WANT_CMT)
ADD_SUBDIRECTORY(cmt)
ENDIF(WANT_CMT)
IF(WANT_CALF)
ADD_SUBDIRECTORY(calf)
ENDIF(WANT_CALF)
INCLUDE(BuildPlugin)

View 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>

View 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)

View 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!

View 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.

View 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

View 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.

View File

View 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.

View 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)

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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 &param_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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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 &microname = 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 &microname = 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 &microname = 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 &microname = "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 &target;
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 &microname = 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 &microname = 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 &microname = "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

View 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

View 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

View 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

View 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

View 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> &params)
{
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

View 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;
}
}
}

View 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>
}

View 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;
}

File diff suppressed because it is too large Load Diff

View 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;
}

View 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();
}

View 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

View 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;
}

View 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;
}
}