From 995986bf364773fa08bb173ca4c59707c6a969ea Mon Sep 17 00:00:00 2001 From: Packit Date: Sep 30 2020 09:27:04 +0000 Subject: ladspa_sdk base --- diff --git a/README b/README new file mode 100644 index 0000000..9c3c16e --- /dev/null +++ b/README @@ -0,0 +1,12 @@ +LADSPA SDK +---------- + +The API itself can be be found in src/ladspa.h. The rest of this +package is a software development kit to make the API even easier to +work with. + +See http://www.ladspa.org for news and updates to the API and SDK. See +http://www.ladspa.org/cmt for a basic plugin library. The latter is +GPL'd and contains `full' versions of all the plugins contained in the +example libraries. + diff --git a/doc/COPYING b/doc/COPYING new file mode 100644 index 0000000..07d2ddf --- /dev/null +++ b/doc/COPYING @@ -0,0 +1,514 @@ + + 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. +^L + 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. +^L + 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. +^L + 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. +^L + 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. +^L + 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. +^L + 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. +^L + 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 +^L + 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. + + + + Copyright (C) + + 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. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/doc/background.html b/doc/background.html new file mode 100644 index 0000000..ba9af8d --- /dev/null +++ b/doc/background.html @@ -0,0 +1,22 @@ +

Background

+ +

The Linux Audio Developer's Simple Plugin API (LADSPA) originates +in frustration on the part of Richard Furse with the lack +of consensus on a standard audio plugin API for Linux. The API is +intended to describe a very light-weight plugin form that can be +handled by many types of host. To achieve this simplicity various +compromises are made, notably the presence of only one data type +(float).

+ +

Technical documentation is contained in the ladspa.h header file. When +clashes occur between this file and external documentation then the +header file should be considered the definitive version.

+ +

The ladspa.h header file includes direct contributions +by Richard W.E. Furse, Paul Barton-Davis and Stefan Westerfeld. Many +additional contributions have been made by the members of the Linux +Audio Developer's mailing list after whom the plugin is named. Thanks +in particular to Alexander Konig.

+ diff --git a/doc/changes.html b/doc/changes.html new file mode 100644 index 0000000..ecf0a85 --- /dev/null +++ b/doc/changes.html @@ -0,0 +1,120 @@ +

Changes

+ +

[Version numbers relate to the SDK, not the ladspa.h +header file itself.]

+ +

Version 1.01 - 2 Apr 2000

+
    + +
  • Initial Release with header file, limited documentation, three +plugins and two hosts.
  • + +
+ +

Version 1.02 - 4 May 2000

+
    + +
  • Introduction of version numbering for SDK.
  • + +
  • Plugins added, bringing basic example set to ten. Further +development of basic plugins for serious use shifted away to the CMT +project.
  • + +
  • Bug fix to applyplugin when handling channel count changes.
  • + +
  • Introduce support for the LADSPA_PATH environment +variable to both example hosts.
  • + +
+ +

Version 1.03 - 4 May 2000

+
    + +
  • Documentation Converted to HTML.
  • + +
+ +

Version 1.04 - 11 May 2000

+
    + +
  • Use _init() and _fini() in example +plugins.
  • + +
+ +

Version 1.05 - 14 May 2000

+
    + +
  • Updated to correspond to http://www.ladspa.org/.
  • + +
+ +

Version 1.06 - 18 May 2000

+
    + +
  • Add listplugins program.
  • + +
+ +

Version 1.07 - 24 Sep 2000

+
    + +
  • Discourage reliance on LD_LIBRARY_PATH by stopping analyseplugin +and applyplugin from searching it when looking for LADSPA +plugins.
  • + +
+ +

Version 1.08 - 30 Sep 2000

+
    + +
  • Use constructor/destructor rather than _fini() and _init() in +C++. Use C++ for linkage.
  • + +
+ +

Version 1.09 - 4 Nov 2000

+
    + +
  • Add optional plugin label parameter to analyseplugin.
  • + +
+ +

Version 1.10 - 8 May 2001

+
    + +
  • Introduction of LGPL license.
  • + +
  • Removal of superfluous semicolon on line 492 of header file.
  • + +
+ +

Version 1.11 - 21 Jul 2001

+
    + +
  • Remove memory leak in search code.
  • + +
+ +

Version 1.12 - 7 Aug 2002

+
    + +
  • Update for LADSPA v1.1 (default values and 1.0f=0dB).
  • + +
+ +

Version 1.13 - 6 Nov 2007

+
    + +
  • Fix compile error in sine.cpp (GCC4).
  • + +
  • Fix typo in text output by analyseplugin.
  • + +
  • Extra usage text in analyseplugin and applyplugin.
  • + +
  • Replace strdup() with localStrdup() in sine.cpp to avoid +malloc/new mismatch.
  • + +
  • Remove "local" part from install directories.
  • + +
diff --git a/doc/download.html b/doc/download.html new file mode 100644 index 0000000..fdeb431 --- /dev/null +++ b/doc/download.html @@ -0,0 +1,20 @@ +

Download

+ +

Please select the file you wish to download:

+ + + + + + + + + + + + + +
FileDescription
+ladspa_sdk.tgzThe LADSPA SDK, including the ladspa.h API header +file, ten example LADSPA plugins and three example programs +(applyplugin, analyseplugin and listplugins).
diff --git a/doc/example_plugins.html b/doc/example_plugins.html new file mode 100644 index 0000000..a42250d --- /dev/null +++ b/doc/example_plugins.html @@ -0,0 +1,90 @@ +

Example Plugins

+ +

Ten example plugins are provided. These provide a rudimentary basis +for a computer-music synthesis kit. They are written simply and may be +used as the basis for your creations. Note that the programming is not +of a massively high quality: in particular, memory management is +rather crude and failures during malloc() will produce +unpleasant behaviour.

+ +

Note that these plugins are examples. More polished versions of +them all are included within the CMT plugin +set.

+ +

The following plugins are provided:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
File NamePlugin LabelDescription
amp.soamp_monoMono Amplifier.
amp.soamp_stereoStereo Amplifier.
filter.solpfSimple Low Pass Filter.
filter.sohpfSimple High Pass Filter
delay.sodelay_5sSimple Delay Line. The delay time may be varied up to 5 +seconds. No feedback is provided.
sine.sosine_faaaSine Oscillator. Frequency input is audio, Amplitude input is +audio.
sine.sosine_faacSimple Oscillator. Frequency input is audio, Amplitude input is +control.
sine.sosine_fcaaSimple Oscillator. Frequency input is control, Amplitude input is +audio.
sine.sosine_fcacSimple Oscillator. Frequency input is control, Amplitude input is +control.
noise.sonoise_whiteWhite noise source.
diff --git a/doc/index.html b/doc/index.html new file mode 100644 index 0000000..fb7f61b --- /dev/null +++ b/doc/index.html @@ -0,0 +1,25 @@ +

LADSPA SDK Index

+ + + +

Other Links

+ + + +

The author Richard Furse can be emailed as +richard@muse.demon.co.uk. +

diff --git a/doc/installation.html b/doc/installation.html new file mode 100644 index 0000000..1b0b339 --- /dev/null +++ b/doc/installation.html @@ -0,0 +1,19 @@ +

Installation

+ +

This distribution includes both the ladspa.h API header file and a +number of hosts and example plugins. Go to the src/ +directory and type make to build and test them.

+ +

The ladspa.h API header +file is needed to build hosts and plugins. It may be useful to +install it in /usr/local/include/ or +/usr/include/. The programs generated in the +bin/ directory may be moved to +/usr/local/bin/ or /usr/bin/ and the plugins +generated in the plugins/ directory may be moved to +/usr/local/lib/ladspa/ or +/usr/lib/ladspa/.

+ +

To perform automatic installation, log in as root and run +make install. This by default will install plugins, hosts +and the header file into the /usr/local/ tree.

diff --git a/doc/ladspa.h.txt b/doc/ladspa.h.txt new file mode 120000 index 0000000..92b4c22 --- /dev/null +++ b/doc/ladspa.h.txt @@ -0,0 +1 @@ +../src/ladspa.h \ No newline at end of file diff --git a/doc/license.html b/doc/license.html new file mode 100644 index 0000000..3e740ff --- /dev/null +++ b/doc/license.html @@ -0,0 +1,13 @@ +

LADSPA and LADSPA SDK License

+ +

The LADSPA Software Development Kit and the LADSPA plugin API +itself are licensed under LGPL version 2.1.

+ +

Please note that this is not intended to be the final license +for LADSPA. In the long term it is hoped that LADSPA will have a +public license that is even less restrictive, so that commercial +applications can use it without having to use a derived LGPL library +(in a way that still protects the open-source community). It may be +that LGPL is already free enough for this, but we aren't sure. Does +anyone want to pay for a lawyer? In the meantime, please mail me if +this is an issue for you.

diff --git a/doc/overview.html b/doc/overview.html new file mode 100644 index 0000000..e19e21f --- /dev/null +++ b/doc/overview.html @@ -0,0 +1,34 @@ +

LADSPA SDK v1.13 Overview

+ +

There is a large number of synthesis packages in use or development +on the Linux platform at this time. The Linux Audio Developer's Simple +Plugin API (LADSPA) attempts to give programmers the ability to write +simple `plugin' audio processors in C/C++ and link them dynamically +against a range of host applications.

+ +

Definitive technical documentation on LADSPA plugins for both host +and plugin writers is contained within copious comments in the ladspa.h header file.

+ +

This SDK provides:

+ +
    + +
  • The API header file.
  • + +
  • Ten simple example plugins that may be used as the basis for +further development.
  • + +
  • A program (`analyseplugin') which analyses a plugin library and +describes the plugins within it. This program may be used as the basis +for further development.
  • + +
  • A simple host program (`applyplugin') which allows a chain of +plugins to modify a Wave file. This host may be used as the basis for +further development.
  • + +
  • A program (`listplugins') which searches directories on the +LADSPA_PATH for LADSPA plugins. This program may be used +as the basis for further development.
  • + +
diff --git a/doc/shared_plugins.html b/doc/shared_plugins.html new file mode 100644 index 0000000..721c6a2 --- /dev/null +++ b/doc/shared_plugins.html @@ -0,0 +1,22 @@ +

Shared Plugins

+ +

Plugins may be used by a range of different hosts. However, the +host needs to be able to find the plugins. Hosts vary, however the +recommended method uses the environment variable +LADSPA_PATH. If present, this should contain a +colon-separated path indicating directories that should be searched +(in order) when loading plugin types.

+ +

It is recommended for standard Linux distributions such as RedHat, +that this plugin path should be +/home/<user>/.ladspa:/usr/local/lib/ladspa:/usr/lib/ladspa. +Plugins can then be installed into /usr/local/lib/ladspa/ +or /usr/lib/ladspa/.

+ +

You may wish to add a line such as the following to your +.bash_profile login file (if you use Bash): export +LADSPA_PATH=$LADSPA_PATH:/home/<user>/.ladspa:/usr/local/lib/ladspa:/usr/lib/ladspa

+ +

To list the plugins that can be found on your +LADSPA_PATH, run the listplugins +program.

diff --git a/doc/unique_ids.html b/doc/unique_ids.html new file mode 100644 index 0000000..c923867 --- /dev/null +++ b/doc/unique_ids.html @@ -0,0 +1,17 @@ +

Unique IDs

+ +

Plugin types are identified by unique IDs. At the time of writing +plugin writers may request these from ladspa@muse.demon.co.uk. +This may change, in which case +http://www.ladspa.org is likely to provide information on new +sources.

+ +

Plugin IDs 1-1000 are reserved for development use and plugins must +not be released publicly with these IDs as clashes are likely. Plugin +ID 0 will never be allocated.

+ +

It is hoped that plugin IDs will fit into 24bits. In the unlikely +event that this range needs to be extended it will be, possibly with +the addition of a second dimension (e.g. developer ID). In the +meantime hosts may assume that IDs will not exceed 0x00FFFFFF.

diff --git a/snd/noise.wav b/snd/noise.wav new file mode 100644 index 0000000..6a3044b Binary files /dev/null and b/snd/noise.wav differ diff --git a/src/analyseplugin.c b/src/analyseplugin.c new file mode 100644 index 0000000..502e0f9 --- /dev/null +++ b/src/analyseplugin.c @@ -0,0 +1,405 @@ +/* analyseplugin.c + + Free software by Richard W.E. Furse. Do with as you will. No + warranty. */ + +/*****************************************************************************/ + +#include +#include +#include +#include + +/*****************************************************************************/ + +#include "ladspa.h" + +#include "utils.h" + +/*****************************************************************************/ + +/* Returns 0 if all goes well, otherwise returns 1. Label may be null + indicating `all plugins.' */ +static int +analysePlugin(const char * pcPluginFilename, + const char * pcPluginLabel, + const int bVerbose) { + + LADSPA_Descriptor_Function pfDescriptorFunction; + const LADSPA_Descriptor * psDescriptor; + unsigned long lPluginIndex; + unsigned long lPortIndex; + unsigned long lSpaceIndex; + unsigned long lSpacePadding1; + unsigned long lSpacePadding2; + unsigned long lLength; + void * pvPluginHandle; + LADSPA_PortRangeHintDescriptor iHintDescriptor; + LADSPA_Data fBound; + LADSPA_Data fDefault; + + pvPluginHandle = loadLADSPAPluginLibrary(pcPluginFilename); + + dlerror(); + pfDescriptorFunction + = (LADSPA_Descriptor_Function)dlsym(pvPluginHandle, "ladspa_descriptor"); + if (!pfDescriptorFunction) { + const char * pcError = dlerror(); + if (pcError) + fprintf(stderr, + "Unable to find ladspa_descriptor() function in plugin file " + "\"%s\": %s.\n" + "Are you sure this is a LADSPA plugin file?\n", + pcPluginFilename, + pcError); + return 1; + } + + lSpacePadding1 = 0; + lSpacePadding2 = 0; + if (!bVerbose) { + for (lPluginIndex = 0;; lPluginIndex++) { + psDescriptor = pfDescriptorFunction(lPluginIndex); + if (!psDescriptor) + break; + if (pcPluginLabel != NULL) + if (strcmp(pcPluginLabel, psDescriptor->Label) != 0) + continue; + + lLength = strlen(psDescriptor->Label); + if (lSpacePadding1 < lLength) + lSpacePadding1 = lLength; + + lLength = (long)(log10(psDescriptor->UniqueID)) + 1; + if (lSpacePadding2 < lLength) + lSpacePadding2 = lLength; + } + lSpacePadding1 += 2; + lSpacePadding2 += 2; + } + + for (lPluginIndex = 0;; lPluginIndex++) { + + psDescriptor = pfDescriptorFunction(lPluginIndex); + if (!psDescriptor) + break; + if (pcPluginLabel != NULL) + if (strcmp(pcPluginLabel, psDescriptor->Label) != 0) + continue; + + if (!bVerbose) { + printf("%s", psDescriptor->Label); + for (lSpaceIndex = strlen(psDescriptor->Label); + lSpaceIndex < lSpacePadding1; + lSpaceIndex++) + putchar(' '); + printf("%lu", psDescriptor->UniqueID); + for (lSpaceIndex = (long)(log10(psDescriptor->UniqueID)) + 1; + lSpaceIndex < lSpacePadding2; + lSpaceIndex++) + putchar(' '); + puts(psDescriptor->Name); + } + else { + + putchar('\n'); + + printf("Plugin Name: \"%s\"\n", psDescriptor->Name); + printf("Plugin Label: \"%s\"\n", psDescriptor->Label); + printf("Plugin Unique ID: %lu\n", psDescriptor->UniqueID); + printf("Maker: \"%s\"\n", psDescriptor->Maker); + printf("Copyright: \"%s\"\n", psDescriptor->Copyright); + + printf("Must Run Real-Time: "); + if (LADSPA_IS_REALTIME(psDescriptor->Properties)) + printf("Yes\n"); + else + printf("No\n"); + + printf("Has activate() Function: "); + if (psDescriptor->activate != NULL) + printf("Yes\n"); + else + printf("No\n"); + printf("Has deactivate() Function: "); + if (psDescriptor->deactivate != NULL) + printf("Yes\n"); + else + printf("No\n"); + printf("Has run_adding() Function: "); + if (psDescriptor->run_adding != NULL) + printf("Yes\n"); + else + printf("No\n"); + + if (psDescriptor->instantiate == NULL) + printf("ERROR: PLUGIN HAS NO INSTANTIATE FUNCTION.\n"); + if (psDescriptor->connect_port == NULL) + printf("ERROR: PLUGIN HAS NO CONNECT_PORT FUNCTION.\n"); + if (psDescriptor->run == NULL) + printf("ERROR: PLUGIN HAS NO RUN FUNCTION.\n"); + if (psDescriptor->run_adding != NULL + && psDescriptor->set_run_adding_gain == NULL) + printf("ERROR: PLUGIN HAS RUN_ADDING FUNCTION BUT " + "NOT SET_RUN_ADDING_GAIN.\n"); + if (psDescriptor->run_adding == NULL + && psDescriptor->set_run_adding_gain != NULL) + printf("ERROR: PLUGIN HAS SET_RUN_ADDING_GAIN FUNCTION BUT " + "NOT RUN_ADDING.\n"); + if (psDescriptor->cleanup == NULL) + printf("ERROR: PLUGIN HAS NO CLEANUP FUNCTION.\n"); + + printf("Environment: "); + if (LADSPA_IS_HARD_RT_CAPABLE(psDescriptor->Properties)) + printf("Normal or Hard Real-Time\n"); + else + printf("Normal\n"); + + if (LADSPA_IS_INPLACE_BROKEN(psDescriptor->Properties)) + printf("This plugin cannot use in-place processing. " + "It will not work with all hosts.\n"); + + printf("Ports:"); + + if (psDescriptor->PortCount == 0) + printf("\tERROR: PLUGIN HAS NO PORTS.\n"); + + for (lPortIndex = 0; + lPortIndex < psDescriptor->PortCount; + lPortIndex++) { + + printf("\t\"%s\" ", psDescriptor->PortNames[lPortIndex]); + + if (LADSPA_IS_PORT_INPUT + (psDescriptor->PortDescriptors[lPortIndex]) + && LADSPA_IS_PORT_OUTPUT + (psDescriptor->PortDescriptors[lPortIndex])) + printf("ERROR: INPUT AND OUTPUT"); + else if (LADSPA_IS_PORT_INPUT + (psDescriptor->PortDescriptors[lPortIndex])) + printf("input"); + else if (LADSPA_IS_PORT_OUTPUT + (psDescriptor->PortDescriptors[lPortIndex])) + printf("output"); + else + printf("ERROR: NEITHER INPUT NOR OUTPUT"); + + if (LADSPA_IS_PORT_CONTROL + (psDescriptor->PortDescriptors[lPortIndex]) + && LADSPA_IS_PORT_AUDIO + (psDescriptor->PortDescriptors[lPortIndex])) + printf(", ERROR: CONTROL AND AUDIO"); + else if (LADSPA_IS_PORT_CONTROL + (psDescriptor->PortDescriptors[lPortIndex])) + printf(", control"); + else if (LADSPA_IS_PORT_AUDIO + (psDescriptor->PortDescriptors[lPortIndex])) + printf(", audio"); + else + printf(", ERROR: NEITHER CONTROL NOR AUDIO"); + + iHintDescriptor + = psDescriptor->PortRangeHints[lPortIndex].HintDescriptor; + + if (LADSPA_IS_HINT_BOUNDED_BELOW(iHintDescriptor) + || LADSPA_IS_HINT_BOUNDED_ABOVE(iHintDescriptor)) { + printf(", "); + if (LADSPA_IS_HINT_BOUNDED_BELOW(iHintDescriptor)) { + fBound = psDescriptor->PortRangeHints[lPortIndex].LowerBound; + if (LADSPA_IS_HINT_SAMPLE_RATE(iHintDescriptor) && fBound != 0) + printf("%g*srate", fBound); + else + printf("%g", fBound); + } + else + printf("..."); + printf(" to "); + if (LADSPA_IS_HINT_BOUNDED_ABOVE(iHintDescriptor)) { + fBound = psDescriptor->PortRangeHints[lPortIndex].UpperBound; + if (LADSPA_IS_HINT_SAMPLE_RATE(iHintDescriptor) && fBound != 0) + printf("%g*srate", fBound); + else + printf("%g", fBound); + } + else + printf("..."); + } + + if (LADSPA_IS_HINT_TOGGLED(iHintDescriptor)) { + if ((iHintDescriptor + | LADSPA_HINT_DEFAULT_0 + | LADSPA_HINT_DEFAULT_1) + != (LADSPA_HINT_TOGGLED + | LADSPA_HINT_DEFAULT_0 + | LADSPA_HINT_DEFAULT_1)) + printf(", ERROR: TOGGLED INCOMPATIBLE WITH OTHER HINT"); + else + printf(", toggled"); + } + + switch (iHintDescriptor & LADSPA_HINT_DEFAULT_MASK) { + case LADSPA_HINT_DEFAULT_NONE: + break; + case LADSPA_HINT_DEFAULT_MINIMUM: + fDefault = psDescriptor->PortRangeHints[lPortIndex].LowerBound; + if (LADSPA_IS_HINT_SAMPLE_RATE(iHintDescriptor) && fDefault != 0) + printf(", default %g*srate", fDefault); + else + printf(", default %g", fDefault); + break; + case LADSPA_HINT_DEFAULT_LOW: + if (LADSPA_IS_HINT_LOGARITHMIC(iHintDescriptor)) { + fDefault + = exp(log(psDescriptor->PortRangeHints[lPortIndex].LowerBound) + * 0.75 + + log(psDescriptor->PortRangeHints[lPortIndex].UpperBound) + * 0.25); + } + else { + fDefault + = (psDescriptor->PortRangeHints[lPortIndex].LowerBound + * 0.75 + + psDescriptor->PortRangeHints[lPortIndex].UpperBound + * 0.25); + } + if (LADSPA_IS_HINT_SAMPLE_RATE(iHintDescriptor) && fDefault != 0) + printf(", default %g*srate", fDefault); + else + printf(", default %g", fDefault); + break; + case LADSPA_HINT_DEFAULT_MIDDLE: + if (LADSPA_IS_HINT_LOGARITHMIC(iHintDescriptor)) { + fDefault + = sqrt(psDescriptor->PortRangeHints[lPortIndex].LowerBound + * psDescriptor->PortRangeHints[lPortIndex].UpperBound); + } + else { + fDefault + = 0.5 * (psDescriptor->PortRangeHints[lPortIndex].LowerBound + + psDescriptor->PortRangeHints[lPortIndex].UpperBound); + } + if (LADSPA_IS_HINT_SAMPLE_RATE(iHintDescriptor) && fDefault != 0) + printf(", default %g*srate", fDefault); + else + printf(", default %g", fDefault); + break; + case LADSPA_HINT_DEFAULT_HIGH: + if (LADSPA_IS_HINT_LOGARITHMIC(iHintDescriptor)) { + fDefault + = exp(log(psDescriptor->PortRangeHints[lPortIndex].LowerBound) + * 0.25 + + log(psDescriptor->PortRangeHints[lPortIndex].UpperBound) + * 0.75); + } + else { + fDefault + = (psDescriptor->PortRangeHints[lPortIndex].LowerBound + * 0.25 + + psDescriptor->PortRangeHints[lPortIndex].UpperBound + * 0.75); + } + if (LADSPA_IS_HINT_SAMPLE_RATE(iHintDescriptor) && fDefault != 0) + printf(", default %g*srate", fDefault); + else + printf(", default %g", fDefault); + break; + case LADSPA_HINT_DEFAULT_MAXIMUM: + fDefault = psDescriptor->PortRangeHints[lPortIndex].UpperBound; + if (LADSPA_IS_HINT_SAMPLE_RATE(iHintDescriptor) && fDefault != 0) + printf(", default %g*srate", fDefault); + else + printf(", default %g", fDefault); + break; + case LADSPA_HINT_DEFAULT_0: + printf(", default 0"); + break; + case LADSPA_HINT_DEFAULT_1: + printf(", default 1"); + break; + case LADSPA_HINT_DEFAULT_100: + printf(", default 100"); + break; + case LADSPA_HINT_DEFAULT_440: + printf(", default 440"); + break; + default: + printf(", UNKNOWN DEFAULT CODE"); + /* (Not necessarily an error - may be a newer version.) */ + break; + } + + if (LADSPA_IS_HINT_LOGARITHMIC(iHintDescriptor)) + printf(", logarithmic"); + + if (LADSPA_IS_HINT_INTEGER(iHintDescriptor)) + printf(", integer"); + + putchar('\n'); + } + } + } + + if (bVerbose) + putchar('\n'); + + unloadLADSPAPluginLibrary(pvPluginHandle); + + return(0); +} + +/*****************************************************************************/ + +int +main(const int iArgc, const char ** ppcArgv) { + + const char * pcPluginName = NULL; + const char * pcPluginLabel = NULL; + int bVerbose = 1; + + /* Check for a flag, but only at the start. Cannot get use getopt() + as it gets thoroughly confused when faced with negative numbers + on the command line. */ + switch (iArgc) { + case 2: + if (strcmp(ppcArgv[1], "-h") != 0) { + pcPluginName = ppcArgv[1]; + pcPluginLabel = NULL; + } + break; + case 3: + if (strcmp(ppcArgv[1], "-l") == 0) { + pcPluginName = ppcArgv[2]; + pcPluginLabel = NULL; + bVerbose = 0; + } + else { + pcPluginName = ppcArgv[1]; + pcPluginLabel = ppcArgv[2]; + } + break; + case 4: + if (strcmp(ppcArgv[1], "-l") == 0) { + pcPluginName = ppcArgv[2]; + pcPluginLabel = ppcArgv[3]; + bVerbose = 0; + } + break; + } + + if (!pcPluginName) { + fprintf(stderr, + "Usage:\tanalyseplugin [flags] " + "[].\n" + "Flags:" + "-l Produce a summary list rather than a verbose report.\n" + "Note that the LADSPA_PATH environment variable is used " + "to help find plugins.\n"); + return(1); + } + + return analysePlugin(pcPluginName, pcPluginLabel, bVerbose); +} + +/*****************************************************************************/ + +/* EOF */ diff --git a/src/applyplugin.c b/src/applyplugin.c new file mode 100644 index 0000000..335e89b --- /dev/null +++ b/src/applyplugin.c @@ -0,0 +1,816 @@ +/* applyplugin.c + + Free software by Richard W.E. Furse. Do with as you will. No + warranty. */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +/*****************************************************************************/ + +#include "ladspa.h" + +#include "utils.h" + +/*****************************************************************************/ + +#define BUFFER_SIZE 2048 + +/*****************************************************************************/ + +/* Macros provide hook for word-order swapping because of + big/little-endian issues. */ + +#ifndef BYTE_ORDER +#error "Could not determine byte order." +#endif + +#if (BYTE_ORDER == LITTLE_ENDIAN) + +/* (i386 is little-endian.) */ + +#define WORD_ORDER_SWAP_16BIT(ptr) +#define WORD_ORDER_SWAP_32BIT(ptr) + +#else + +/* The following isn't ANSI C, but it'll work fine on GCC. To make it + ANSI compiliant, make cTmp global, remove the braces (etc). Then + check carefully to see where WORD_ORDER_SWAP_* is + used. Alternatively, rewrite as procedures. */ + +#define WORD_ORDER_SWAP_16BIT(pvPtr) { \ + char cTmp, * pcPtr = (char *)pvPtr; \ + cTmp = pcPtr[0]; \ + pcPtr[0] = pcPtr[1]; \ + pcPtr[1] = cTmp; \ +} +#define WORD_ORDER_SWAP_32BIT(pvPtr) { \ + char cTmp, * pcPtr = (char *)pvPtr; \ + cTmp = pcPtr[0]; \ + pcPtr[0] = pcPtr[3]; \ + pcPtr[3] = cTmp; \ + cTmp = pcPtr[1]; \ + pcPtr[1] = pcPtr[2]; \ + pcPtr[2] = cTmp; \ +} + +#endif + +/*****************************************************************************/ + +/* Horrid hard-coded Wave Interface: + --------------------------------- */ +/* Please please feel free to replace this with something nice. + + Only handles 16bit PCM files in a single data block. Returns + channel count and sample rate by reference. Only one file can be + open for read at a time. */ + +FILE * g_poInputFile; +FILE * g_poOutputFile; +LADSPA_Data g_fPeakWritten = 0; + +unsigned long g_lInputFileChannelCount; +unsigned long g_lOutputFileChannelCount; + +short * g_psInputFileBuffer; +short * g_psOutputFileBuffer; + +static void +openWaveFile(const char * pcFilename, + unsigned long * plChannelCount, + unsigned long * plSampleRate, + unsigned long * plLength) { + + char pcHeader[44]; + size_t lReadLength; + + g_poInputFile = fopen(pcFilename, "rb"); + if (!g_poInputFile) { + fprintf(stderr, + "Failed to open input file \"%s\": %s\n", + pcFilename, + strerror(errno)); + exit(1); + } + + lReadLength = fread(pcHeader, sizeof(char), 44, g_poInputFile); + if (lReadLength < 44) { + fprintf(stderr, + "Failed to read header from input file \"%s\": %s\n", + pcFilename, + strerror(errno)); + exit(1); + } + if (strncmp(pcHeader + 0, "RIFF", 4) != 0 || + strncmp(pcHeader + 8, "WAVE", 4) != 0 || + strncmp(pcHeader + 12, "fmt ", 4) != 0 || + pcHeader[20] != 1 || pcHeader[21] != 0 || /* PCM */ + pcHeader[34] != 16 || pcHeader[35] != 0 || /* 16 bit */ + strncmp(pcHeader + 36, "data", 4) != 0) /* Data block elsewhere */ { + fprintf(stderr, + "\"applyplugin\" has very limited support for sound files types. " + "The file \"%s\" is not a simple 16bit PCM Wave File.\n", + pcFilename); + exit(1); + } + + /* Not portable casting (short assumed 16bit, unsigned int assumed + 32bit), alignment assumed not a problem. This is all bad. */ + WORD_ORDER_SWAP_16BIT(pcHeader + 22); + g_lInputFileChannelCount + = *plChannelCount + = *((unsigned short *)(pcHeader + 22)); + WORD_ORDER_SWAP_32BIT(pcHeader + 24); + *plSampleRate + = *((unsigned int *)(pcHeader + 24)); + WORD_ORDER_SWAP_32BIT(pcHeader + 40); + *plLength + = (*((unsigned int *)(pcHeader + 40)) + / (*plChannelCount * sizeof(short))); + + g_psInputFileBuffer + = (short *)calloc(*plChannelCount * BUFFER_SIZE, sizeof(short)); +} + +static void +createWaveFile(const char * pcFilename, + unsigned long lChannelCount, + unsigned long lSampleRate, + unsigned long lLength) { + + + char pcHeader[44]; + size_t lWriteLength; + + static const char pcHeaderBase[44] = { + 'R', 'I', 'F', 'F', + 0, 0, 0, 0, + 'W', 'A', 'V', 'E', + 'f', 'm', 't', ' ', + 16, 0, 0, 0, + 1, 0, + 1, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, + 16, 0, + 'd', 'a', 't', 'a', + 0, 0, 0, 0 + }; + + g_poOutputFile = fopen(pcFilename, "wb"); + if (!g_poOutputFile) { + fprintf(stderr, + "Failed to open output file \"%s\": %s\n", + pcFilename, + strerror(errno)); + exit(1); + } + + /* Not portable casting (short assumed 16bit, unsigned int assumed + 32bit), alignment assumed not a problem. This is all bad. */ + memcpy(pcHeader, pcHeaderBase, 44); + + *(unsigned int *)(pcHeader + 4) + = (unsigned int)(lLength * lChannelCount * sizeof(short) + 36); + WORD_ORDER_SWAP_32BIT(pcHeader + 4); + /* (Or should it be +32?) */ + *(unsigned short *)(pcHeader + 22) + = (unsigned short)lChannelCount; + WORD_ORDER_SWAP_16BIT(pcHeader + 22); + *(unsigned int *)(pcHeader + 24) + = (unsigned int)lSampleRate; + WORD_ORDER_SWAP_32BIT(pcHeader + 24); + *(unsigned int *)(pcHeader + 28) + = (unsigned int)(lSampleRate * lChannelCount * sizeof(short)); + WORD_ORDER_SWAP_32BIT(pcHeader + 28); + *(unsigned short *)(pcHeader + 32) + = (unsigned short)(lChannelCount * sizeof(short)); + WORD_ORDER_SWAP_16BIT(pcHeader + 32); + *(unsigned int *)(pcHeader + 40) + = (unsigned int)(lLength * lChannelCount * sizeof(short)); + WORD_ORDER_SWAP_32BIT(pcHeader + 40); + + g_lOutputFileChannelCount = lChannelCount; + + lWriteLength = fwrite(pcHeader, sizeof(char), 44, g_poOutputFile); + if (lWriteLength < 44) { + fprintf(stderr, + "Failed to write header to output file \"%s\": %s\n", + pcFilename, + strerror(errno)); + exit(1); + } + + g_psOutputFileBuffer + = (short *)calloc(lChannelCount * BUFFER_SIZE, sizeof(short)); +} + +static void +readIntoBuffers(LADSPA_Data ** ppfBuffers, + const unsigned long lFrameSize) { + + short * psReadPointer; + size_t lReadLength; + unsigned long lChannelIndex; + unsigned long lFrameIndex; + + lReadLength = fread(g_psInputFileBuffer, + sizeof(short) * g_lInputFileChannelCount, + lFrameSize, + g_poInputFile); + if (lReadLength < lFrameSize) { + fprintf(stderr, + "Failed to read audio from input file. Is the file damaged?\n"); + exit(1); + } + + for (lChannelIndex = 0; + lChannelIndex < g_lInputFileChannelCount; + lChannelIndex++) { + psReadPointer = g_psInputFileBuffer + lChannelIndex; + for (lFrameIndex = 0; + lFrameIndex < lFrameSize; + lFrameIndex++, psReadPointer += g_lInputFileChannelCount) { + WORD_ORDER_SWAP_16BIT(psReadPointer); + ppfBuffers[lChannelIndex][lFrameIndex] + = ((LADSPA_Data)*psReadPointer) * (1.0f / 32767.5f); + } + } +} + +static void +writeFromBuffers(LADSPA_Data ** ppfBuffers, + const unsigned long lFrameSize) { + + LADSPA_Data fValue, fAbsValue; + short * psWritePointer; + size_t lWriteLength; + unsigned long lChannelIndex; + unsigned long lFrameIndex; + + for (lChannelIndex = 0; + lChannelIndex < g_lOutputFileChannelCount; + lChannelIndex++) { + psWritePointer = g_psOutputFileBuffer + lChannelIndex; + for (lFrameIndex = 0; + lFrameIndex < lFrameSize; + lFrameIndex++, psWritePointer += g_lOutputFileChannelCount) { + fValue = ppfBuffers[lChannelIndex][lFrameIndex] * 32767.5f; + fAbsValue = fabs(fValue); + if (fAbsValue > g_fPeakWritten) + g_fPeakWritten = fAbsValue; + /* Use hard clipping as it sounds better than wraparound. */ + if (fValue > 32767) + *psWritePointer = 32767; + else if (fValue <= -32768) + *psWritePointer = -32768; + else + *psWritePointer = (short)fValue; + WORD_ORDER_SWAP_16BIT(psWritePointer); + } + } + + lWriteLength = fwrite(g_psOutputFileBuffer, + sizeof(short) * g_lOutputFileChannelCount, + lFrameSize, + g_poOutputFile); + if (lWriteLength < lFrameSize) { + fprintf(stderr, + "Failed to read audio to output file. Is the disk full?\n"); + exit(1); + } + +} + +static void +closeFiles(void) { + fclose(g_poInputFile); + fclose(g_poOutputFile); + printf("Peak output: %g\n", g_fPeakWritten); +} + +/*****************************************************************************/ + +static unsigned long +getPortCountByType(const LADSPA_Descriptor * psDescriptor, + const LADSPA_PortDescriptor iType) { + + unsigned long lCount; + unsigned long lIndex; + + lCount = 0; + for (lIndex = 0; lIndex < psDescriptor->PortCount; lIndex++) + if ((psDescriptor->PortDescriptors[lIndex] & iType) == iType) + lCount++; + + return lCount; +} + +/*****************************************************************************/ + +static void +listControlsForPlugin(const LADSPA_Descriptor * psDescriptor) { + + int bFound; + unsigned long lIndex; + LADSPA_PortRangeHintDescriptor iHintDescriptor; + LADSPA_Data fBound; + + fprintf(stderr, + "Plugin \"%s\" has the following control inputs:\n", + psDescriptor->Name); + + bFound = 0; + for (lIndex = 0; lIndex < psDescriptor->PortCount; lIndex++) + if (LADSPA_IS_PORT_INPUT(psDescriptor->PortDescriptors[lIndex]) + && LADSPA_IS_PORT_CONTROL(psDescriptor->PortDescriptors[lIndex])) { + fprintf(stderr, + "\t%s", + psDescriptor->PortNames[lIndex]); + bFound = 1; + iHintDescriptor = psDescriptor->PortRangeHints[lIndex].HintDescriptor; + if (LADSPA_IS_HINT_BOUNDED_BELOW(iHintDescriptor) + || LADSPA_IS_HINT_BOUNDED_ABOVE(iHintDescriptor)) { + fprintf(stderr, " ("); + if (LADSPA_IS_HINT_BOUNDED_BELOW(iHintDescriptor)) { + fBound = psDescriptor->PortRangeHints[lIndex].LowerBound; + if (LADSPA_IS_HINT_SAMPLE_RATE(iHintDescriptor)) { + if (fBound == 0) + fprintf(stderr, "0"); + else + fprintf(stderr, "%g * sample rate", fBound); + } + else + fprintf(stderr, "%g", fBound); + } + else + fprintf(stderr, "..."); + fprintf(stderr, " to "); + if (LADSPA_IS_HINT_BOUNDED_ABOVE(iHintDescriptor)) { + fBound = psDescriptor->PortRangeHints[lIndex].UpperBound; + if (LADSPA_IS_HINT_SAMPLE_RATE(iHintDescriptor)) { + if (fBound == 0) + fprintf(stderr, "0"); + else + fprintf(stderr, "%g * sample rate", fBound); + } + else + fprintf(stderr, "%g", fBound); + } + else + fprintf(stderr, "..."); + fprintf(stderr, ")\n"); + } + else + fprintf(stderr, "\n"); + } + + if (!bFound) + fprintf(stderr, "\tnone\n"); +} + +/*****************************************************************************/ + +/* Note that this procedure leaks memory like mad. */ +static void +applyPlugin(const char * pcInputFilename, + const char * pcOutputFilename, + const LADSPA_Data fExtraSeconds, + const unsigned long lPluginCount, + const LADSPA_Descriptor ** ppsPluginDescriptors, + LADSPA_Data ** ppfPluginControlValues) { + + LADSPA_PortDescriptor iPortDescriptor; + LADSPA_Handle * ppsPlugins; + LADSPA_Data ** ppfBuffers; + long lFrameSize; + unsigned long lAudioInputCount; + unsigned long lAudioOutputCount; + unsigned long lPreviousAudioOutputCount; + unsigned long lBufferCount; + unsigned long lBufferIndex; + unsigned long lControlIndex; + unsigned long lInputFileChannelCount; + unsigned long lInputFileLength; + unsigned long lOutputFileChannelCount; + unsigned long lOutputFileLength; + unsigned long lPluginIndex; + unsigned long lPortIndex; + unsigned long lSampleRate; + unsigned long lTimeAt; + LADSPA_Data fDummyControlOutput; + + /* Open input file and output file: + -------------------------------- */ + + lOutputFileChannelCount + = getPortCountByType(ppsPluginDescriptors[lPluginCount - 1], + LADSPA_PORT_AUDIO | LADSPA_PORT_OUTPUT); + if (lOutputFileChannelCount == 0) { + fprintf(stderr, + "The last plugin in the chain has no audio outputs.\n"); + exit(1); + } + + openWaveFile(pcInputFilename, + &lInputFileChannelCount, + &lSampleRate, + &lInputFileLength); + if (lInputFileChannelCount + != getPortCountByType(ppsPluginDescriptors[0], + LADSPA_PORT_AUDIO | LADSPA_PORT_INPUT)) { + fprintf(stderr, + "Mismatch between channel count in input file and audio inputs " + "on first plugin in chain.\n"); + exit(1); + } + + lOutputFileLength + = lInputFileLength + (unsigned long)(fExtraSeconds * lSampleRate); + + createWaveFile(pcOutputFilename, + lOutputFileChannelCount, + lSampleRate, + lOutputFileLength); + + /* Count buffers and sanity-check the flow graph: + ---------------------------------------------- */ + + lBufferCount = 0; + lPreviousAudioOutputCount = 0; + for (lPluginIndex = 0; lPluginIndex < lPluginCount; lPluginIndex++) { + + lAudioInputCount + = getPortCountByType(ppsPluginDescriptors[lPluginIndex], + LADSPA_PORT_AUDIO | LADSPA_PORT_INPUT); + lAudioOutputCount + = getPortCountByType(ppsPluginDescriptors[lPluginIndex], + LADSPA_PORT_AUDIO | LADSPA_PORT_OUTPUT); + + if (lBufferCount < lAudioInputCount) + lBufferCount = lAudioInputCount; + + if (lPluginIndex > 0) + if (lAudioInputCount != lPreviousAudioOutputCount) { + fprintf(stderr, + "There is a mismatch between the number of output channels " + "on plugin \"%s\" (%ld) and the number of input channels on " + "plugin \"%s\" (%ld).\n", + ppsPluginDescriptors[lPluginIndex - 1]->Name, + lPreviousAudioOutputCount, + ppsPluginDescriptors[lPluginIndex]->Name, + lAudioInputCount); + exit(1); + } + + lPreviousAudioOutputCount = lAudioOutputCount; + + if (lBufferCount < lAudioOutputCount) + lBufferCount = lAudioOutputCount; + } + + /* Create the buffers, create instances, wire them up: + --------------------------------------------------- */ + + ppsPlugins = (LADSPA_Handle *)calloc(lPluginCount, sizeof(LADSPA_Handle)); + ppfBuffers = (LADSPA_Data **)calloc(lBufferCount, sizeof(LADSPA_Data *)); + for (lBufferIndex = 0; lBufferIndex < lBufferCount; lBufferIndex++) + ppfBuffers[lBufferIndex] + = (LADSPA_Data *)calloc(BUFFER_SIZE, sizeof(LADSPA_Data)); + + for (lPluginIndex = 0; lPluginIndex < lPluginCount; lPluginIndex++) { + + ppsPlugins[lPluginIndex] + = ppsPluginDescriptors[lPluginIndex] + ->instantiate(ppsPluginDescriptors[lPluginIndex], + lSampleRate); + if (!ppsPlugins[lPluginIndex]) { + fprintf(stderr, + "Failed to instantiate plugin of type \"%s\".\n", + ppsPluginDescriptors[lPluginIndex]->Name); + exit(1); + } + + /* Controls: + --------- */ + + lControlIndex = 0; + for (lPortIndex = 0; + lPortIndex < ppsPluginDescriptors[lPluginIndex]->PortCount; + lPortIndex++) { + + iPortDescriptor + = ppsPluginDescriptors[lPluginIndex]->PortDescriptors[lPortIndex]; + + if (LADSPA_IS_PORT_CONTROL(iPortDescriptor)) { + if (LADSPA_IS_PORT_INPUT(iPortDescriptor)) + ppsPluginDescriptors[lPluginIndex]->connect_port + (ppsPlugins[lPluginIndex], + lPortIndex, + ppfPluginControlValues[lPluginIndex] + (lControlIndex++)); + if (LADSPA_IS_PORT_OUTPUT(iPortDescriptor)) + ppsPluginDescriptors[lPluginIndex]->connect_port + (ppsPlugins[lPluginIndex], + lPortIndex, + &fDummyControlOutput); + } + } + + /* Input Buffers: + -------------- */ + + lBufferIndex = 0; + for (lPortIndex = 0; + lPortIndex < ppsPluginDescriptors[lPluginIndex]->PortCount; + lPortIndex++) { + iPortDescriptor + = ppsPluginDescriptors[lPluginIndex]->PortDescriptors[lPortIndex]; + if (LADSPA_IS_PORT_INPUT(iPortDescriptor) + && LADSPA_IS_PORT_AUDIO(iPortDescriptor)) + ppsPluginDescriptors[lPluginIndex]->connect_port + (ppsPlugins[lPluginIndex], + lPortIndex, + ppfBuffers[lBufferIndex++]); + } + + + /* Output Buffers: + --------------- */ + + lBufferIndex = 0; + for (lPortIndex = 0; + lPortIndex < ppsPluginDescriptors[lPluginIndex]->PortCount; + lPortIndex++) { + iPortDescriptor + = ppsPluginDescriptors[lPluginIndex]->PortDescriptors[lPortIndex]; + if (LADSPA_IS_PORT_OUTPUT(iPortDescriptor) + && LADSPA_IS_PORT_AUDIO(iPortDescriptor)) + ppsPluginDescriptors[lPluginIndex]->connect_port + (ppsPlugins[lPluginIndex], + lPortIndex, + ppfBuffers[lBufferIndex++]); + } + } + + /* Activate: + --------- */ + + for (lPluginIndex = 0; lPluginIndex < lPluginCount; lPluginIndex++) + if (ppsPluginDescriptors[lPluginIndex]->activate != NULL) + ppsPluginDescriptors[lPluginIndex]->activate(ppsPlugins[lPluginIndex]); + + /* Run: + ---- */ + + lTimeAt = 0; + while (lTimeAt < lOutputFileLength) { + + lFrameSize = lInputFileLength - lTimeAt; + if (lFrameSize > BUFFER_SIZE) + lFrameSize = BUFFER_SIZE; + else { + /* We've reached or are reaching the end of the file. We're not + going to fill the buffer from file. Could just memset the end + part, but there's only one frame where this is worth the + effort. */ + for (lBufferIndex = 0; lBufferIndex < lBufferCount; lBufferIndex++) + memset(ppfBuffers[lBufferIndex], 0, sizeof(LADSPA_Data) * BUFFER_SIZE); + } + + if (lFrameSize > 0) { + /* Read from disk. */ + readIntoBuffers(ppfBuffers, lFrameSize); + } + + /* Run the plugins: */ + lFrameSize = lOutputFileLength - lTimeAt; + if (lFrameSize > BUFFER_SIZE) + lFrameSize = BUFFER_SIZE; + + for (lPluginIndex = 0; lPluginIndex < lPluginCount; lPluginIndex++) + ppsPluginDescriptors[lPluginIndex] + ->run(ppsPlugins[lPluginIndex], + lFrameSize); + + /* Write the output to disk. */ + writeFromBuffers(ppfBuffers, lFrameSize); + + lTimeAt += lFrameSize; + } + + /* Deactivate: + ----------- */ + + for (lPluginIndex = 0; lPluginIndex < lPluginCount; lPluginIndex++) + if (ppsPluginDescriptors[lPluginIndex]->deactivate != NULL) + ppsPluginDescriptors[lPluginIndex]->deactivate(ppsPlugins[lPluginIndex]); + + /* Cleanup: + -------- */ + + for (lPluginIndex = 0; lPluginIndex < lPluginCount; lPluginIndex++) + ppsPluginDescriptors[lPluginIndex]->cleanup(ppsPlugins[lPluginIndex]); + + /* Close the input and output files: + --------------------------------- */ + + closeFiles(); + +} + +/*****************************************************************************/ + +/* Note that this function leaks memory and that dynamic libraries + that are dlopen()ed are never dlclose()d. */ +int +main(const int iArgc, char * const ppcArgv[]) { + + char * pcEndPointer; + const char * pcControlValue; + const char * pcInputFilename; + const char * pcOutputFilename; + const LADSPA_Descriptor ** ppsPluginDescriptors; + LADSPA_Data ** ppfPluginControlValues; + LADSPA_Data fExtraSeconds; + int bBadParameters; + int bBadControls; + LADSPA_Properties iProperties; + unsigned long lArgumentIndex; + unsigned long lControlValueCount; + unsigned long lControlValueIndex; + unsigned long lPluginCount; + unsigned long lPluginCountUpperLimit; + unsigned long lPluginIndex; + void ** ppvPluginLibraries; + + bBadParameters = 0; + fExtraSeconds = 0; + + /* Check for a -s flag, but only at the start. Cannot get use + getopt() as it gets thoroughly confused when faced with negative + numbers on the command line. */ + lArgumentIndex = 1; + if (iArgc >= 3) { + if (strcmp(ppcArgv[1], "-s") == 0) { + fExtraSeconds = (LADSPA_Data)strtod(ppcArgv[2], &pcEndPointer); + bBadControls = (ppcArgv[2] + strlen(ppcArgv[2]) + != pcEndPointer); + lArgumentIndex = 3; + } + else if (strncmp(ppcArgv[1], "-s", 2) == 0) { + fExtraSeconds = (LADSPA_Data)strtod(ppcArgv[1] + 2, &pcEndPointer); + bBadControls = (ppcArgv[1] + strlen(ppcArgv[1]) + != pcEndPointer); + lArgumentIndex = 2; + } + } + + /* We need to analyse the rest of the parameters. The first two + should be input and output files involved. */ + if (lArgumentIndex + 4 > (unsigned long)iArgc) { + /* There aren't enough parameters to include an input file, an + output file and one plugin. */ + bBadParameters = 1; + } + else { + + pcInputFilename = ppcArgv[lArgumentIndex]; + pcOutputFilename = ppcArgv[lArgumentIndex + 1]; + + /* Now we need to look through any plugins and plugin parameters + present. At this stage we're loading plugins and parameters, + but not attempting to wire them up. + + First we construct some arrays to contain the data we're + hopefully about to extract. Note that these arrays are usually + larger than is needed, however they will be large enough. + + WARNING: Note that there is no attempt to tidy up the memory at + the end of this function and libraries are not unloaded under + error conditions. This is only a toy program. */ + + lPluginCountUpperLimit = (iArgc - lArgumentIndex - 1) / 2; + + ppvPluginLibraries = ((void **) + calloc(lPluginCountUpperLimit, + sizeof(void *))); + ppsPluginDescriptors = ((const LADSPA_Descriptor **) + calloc(lPluginCountUpperLimit, + sizeof(LADSPA_Descriptor *))); + ppfPluginControlValues = ((LADSPA_Data **) + calloc(lPluginCountUpperLimit, + sizeof(LADSPA_Data *))); + lPluginIndex = 0; + lArgumentIndex += 2; + bBadControls = 0; + while (lArgumentIndex < (unsigned long)iArgc && !bBadControls) { + + if (lArgumentIndex + 1 == (unsigned long)iArgc) { + bBadParameters = 1; + break; + } + + /* Parameter should be a plugin file name followed by a + label. Load the plugin. This call will exit() if the load + fails. */ + ppvPluginLibraries[lPluginIndex] + = loadLADSPAPluginLibrary(ppcArgv[lArgumentIndex]); + ppsPluginDescriptors[lPluginIndex] + = findLADSPAPluginDescriptor(ppvPluginLibraries[lPluginIndex], + ppcArgv[lArgumentIndex], + ppcArgv[lArgumentIndex + 1]); + + /* Check the plugin is in-place compatible. */ + iProperties = ppsPluginDescriptors[lPluginIndex]->Properties; + if (LADSPA_IS_INPLACE_BROKEN(iProperties)) { + fprintf(stderr, + "Plugin \"%s\" is not capable of in-place processing and " + "therefore cannot be used by this program.\n", + ppsPluginDescriptors[lPluginIndex]->Name); + /* This is somewhat lazy - this isn't a difficult problem to + get around. */ + return(1); + } + + lControlValueCount + = getPortCountByType(ppsPluginDescriptors[lPluginIndex], + LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL); + + bBadControls = (lControlValueCount + lArgumentIndex + 2 + > (unsigned long)iArgc); + if (lControlValueCount > 0 && !bBadControls) { + ppfPluginControlValues[lPluginIndex] + = (LADSPA_Data *)calloc(lControlValueCount, sizeof(LADSPA_Data)); + for (lControlValueIndex = 0; + lControlValueIndex < lControlValueCount && !bBadControls; + lControlValueIndex++) { + pcControlValue = ppcArgv[lArgumentIndex + 2 + lControlValueIndex]; + + ppfPluginControlValues[lPluginIndex][lControlValueIndex] + = (LADSPA_Data)strtod(pcControlValue, &pcEndPointer); + + bBadControls = (pcControlValue + strlen(pcControlValue) + != pcEndPointer); + } + } + + if (bBadControls) + listControlsForPlugin(ppsPluginDescriptors[lPluginIndex]); + + lArgumentIndex += (2 + lControlValueCount); + lPluginIndex++; + } + + lPluginCount = lPluginIndex; + + if (!bBadControls) { + + /* We have all the data we need. Go go go. If this function + fails it will exit(). */ + applyPlugin(pcInputFilename, + pcOutputFilename, + fExtraSeconds, + lPluginCount, + ppsPluginDescriptors, + ppfPluginControlValues); + + for (lPluginIndex = 0; lPluginIndex < lPluginCount; lPluginIndex++) + unloadLADSPAPluginLibrary(ppvPluginLibraries[lPluginIndex]); + } + } + + if (bBadParameters) { + fprintf(stderr, + "Usage:\tapplyplugin [flags] " + "\n" + "\t " + " ...\n" + "\t[ " + " ...]...\n" + "Flags:" + "\t-s Add seconds of silence after end of input file.\n" + "\n" + "To find out what control values are needed by a plugin, " + "use the\n" + "\"analyseplugin\" program and check for control input ports.\n" + "Note that the LADSPA_PATH environment variable is used " + "to help find plugins.\n"); + return(1); + } + + return(0); +} + +/*****************************************************************************/ + +/* EOF */ diff --git a/src/default.c b/src/default.c new file mode 100644 index 0000000..4a2a30b --- /dev/null +++ b/src/default.c @@ -0,0 +1,96 @@ +/* default.c + + Free software by Richard W.E. Furse. Do with as you will. No + warranty. */ + +/*****************************************************************************/ + +#include + +/*****************************************************************************/ + +#include "ladspa.h" +#include "utils.h" + +/*****************************************************************************/ + +int +getLADSPADefault(const LADSPA_PortRangeHint * psPortRangeHint, + const unsigned long lSampleRate, + LADSPA_Data * pfResult) { + + int iHintDescriptor; + + iHintDescriptor = psPortRangeHint->HintDescriptor & LADSPA_HINT_DEFAULT_MASK; + + switch (iHintDescriptor & LADSPA_HINT_DEFAULT_MASK) { + case LADSPA_HINT_DEFAULT_NONE: + return -1; + case LADSPA_HINT_DEFAULT_MINIMUM: + *pfResult = psPortRangeHint->LowerBound; + if (LADSPA_IS_HINT_SAMPLE_RATE(psPortRangeHint->HintDescriptor)) + *pfResult *= lSampleRate; + return 0; + case LADSPA_HINT_DEFAULT_LOW: + if (LADSPA_IS_HINT_LOGARITHMIC(iHintDescriptor)) { + *pfResult = exp(log(psPortRangeHint->LowerBound) * 0.75 + + log(psPortRangeHint->UpperBound) * 0.25); + } + else { + *pfResult = (psPortRangeHint->LowerBound * 0.75 + + psPortRangeHint->UpperBound * 0.25); + } + if (LADSPA_IS_HINT_SAMPLE_RATE(psPortRangeHint->HintDescriptor)) + *pfResult *= lSampleRate; + return 0; + case LADSPA_HINT_DEFAULT_MIDDLE: + if (LADSPA_IS_HINT_LOGARITHMIC(iHintDescriptor)) { + *pfResult = sqrt(psPortRangeHint->LowerBound + * psPortRangeHint->UpperBound); + } + else { + *pfResult = 0.5 * (psPortRangeHint->LowerBound + + psPortRangeHint->UpperBound); + } + if (LADSPA_IS_HINT_SAMPLE_RATE(psPortRangeHint->HintDescriptor)) + *pfResult *= lSampleRate; + return 0; + case LADSPA_HINT_DEFAULT_HIGH: + if (LADSPA_IS_HINT_LOGARITHMIC(iHintDescriptor)) { + *pfResult = exp(log(psPortRangeHint->LowerBound) * 0.25 + + log(psPortRangeHint->UpperBound) * 0.75); + } + else { + *pfResult = (psPortRangeHint->LowerBound * 0.25 + + psPortRangeHint->UpperBound * 0.75); + } + if (LADSPA_IS_HINT_SAMPLE_RATE(psPortRangeHint->HintDescriptor)) + *pfResult *= lSampleRate; + return 0; + case LADSPA_HINT_DEFAULT_MAXIMUM: + *pfResult = psPortRangeHint->UpperBound; + if (LADSPA_IS_HINT_SAMPLE_RATE(psPortRangeHint->HintDescriptor)) + *pfResult *= lSampleRate; + return 0; + case LADSPA_HINT_DEFAULT_0: + *pfResult = 0; + return 0; + case LADSPA_HINT_DEFAULT_1: + *pfResult = 1; + return 0; + case LADSPA_HINT_DEFAULT_100: + *pfResult = 100; + return 0; + case LADSPA_HINT_DEFAULT_440: + *pfResult = 440; + return 0; + } + + /* We don't recognise this default flag. It's probably from a more + recent version of LADSPA. */ + return -1; +} + +/*****************************************************************************/ + +/* EOF */ diff --git a/src/ladspa.h b/src/ladspa.h new file mode 100644 index 0000000..5c30a8a --- /dev/null +++ b/src/ladspa.h @@ -0,0 +1,603 @@ +/* ladspa.h + + Linux Audio Developer's Simple Plugin API Version 1.1[LGPL]. + Copyright (C) 2000-2002 Richard W.E. Furse, Paul Barton-Davis, + Stefan Westerfeld. + + 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.1 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. */ + +#ifndef LADSPA_INCLUDED +#define LADSPA_INCLUDED + +#define LADSPA_VERSION "1.1" +#define LADSPA_VERSION_MAJOR 1 +#define LADSPA_VERSION_MINOR 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/*****************************************************************************/ + +/* Overview: + + There is a large number of synthesis packages in use or development + on the Linux platform at this time. This API (`The Linux Audio + Developer's Simple Plugin API') attempts to give programmers the + ability to write simple `plugin' audio processors in C/C++ and link + them dynamically (`plug') into a range of these packages (`hosts'). + It should be possible for any host and any plugin to communicate + completely through this interface. + + This API is deliberately short and simple. To achieve compatibility + with a range of promising Linux sound synthesis packages it + attempts to find the `greatest common divisor' in their logical + behaviour. Having said this, certain limiting decisions are + implicit, notably the use of a fixed type (LADSPA_Data) for all + data transfer and absence of a parameterised `initialisation' + phase. See below for the LADSPA_Data typedef. + + Plugins are expected to distinguish between control and audio + data. Plugins have `ports' that are inputs or outputs for audio or + control data and each plugin is `run' for a `block' corresponding + to a short time interval measured in samples. Audio data is + communicated using arrays of LADSPA_Data, allowing a block of audio + to be processed by the plugin in a single pass. Control data is + communicated using single LADSPA_Data values. Control data has a + single value at the start of a call to the `run()' or `run_adding()' + function, and may be considered to remain this value for its + duration. The plugin may assume that all its input and output ports + have been connected to the relevant data location (see the + `connect_port()' function below) before it is asked to run. + + Plugins will reside in shared object files suitable for dynamic + linking by dlopen() and family. The file will provide a number of + `plugin types' that can be used to instantiate actual plugins + (sometimes known as `plugin instances') that can be connected + together to perform tasks. + + This API contains very limited error-handling. */ + +/*****************************************************************************/ + +/* Fundamental data type passed in and out of plugin. This data type + is used to communicate audio samples and control values. It is + assumed that the plugin will work sensibly given any numeric input + value although it may have a preferred range (see hints below). + + For audio it is generally assumed that 1.0f is the `0dB' reference + amplitude and is a `normal' signal level. */ + +typedef float LADSPA_Data; + +/*****************************************************************************/ + +/* Special Plugin Properties: + + Optional features of the plugin type are encapsulated in the + LADSPA_Properties type. This is assembled by ORing individual + properties together. */ + +typedef int LADSPA_Properties; + +/* Property LADSPA_PROPERTY_REALTIME indicates that the plugin has a + real-time dependency (e.g. listens to a MIDI device) and so its + output must not be cached or subject to significant latency. */ +#define LADSPA_PROPERTY_REALTIME 0x1 + +/* Property LADSPA_PROPERTY_INPLACE_BROKEN indicates that the plugin + may cease to work correctly if the host elects to use the same data + location for both input and output (see connect_port()). This + should be avoided as enabling this flag makes it impossible for + hosts to use the plugin to process audio `in-place.' */ +#define LADSPA_PROPERTY_INPLACE_BROKEN 0x2 + +/* Property LADSPA_PROPERTY_HARD_RT_CAPABLE indicates that the plugin + is capable of running not only in a conventional host but also in a + `hard real-time' environment. To qualify for this the plugin must + satisfy all of the following: + + (1) The plugin must not use malloc(), free() or other heap memory + management within its run() or run_adding() functions. All new + memory used in run() must be managed via the stack. These + restrictions only apply to the run() function. + + (2) The plugin will not attempt to make use of any library + functions with the exceptions of functions in the ANSI standard C + and C maths libraries, which the host is expected to provide. + + (3) The plugin will not access files, devices, pipes, sockets, IPC + or any other mechanism that might result in process or thread + blocking. + + (4) The plugin will take an amount of time to execute a run() or + run_adding() call approximately of form (A+B*SampleCount) where A + and B depend on the machine and host in use. This amount of time + may not depend on input signals or plugin state. The host is left + the responsibility to perform timings to estimate upper bounds for + A and B. */ +#define LADSPA_PROPERTY_HARD_RT_CAPABLE 0x4 + +#define LADSPA_IS_REALTIME(x) ((x) & LADSPA_PROPERTY_REALTIME) +#define LADSPA_IS_INPLACE_BROKEN(x) ((x) & LADSPA_PROPERTY_INPLACE_BROKEN) +#define LADSPA_IS_HARD_RT_CAPABLE(x) ((x) & LADSPA_PROPERTY_HARD_RT_CAPABLE) + +/*****************************************************************************/ + +/* Plugin Ports: + + Plugins have `ports' that are inputs or outputs for audio or + data. Ports can communicate arrays of LADSPA_Data (for audio + inputs/outputs) or single LADSPA_Data values (for control + input/outputs). This information is encapsulated in the + LADSPA_PortDescriptor type which is assembled by ORing individual + properties together. + + Note that a port must be an input or an output port but not both + and that a port must be a control or audio port but not both. */ + +typedef int LADSPA_PortDescriptor; + +/* Property LADSPA_PORT_INPUT indicates that the port is an input. */ +#define LADSPA_PORT_INPUT 0x1 + +/* Property LADSPA_PORT_OUTPUT indicates that the port is an output. */ +#define LADSPA_PORT_OUTPUT 0x2 + +/* Property LADSPA_PORT_CONTROL indicates that the port is a control + port. */ +#define LADSPA_PORT_CONTROL 0x4 + +/* Property LADSPA_PORT_AUDIO indicates that the port is a audio + port. */ +#define LADSPA_PORT_AUDIO 0x8 + +#define LADSPA_IS_PORT_INPUT(x) ((x) & LADSPA_PORT_INPUT) +#define LADSPA_IS_PORT_OUTPUT(x) ((x) & LADSPA_PORT_OUTPUT) +#define LADSPA_IS_PORT_CONTROL(x) ((x) & LADSPA_PORT_CONTROL) +#define LADSPA_IS_PORT_AUDIO(x) ((x) & LADSPA_PORT_AUDIO) + +/*****************************************************************************/ + +/* Plugin Port Range Hints: + + The host may wish to provide a representation of data entering or + leaving a plugin (e.g. to generate a GUI automatically). To make + this more meaningful, the plugin should provide `hints' to the host + describing the usual values taken by the data. + + Note that these are only hints. The host may ignore them and the + plugin must not assume that data supplied to it is meaningful. If + the plugin receives invalid input data it is expected to continue + to run without failure and, where possible, produce a sensible + output (e.g. a high-pass filter given a negative cutoff frequency + might switch to an all-pass mode). + + Hints are meaningful for all input and output ports but hints for + input control ports are expected to be particularly useful. + + More hint information is encapsulated in the + LADSPA_PortRangeHintDescriptor type which is assembled by ORing + individual hint types together. Hints may require further + LowerBound and UpperBound information. + + All the hint information for a particular port is aggregated in the + LADSPA_PortRangeHint structure. */ + +typedef int LADSPA_PortRangeHintDescriptor; + +/* Hint LADSPA_HINT_BOUNDED_BELOW indicates that the LowerBound field + of the LADSPA_PortRangeHint should be considered meaningful. The + value in this field should be considered the (inclusive) lower + bound of the valid range. If LADSPA_HINT_SAMPLE_RATE is also + specified then the value of LowerBound should be multiplied by the + sample rate. */ +#define LADSPA_HINT_BOUNDED_BELOW 0x1 + +/* Hint LADSPA_HINT_BOUNDED_ABOVE indicates that the UpperBound field + of the LADSPA_PortRangeHint should be considered meaningful. The + value in this field should be considered the (inclusive) upper + bound of the valid range. If LADSPA_HINT_SAMPLE_RATE is also + specified then the value of UpperBound should be multiplied by the + sample rate. */ +#define LADSPA_HINT_BOUNDED_ABOVE 0x2 + +/* Hint LADSPA_HINT_TOGGLED indicates that the data item should be + considered a Boolean toggle. Data less than or equal to zero should + be considered `off' or `false,' and data above zero should be + considered `on' or `true.' LADSPA_HINT_TOGGLED may not be used in + conjunction with any other hint except LADSPA_HINT_DEFAULT_0 or + LADSPA_HINT_DEFAULT_1. */ +#define LADSPA_HINT_TOGGLED 0x4 + +/* Hint LADSPA_HINT_SAMPLE_RATE indicates that any bounds specified + should be interpreted as multiples of the sample rate. For + instance, a frequency range from 0Hz to the Nyquist frequency (half + the sample rate) could be requested by this hint in conjunction + with LowerBound = 0 and UpperBound = 0.5. Hosts that support bounds + at all must support this hint to retain meaning. */ +#define LADSPA_HINT_SAMPLE_RATE 0x8 + +/* Hint LADSPA_HINT_LOGARITHMIC indicates that it is likely that the + user will find it more intuitive to view values using a logarithmic + scale. This is particularly useful for frequencies and gains. */ +#define LADSPA_HINT_LOGARITHMIC 0x10 + +/* Hint LADSPA_HINT_INTEGER indicates that a user interface would + probably wish to provide a stepped control taking only integer + values. Any bounds set should be slightly wider than the actual + integer range required to avoid floating point rounding errors. For + instance, the integer set {0,1,2,3} might be described as [-0.1, + 3.1]. */ +#define LADSPA_HINT_INTEGER 0x20 + +/* The various LADSPA_HINT_HAS_DEFAULT_* hints indicate a `normal' + value for the port that is sensible as a default. For instance, + this value is suitable for use as an initial value in a user + interface or as a value the host might assign to a control port + when the user has not provided one. Defaults are encoded using a + mask so only one default may be specified for a port. Some of the + hints make use of lower and upper bounds, in which case the + relevant bound or bounds must be available and + LADSPA_HINT_SAMPLE_RATE must be applied as usual. The resulting + default must be rounded if LADSPA_HINT_INTEGER is present. Default + values were introduced in LADSPA v1.1. */ +#define LADSPA_HINT_DEFAULT_MASK 0x3C0 + +/* This default values indicates that no default is provided. */ +#define LADSPA_HINT_DEFAULT_NONE 0x0 + +/* This default hint indicates that the suggested lower bound for the + port should be used. */ +#define LADSPA_HINT_DEFAULT_MINIMUM 0x40 + +/* This default hint indicates that a low value between the suggested + lower and upper bounds should be chosen. For ports with + LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.75 + + log(upper) * 0.25). Otherwise, this should be (lower * 0.75 + upper + * 0.25). */ +#define LADSPA_HINT_DEFAULT_LOW 0x80 + +/* This default hint indicates that a middle value between the + suggested lower and upper bounds should be chosen. For ports with + LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.5 + + log(upper) * 0.5). Otherwise, this should be (lower * 0.5 + upper * + 0.5). */ +#define LADSPA_HINT_DEFAULT_MIDDLE 0xC0 + +/* This default hint indicates that a high value between the suggested + lower and upper bounds should be chosen. For ports with + LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.25 + + log(upper) * 0.75). Otherwise, this should be (lower * 0.25 + upper + * 0.75). */ +#define LADSPA_HINT_DEFAULT_HIGH 0x100 + +/* This default hint indicates that the suggested upper bound for the + port should be used. */ +#define LADSPA_HINT_DEFAULT_MAXIMUM 0x140 + +/* This default hint indicates that the number 0 should be used. Note + that this default may be used in conjunction with + LADSPA_HINT_TOGGLED. */ +#define LADSPA_HINT_DEFAULT_0 0x200 + +/* This default hint indicates that the number 1 should be used. Note + that this default may be used in conjunction with + LADSPA_HINT_TOGGLED. */ +#define LADSPA_HINT_DEFAULT_1 0x240 + +/* This default hint indicates that the number 100 should be used. */ +#define LADSPA_HINT_DEFAULT_100 0x280 + +/* This default hint indicates that the Hz frequency of `concert A' + should be used. This will be 440 unless the host uses an unusual + tuning convention, in which case it may be within a few Hz. */ +#define LADSPA_HINT_DEFAULT_440 0x2C0 + +#define LADSPA_IS_HINT_BOUNDED_BELOW(x) ((x) & LADSPA_HINT_BOUNDED_BELOW) +#define LADSPA_IS_HINT_BOUNDED_ABOVE(x) ((x) & LADSPA_HINT_BOUNDED_ABOVE) +#define LADSPA_IS_HINT_TOGGLED(x) ((x) & LADSPA_HINT_TOGGLED) +#define LADSPA_IS_HINT_SAMPLE_RATE(x) ((x) & LADSPA_HINT_SAMPLE_RATE) +#define LADSPA_IS_HINT_LOGARITHMIC(x) ((x) & LADSPA_HINT_LOGARITHMIC) +#define LADSPA_IS_HINT_INTEGER(x) ((x) & LADSPA_HINT_INTEGER) + +#define LADSPA_IS_HINT_HAS_DEFAULT(x) ((x) & LADSPA_HINT_DEFAULT_MASK) +#define LADSPA_IS_HINT_DEFAULT_MINIMUM(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ + == LADSPA_HINT_DEFAULT_MINIMUM) +#define LADSPA_IS_HINT_DEFAULT_LOW(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ + == LADSPA_HINT_DEFAULT_LOW) +#define LADSPA_IS_HINT_DEFAULT_MIDDLE(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ + == LADSPA_HINT_DEFAULT_MIDDLE) +#define LADSPA_IS_HINT_DEFAULT_HIGH(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ + == LADSPA_HINT_DEFAULT_HIGH) +#define LADSPA_IS_HINT_DEFAULT_MAXIMUM(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ + == LADSPA_HINT_DEFAULT_MAXIMUM) +#define LADSPA_IS_HINT_DEFAULT_0(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ + == LADSPA_HINT_DEFAULT_0) +#define LADSPA_IS_HINT_DEFAULT_1(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ + == LADSPA_HINT_DEFAULT_1) +#define LADSPA_IS_HINT_DEFAULT_100(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ + == LADSPA_HINT_DEFAULT_100) +#define LADSPA_IS_HINT_DEFAULT_440(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ + == LADSPA_HINT_DEFAULT_440) + +typedef struct _LADSPA_PortRangeHint { + + /* Hints about the port. */ + LADSPA_PortRangeHintDescriptor HintDescriptor; + + /* Meaningful when hint LADSPA_HINT_BOUNDED_BELOW is active. When + LADSPA_HINT_SAMPLE_RATE is also active then this value should be + multiplied by the relevant sample rate. */ + LADSPA_Data LowerBound; + + /* Meaningful when hint LADSPA_HINT_BOUNDED_ABOVE is active. When + LADSPA_HINT_SAMPLE_RATE is also active then this value should be + multiplied by the relevant sample rate. */ + LADSPA_Data UpperBound; + +} LADSPA_PortRangeHint; + +/*****************************************************************************/ + +/* Plugin Handles: + + This plugin handle indicates a particular instance of the plugin + concerned. It is valid to compare this to NULL (0 for C++) but + otherwise the host should not attempt to interpret it. The plugin + may use it to reference internal instance data. */ + +typedef void * LADSPA_Handle; + +/*****************************************************************************/ + +/* Descriptor for a Type of Plugin: + + This structure is used to describe a plugin type. It provides a + number of functions to examine the type, instantiate it, link it to + buffers and workspaces and to run it. */ + +typedef struct _LADSPA_Descriptor { + + /* This numeric identifier indicates the plugin type + uniquely. Plugin programmers may reserve ranges of IDs from a + central body to avoid clashes. Hosts may assume that IDs are + below 0x1000000. */ + unsigned long UniqueID; + + /* This identifier can be used as a unique, case-sensitive + identifier for the plugin type within the plugin file. Plugin + types should be identified by file and label rather than by index + or plugin name, which may be changed in new plugin + versions. Labels must not contain white-space characters. */ + const char * Label; + + /* This indicates a number of properties of the plugin. */ + LADSPA_Properties Properties; + + /* This member points to the null-terminated name of the plugin + (e.g. "Sine Oscillator"). */ + const char * Name; + + /* This member points to the null-terminated string indicating the + maker of the plugin. This can be an empty string but not NULL. */ + const char * Maker; + + /* This member points to the null-terminated string indicating any + copyright applying to the plugin. If no Copyright applies the + string "None" should be used. */ + const char * Copyright; + + /* This indicates the number of ports (input AND output) present on + the plugin. */ + unsigned long PortCount; + + /* This member indicates an array of port descriptors. Valid indices + vary from 0 to PortCount-1. */ + const LADSPA_PortDescriptor * PortDescriptors; + + /* This member indicates an array of null-terminated strings + describing ports (e.g. "Frequency (Hz)"). Valid indices vary from + 0 to PortCount-1. */ + const char * const * PortNames; + + /* This member indicates an array of range hints for each port (see + above). Valid indices vary from 0 to PortCount-1. */ + const LADSPA_PortRangeHint * PortRangeHints; + + /* This may be used by the plugin developer to pass any custom + implementation data into an instantiate call. It must not be used + or interpreted by the host. It is expected that most plugin + writers will not use this facility as LADSPA_Handle should be + used to hold instance data. */ + void * ImplementationData; + + /* This member is a function pointer that instantiates a plugin. A + handle is returned indicating the new plugin instance. The + instantiation function accepts a sample rate as a parameter. The + plugin descriptor from which this instantiate function was found + must also be passed. This function must return NULL if + instantiation fails. + + Note that instance initialisation should generally occur in + activate() rather than here. */ + LADSPA_Handle (*instantiate)(const struct _LADSPA_Descriptor * Descriptor, + unsigned long SampleRate); + + /* This member is a function pointer that connects a port on an + instantiated plugin to a memory location at which a block of data + for the port will be read/written. The data location is expected + to be an array of LADSPA_Data for audio ports or a single + LADSPA_Data value for control ports. Memory issues will be + managed by the host. The plugin must read/write the data at these + locations every time run() or run_adding() is called and the data + present at the time of this connection call should not be + considered meaningful. + + connect_port() may be called more than once for a plugin instance + to allow the host to change the buffers that the plugin is + reading or writing. These calls may be made before or after + activate() or deactivate() calls. + + connect_port() must be called at least once for each port before + run() or run_adding() is called. When working with blocks of + LADSPA_Data the plugin should pay careful attention to the block + size passed to the run function as the block allocated may only + just be large enough to contain the block of samples. + + Plugin writers should be aware that the host may elect to use the + same buffer for more than one port and even use the same buffer + for both input and output (see LADSPA_PROPERTY_INPLACE_BROKEN). + However, overlapped buffers or use of a single buffer for both + audio and control data may result in unexpected behaviour. */ + void (*connect_port)(LADSPA_Handle Instance, + unsigned long Port, + LADSPA_Data * DataLocation); + + /* This member is a function pointer that initialises a plugin + instance and activates it for use. This is separated from + instantiate() to aid real-time support and so that hosts can + reinitialise a plugin instance by calling deactivate() and then + activate(). In this case the plugin instance must reset all state + information dependent on the history of the plugin instance + except for any data locations provided by connect_port() and any + gain set by set_run_adding_gain(). If there is nothing for + activate() to do then the plugin writer may provide a NULL rather + than an empty function. + + When present, hosts must call this function once before run() (or + run_adding()) is called for the first time. This call should be + made as close to the run() call as possible and indicates to + real-time plugins that they are now live. Plugins should not rely + on a prompt call to run() after activate(). activate() may not be + called again unless deactivate() is called first. Note that + connect_port() may be called before or after a call to + activate(). */ + void (*activate)(LADSPA_Handle Instance); + + /* This method is a function pointer that runs an instance of a + plugin for a block. Two parameters are required: the first is a + handle to the particular instance to be run and the second + indicates the block size (in samples) for which the plugin + instance may run. + + Note that if an activate() function exists then it must be called + before run() or run_adding(). If deactivate() is called for a + plugin instance then the plugin instance may not be reused until + activate() has been called again. + + If the plugin has the property LADSPA_PROPERTY_HARD_RT_CAPABLE + then there are various things that the plugin should not do + within the run() or run_adding() functions (see above). */ + void (*run)(LADSPA_Handle Instance, + unsigned long SampleCount); + + /* This method is a function pointer that runs an instance of a + plugin for a block. This has identical behaviour to run() except + in the way data is output from the plugin. When run() is used, + values are written directly to the memory areas associated with + the output ports. However when run_adding() is called, values + must be added to the values already present in the memory + areas. Furthermore, output values written must be scaled by the + current gain set by set_run_adding_gain() (see below) before + addition. + + run_adding() is optional. When it is not provided by a plugin, + this function pointer must be set to NULL. When it is provided, + the function set_run_adding_gain() must be provided also. */ + void (*run_adding)(LADSPA_Handle Instance, + unsigned long SampleCount); + + /* This method is a function pointer that sets the output gain for + use when run_adding() is called (see above). If this function is + never called the gain is assumed to default to 1. Gain + information should be retained when activate() or deactivate() + are called. + + This function should be provided by the plugin if and only if the + run_adding() function is provided. When it is absent this + function pointer must be set to NULL. */ + void (*set_run_adding_gain)(LADSPA_Handle Instance, + LADSPA_Data Gain); + + /* This is the counterpart to activate() (see above). If there is + nothing for deactivate() to do then the plugin writer may provide + a NULL rather than an empty function. + + Hosts must deactivate all activated units after they have been + run() (or run_adding()) for the last time. This call should be + made as close to the last run() call as possible and indicates to + real-time plugins that they are no longer live. Plugins should + not rely on prompt deactivation. Note that connect_port() may be + called before or after a call to deactivate(). + + Deactivation is not similar to pausing as the plugin instance + will be reinitialised when activate() is called to reuse it. */ + void (*deactivate)(LADSPA_Handle Instance); + + /* Once an instance of a plugin has been finished with it can be + deleted using the following function. The instance handle passed + ceases to be valid after this call. + + If activate() was called for a plugin instance then a + corresponding call to deactivate() must be made before cleanup() + is called. */ + void (*cleanup)(LADSPA_Handle Instance); + +} LADSPA_Descriptor; + +/**********************************************************************/ + +/* Accessing a Plugin: */ + +/* The exact mechanism by which plugins are loaded is host-dependent, + however all most hosts will need to know is the name of shared + object file containing the plugin types. To allow multiple hosts to + share plugin types, hosts may wish to check for environment + variable LADSPA_PATH. If present, this should contain a + colon-separated path indicating directories that should be searched + (in order) when loading plugin types. + + A plugin programmer must include a function called + "ladspa_descriptor" with the following function prototype within + the shared object file. This function will have C-style linkage (if + you are using C++ this is taken care of by the `extern "C"' clause + at the top of the file). + + A host will find the plugin shared object file by one means or + another, find the ladspa_descriptor() function, call it, and + proceed from there. + + Plugin types are accessed by index (not ID) using values from 0 + upwards. Out of range indexes must result in this function + returning NULL, so the plugin count can be determined by checking + for the least index that results in NULL being returned. */ + +const LADSPA_Descriptor * ladspa_descriptor(unsigned long Index); + +/* Datatype corresponding to the ladspa_descriptor() function. */ +typedef const LADSPA_Descriptor * +(*LADSPA_Descriptor_Function)(unsigned long Index); + +/**********************************************************************/ + +#ifdef __cplusplus +} +#endif + +#endif /* LADSPA_INCLUDED */ + +/* EOF */ diff --git a/src/listplugins.c b/src/listplugins.c new file mode 100644 index 0000000..ba8e4c4 --- /dev/null +++ b/src/listplugins.c @@ -0,0 +1,58 @@ +/* listplugins.c + + Free software by Richard W.E. Furse. Do with as you will. No + warranty. */ + +/*****************************************************************************/ + +#include +#include +#include +#include + +/*****************************************************************************/ + +#include "ladspa.h" +#include "utils.h" + +/*****************************************************************************/ + +static void +describePluginLibrary(const char * pcFullFilename, + void * pvPluginHandle, + LADSPA_Descriptor_Function fDescriptorFunction) { + + const LADSPA_Descriptor * psDescriptor; + long lIndex; + + printf("%s:\n", pcFullFilename); + for (lIndex = 0; + (psDescriptor = fDescriptorFunction(lIndex)) != NULL; + lIndex++) + printf("\t%s (%lu/%s)\n", + psDescriptor->Name, + psDescriptor->UniqueID, + psDescriptor->Label); + + dlclose(pvPluginHandle); +} + +/*****************************************************************************/ + +/* Returns 0 if all goes well, otherwise returns 1. */ +static int +listPlugins() { + LADSPAPluginSearch(describePluginLibrary); + return(0); +} + +/*****************************************************************************/ + +int +main(const int iArgc, const char ** ppcArgv) { + return listPlugins(); +} + +/*****************************************************************************/ + +/* EOF */ diff --git a/src/load.c b/src/load.c new file mode 100644 index 0000000..c2a5aa7 --- /dev/null +++ b/src/load.c @@ -0,0 +1,186 @@ +/* load.c + + Free software by Richard W.E. Furse. Do with as you will. No + warranty. */ + +/*****************************************************************************/ + +#include +#include +#include +#include + +/*****************************************************************************/ + +#include "ladspa.h" +#include "utils.h" + +/*****************************************************************************/ + +/* This function provides a wrapping of dlopen(). When the filename is + not an absolute path (i.e. does not begin with / character), this + routine will search the LADSPA_PATH for the file. */ +static void * +dlopenLADSPA(const char * pcFilename, int iFlag) { + + char * pcBuffer; + const char * pcEnd; + const char * pcLADSPAPath; + const char * pcStart; + int iEndsInSO; + int iNeedSlash; + size_t iFilenameLength; + void * pvResult; + + iFilenameLength = strlen(pcFilename); + pvResult = NULL; + + if (pcFilename[0] == '/') { + + /* The filename is absolute. Assume the user knows what he/she is + doing and simply dlopen() it. */ + + pvResult = dlopen(pcFilename, iFlag); + if (pvResult != NULL) + return pvResult; + + } + else { + + /* If the filename is not absolute then we wish to check along the + LADSPA_PATH path to see if we can find the file there. We do + NOT call dlopen() directly as this would find plugins on the + LD_LIBRARY_PATH, whereas the LADSPA_PATH is the correct place + to search. */ + + pcLADSPAPath = getenv("LADSPA_PATH"); + + if (pcLADSPAPath) { + + pcStart = pcLADSPAPath; + while (*pcStart != '\0') { + pcEnd = pcStart; + while (*pcEnd != ':' && *pcEnd != '\0') + pcEnd++; + + pcBuffer = malloc(iFilenameLength + 2 + (pcEnd - pcStart)); + if (pcEnd > pcStart) + strncpy(pcBuffer, pcStart, pcEnd - pcStart); + iNeedSlash = 0; + if (pcEnd > pcStart) + if (*(pcEnd - 1) != '/') { + iNeedSlash = 1; + pcBuffer[pcEnd - pcStart] = '/'; + } + strcpy(pcBuffer + iNeedSlash + (pcEnd - pcStart), pcFilename); + + pvResult = dlopen(pcBuffer, iFlag); + + free(pcBuffer); + if (pvResult != NULL) + return pvResult; + + pcStart = pcEnd; + if (*pcStart == ':') + pcStart++; + } + } + } + + /* As a last ditch effort, check if filename does not end with + ".so". In this case, add this suffix and recurse. */ + iEndsInSO = 0; + if (iFilenameLength > 3) + iEndsInSO = (strcmp(pcFilename + iFilenameLength - 3, ".so") == 0); + if (!iEndsInSO) { + pcBuffer = malloc(iFilenameLength + 4); + strcpy(pcBuffer, pcFilename); + strcat(pcBuffer, ".so"); + pvResult = dlopenLADSPA(pcBuffer, iFlag); + free(pcBuffer); + } + + if (pvResult != NULL) + return pvResult; + + /* If nothing has worked, then at least we can make sure we set the + correct error message - and this should correspond to a call to + dlopen() with the actual filename requested. The dlopen() manual + page does not specify whether the first or last error message + will be kept when multiple calls are made to dlopen(). We've + covered the former case - now we can handle the latter by calling + dlopen() again here. */ + return dlopen(pcFilename, iFlag); +} + +/*****************************************************************************/ + +void * +loadLADSPAPluginLibrary(const char * pcPluginFilename) { + + void * pvPluginHandle; + + pvPluginHandle = dlopenLADSPA(pcPluginFilename, RTLD_NOW); + if (!pvPluginHandle) { + fprintf(stderr, + "Failed to load plugin \"%s\": %s\n", + pcPluginFilename, + dlerror()); + exit(1); + } + + return pvPluginHandle; +} + +/*****************************************************************************/ + +void +unloadLADSPAPluginLibrary(void * pvLADSPAPluginLibrary) { + dlclose(pvLADSPAPluginLibrary); +} + +/*****************************************************************************/ + +const LADSPA_Descriptor * +findLADSPAPluginDescriptor(void * pvLADSPAPluginLibrary, + const char * pcPluginLibraryFilename, + const char * pcPluginLabel) { + + const LADSPA_Descriptor * psDescriptor; + LADSPA_Descriptor_Function pfDescriptorFunction; + unsigned long lPluginIndex; + + dlerror(); + pfDescriptorFunction + = (LADSPA_Descriptor_Function)dlsym(pvLADSPAPluginLibrary, + "ladspa_descriptor"); + if (!pfDescriptorFunction) { + const char * pcError = dlerror(); + if (pcError) { + fprintf(stderr, + "Unable to find ladspa_descriptor() function in plugin " + "library file \"%s\": %s.\n" + "Are you sure this is a LADSPA plugin file?\n", + pcPluginLibraryFilename, + pcError); + exit(1); + } + } + + for (lPluginIndex = 0;; lPluginIndex++) { + psDescriptor = pfDescriptorFunction(lPluginIndex); + if (psDescriptor == NULL) { + fprintf(stderr, + "Unable to find label \"%s\" in plugin library file \"%s\".\n", + pcPluginLabel, + pcPluginLibraryFilename); + exit(1); + } + if (strcmp(psDescriptor->Label, pcPluginLabel) == 0) + return psDescriptor; + } +} + +/*****************************************************************************/ + +/* EOF */ diff --git a/src/makefile b/src/makefile new file mode 100644 index 0000000..886237f --- /dev/null +++ b/src/makefile @@ -0,0 +1,126 @@ +############################################################################### +# +# Installation DIRECTORIES +# +# Change these if you want to install somewhere else. + +INSTALL_PLUGINS_DIR = /usr/lib/ladspa/ +INSTALL_INCLUDE_DIR = /usr/include/ +INSTALL_BINARY_DIR = /usr/bin/ + +############################################################################### +# +# GENERAL +# + +INCLUDES = -I. +LIBRARIES = -ldl -lm +CFLAGS = $(INCLUDES) -Wall -Werror -O3 -fPIC +CXXFLAGS = $(CFLAGS) +PLUGINS = ../plugins/amp.so \ + ../plugins/delay.so \ + ../plugins/filter.so \ + ../plugins/noise.so \ + ../plugins/sine.so +PROGRAMS = ../bin/analyseplugin \ + ../bin/applyplugin \ + ../bin/listplugins +CC = cc +CPP = c++ + +############################################################################### +# +# RULES TO BUILD PLUGINS FROM C OR C++ CODE +# + +../plugins/%.so: plugins/%.c ladspa.h + $(CC) $(CFLAGS) -o plugins/$*.o -c plugins/$*.c + $(LD) -o ../plugins/$*.so plugins/$*.o -shared + +../plugins/%.so: plugins/%.cpp ladspa.h + $(CPP) $(CXXFLAGS) -o plugins/$*.o -c plugins/$*.cpp + $(CPP) -o ../plugins/$*.so plugins/$*.o -shared + +############################################################################### +# +# TARGETS +# + +test: /tmp/test.wav ../snd/noise.wav always + @echo --------------------------------------------- + @echo First listen to the white noise input signal: + @echo --------------------------------------------- + -sndfile-play ../snd/noise.wav + @echo ------------------------- + @echo Compare to plugin output. + @echo ------------------------- + @echo Should be a noise band around 6000Hz, repeated quietly after 1s. + -sndfile-play /tmp/test.wav + @echo Test complete. + +install: targets + -mkdirhier $(INSTALL_PLUGINS_DIR) + -mkdirhier $(INSTALL_INCLUDE_DIR) + -mkdirhier $(INSTALL_BINARY_DIR) + cp ../plugins/* $(INSTALL_PLUGINS_DIR) + cp ladspa.h $(INSTALL_INCLUDE_DIR) + cp ../bin/* $(INSTALL_BINARY_DIR) + +/tmp/test.wav: targets ../snd/noise.wav + ../bin/listplugins + ../bin/analyseplugin ../plugins/filter.so + ../bin/analyseplugin ../plugins/delay.so + ../bin/analyseplugin ../plugins/sine.so + echo ; ../bin/analyseplugin -l ../plugins/sine.so ; echo + ../bin/analyseplugin ../plugins/amp.so + ../bin/analyseplugin ../plugins/noise.so + ../bin/applyplugin -s 1 \ + ../snd/noise.wav /tmp/test.wav \ + ../plugins/filter.so lpf 500 \ + ../plugins/filter.so lpf 500 \ + ../plugins/sine.so sine_fcaa 6000 \ + ../plugins/delay.so delay_5s 1 0.1 \ + ../plugins/amp.so amp_mono 4 \ + +targets: $(PLUGINS) $(PROGRAMS) + +############################################################################### +# +# PROGRAMS +# + +../bin/applyplugin: applyplugin.o load.o default.o + $(CC) $(CFLAGS) $(LIBRARIES) \ + -o ../bin/applyplugin \ + applyplugin.o load.o default.o + +../bin/analyseplugin: analyseplugin.o load.o default.o + $(CC) $(CFLAGS) $(LIBRARIES) \ + -o ../bin/analyseplugin \ + analyseplugin.o load.o default.o + +../bin/listplugins: listplugins.o search.o + $(CC) $(CFLAGS) $(LIBRARIES) \ + -o ../bin/listplugins \ + listplugins.o search.o + +############################################################################### +# +# UTILITIES +# + +always: + +clean: + -rm -f `find . -name "*.o"` ../bin/* ../plugins/* + -rm -f `find .. -name "*~"` + -rm -f *.bak core score.srt + -rm -f *.bb *.bbg *.da *-ann gmon.out bb.out + -rm -f `find .. -name "*.class"` + +backup: clean + (cd ../../; \ + tar czf `date '+../backup/ladspa_sdk.%Y%m%d%H%M.tgz'` ladspa_sdk/) + +############################################################################### + diff --git a/src/plugins/amp.c b/src/plugins/amp.c new file mode 100644 index 0000000..b6d2345 --- /dev/null +++ b/src/plugins/amp.c @@ -0,0 +1,364 @@ +/* amp.c + + Free software by Richard W.E. Furse. Do with as you will. No + warranty. + + This LADSPA plugin provides simple mono and stereo amplifiers. + + This file has poor memory protection. Failures during malloc() will + not recover nicely. */ + +/*****************************************************************************/ + +#include +#include + +/*****************************************************************************/ + +#include "ladspa.h" + +/*****************************************************************************/ + +/* The port numbers for the plugin: */ + +#define AMP_CONTROL 0 +#define AMP_INPUT1 1 +#define AMP_OUTPUT1 2 +#define AMP_INPUT2 3 +#define AMP_OUTPUT2 4 + +/*****************************************************************************/ + +/* The structure used to hold port connection information and state + (actually gain controls require no further state). */ + +typedef struct { + + /* Ports: + ------ */ + + LADSPA_Data * m_pfControlValue; + LADSPA_Data * m_pfInputBuffer1; + LADSPA_Data * m_pfOutputBuffer1; + LADSPA_Data * m_pfInputBuffer2; /* (Not used for mono) */ + LADSPA_Data * m_pfOutputBuffer2; /* (Not used for mono) */ + +} Amplifier; + +/*****************************************************************************/ + +/* Construct a new plugin instance. */ +LADSPA_Handle +instantiateAmplifier(const LADSPA_Descriptor * Descriptor, + unsigned long SampleRate) { + return malloc(sizeof(Amplifier)); +} + +/*****************************************************************************/ + +/* Connect a port to a data location. */ +void +connectPortToAmplifier(LADSPA_Handle Instance, + unsigned long Port, + LADSPA_Data * DataLocation) { + + Amplifier * psAmplifier; + + psAmplifier = (Amplifier *)Instance; + switch (Port) { + case AMP_CONTROL: + psAmplifier->m_pfControlValue = DataLocation; + break; + case AMP_INPUT1: + psAmplifier->m_pfInputBuffer1 = DataLocation; + break; + case AMP_OUTPUT1: + psAmplifier->m_pfOutputBuffer1 = DataLocation; + break; + case AMP_INPUT2: + /* (This should only happen for stereo.) */ + psAmplifier->m_pfInputBuffer2 = DataLocation; + break; + case AMP_OUTPUT2: + /* (This should only happen for stereo.) */ + psAmplifier->m_pfOutputBuffer2 = DataLocation; + break; + } +} + +/*****************************************************************************/ + +void +runMonoAmplifier(LADSPA_Handle Instance, + unsigned long SampleCount) { + + LADSPA_Data * pfInput; + LADSPA_Data * pfOutput; + LADSPA_Data fGain; + Amplifier * psAmplifier; + unsigned long lSampleIndex; + + psAmplifier = (Amplifier *)Instance; + + pfInput = psAmplifier->m_pfInputBuffer1; + pfOutput = psAmplifier->m_pfOutputBuffer1; + fGain = *(psAmplifier->m_pfControlValue); + + for (lSampleIndex = 0; lSampleIndex < SampleCount; lSampleIndex++) + *(pfOutput++) = *(pfInput++) * fGain; +} + +/*****************************************************************************/ + +void +runStereoAmplifier(LADSPA_Handle Instance, + unsigned long SampleCount) { + + LADSPA_Data * pfInput; + LADSPA_Data * pfOutput; + LADSPA_Data fGain; + Amplifier * psAmplifier; + unsigned long lSampleIndex; + + psAmplifier = (Amplifier *)Instance; + + fGain = *(psAmplifier->m_pfControlValue); + + pfInput = psAmplifier->m_pfInputBuffer1; + pfOutput = psAmplifier->m_pfOutputBuffer1; + for (lSampleIndex = 0; lSampleIndex < SampleCount; lSampleIndex++) + *(pfOutput++) = *(pfInput++) * fGain; + + pfInput = psAmplifier->m_pfInputBuffer2; + pfOutput = psAmplifier->m_pfOutputBuffer2; + for (lSampleIndex = 0; lSampleIndex < SampleCount; lSampleIndex++) + *(pfOutput++) = *(pfInput++) * fGain; +} + +/*****************************************************************************/ + +/* Throw away a simple delay line. */ +void +cleanupAmplifier(LADSPA_Handle Instance) { + free(Instance); +} + +/*****************************************************************************/ + +LADSPA_Descriptor * g_psMonoDescriptor = NULL; +LADSPA_Descriptor * g_psStereoDescriptor = NULL; + +/*****************************************************************************/ + +/* _init() is called automatically when the plugin library is first + loaded. */ +void +_init() { + + char ** pcPortNames; + LADSPA_PortDescriptor * piPortDescriptors; + LADSPA_PortRangeHint * psPortRangeHints; + + g_psMonoDescriptor + = (LADSPA_Descriptor *)malloc(sizeof(LADSPA_Descriptor)); + g_psStereoDescriptor + = (LADSPA_Descriptor *)malloc(sizeof(LADSPA_Descriptor)); + + if (g_psMonoDescriptor) { + + g_psMonoDescriptor->UniqueID + = 1048; + g_psMonoDescriptor->Label + = strdup("amp_mono"); + g_psMonoDescriptor->Properties + = LADSPA_PROPERTY_HARD_RT_CAPABLE; + g_psMonoDescriptor->Name + = strdup("Mono Amplifier"); + g_psMonoDescriptor->Maker + = strdup("Richard Furse (LADSPA example plugins)"); + g_psMonoDescriptor->Copyright + = strdup("None"); + g_psMonoDescriptor->PortCount + = 3; + piPortDescriptors + = (LADSPA_PortDescriptor *)calloc(3, sizeof(LADSPA_PortDescriptor)); + g_psMonoDescriptor->PortDescriptors + = (const LADSPA_PortDescriptor *)piPortDescriptors; + piPortDescriptors[AMP_CONTROL] + = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL; + piPortDescriptors[AMP_INPUT1] + = LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO; + piPortDescriptors[AMP_OUTPUT1] + = LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO; + pcPortNames + = (char **)calloc(3, sizeof(char *)); + g_psMonoDescriptor->PortNames + = (const char **)pcPortNames; + pcPortNames[AMP_CONTROL] + = strdup("Gain"); + pcPortNames[AMP_INPUT1] + = strdup("Input"); + pcPortNames[AMP_OUTPUT1] + = strdup("Output"); + psPortRangeHints = ((LADSPA_PortRangeHint *) + calloc(3, sizeof(LADSPA_PortRangeHint))); + g_psMonoDescriptor->PortRangeHints + = (const LADSPA_PortRangeHint *)psPortRangeHints; + psPortRangeHints[AMP_CONTROL].HintDescriptor + = (LADSPA_HINT_BOUNDED_BELOW + | LADSPA_HINT_LOGARITHMIC + | LADSPA_HINT_DEFAULT_1); + psPortRangeHints[AMP_CONTROL].LowerBound + = 0; + psPortRangeHints[AMP_INPUT1].HintDescriptor + = 0; + psPortRangeHints[AMP_OUTPUT1].HintDescriptor + = 0; + g_psMonoDescriptor->instantiate + = instantiateAmplifier; + g_psMonoDescriptor->connect_port + = connectPortToAmplifier; + g_psMonoDescriptor->activate + = NULL; + g_psMonoDescriptor->run + = runMonoAmplifier; + g_psMonoDescriptor->run_adding + = NULL; + g_psMonoDescriptor->set_run_adding_gain + = NULL; + g_psMonoDescriptor->deactivate + = NULL; + g_psMonoDescriptor->cleanup + = cleanupAmplifier; + } + + if (g_psStereoDescriptor) { + + g_psStereoDescriptor->UniqueID + = 1049; + g_psStereoDescriptor->Label + = strdup("amp_stereo"); + g_psStereoDescriptor->Properties + = LADSPA_PROPERTY_HARD_RT_CAPABLE; + g_psStereoDescriptor->Name + = strdup("Stereo Amplifier"); + g_psStereoDescriptor->Maker + = strdup("Richard Furse (LADSPA example plugins)"); + g_psStereoDescriptor->Copyright + = strdup("None"); + g_psStereoDescriptor->PortCount + = 5; + piPortDescriptors + = (LADSPA_PortDescriptor *)calloc(5, sizeof(LADSPA_PortDescriptor)); + g_psStereoDescriptor->PortDescriptors + = (const LADSPA_PortDescriptor *)piPortDescriptors; + piPortDescriptors[AMP_CONTROL] + = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL; + piPortDescriptors[AMP_INPUT1] + = LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO; + piPortDescriptors[AMP_OUTPUT1] + = LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO; + piPortDescriptors[AMP_INPUT2] + = LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO; + piPortDescriptors[AMP_OUTPUT2] + = LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO; + pcPortNames + = (char **)calloc(5, sizeof(char *)); + g_psStereoDescriptor->PortNames + = (const char **)pcPortNames; + pcPortNames[AMP_CONTROL] + = strdup("Gain"); + pcPortNames[AMP_INPUT1] + = strdup("Input (Left)"); + pcPortNames[AMP_OUTPUT1] + = strdup("Output (Left)"); + pcPortNames[AMP_INPUT2] + = strdup("Input (Right)"); + pcPortNames[AMP_OUTPUT2] + = strdup("Output (Right)"); + psPortRangeHints = ((LADSPA_PortRangeHint *) + calloc(5, sizeof(LADSPA_PortRangeHint))); + g_psStereoDescriptor->PortRangeHints + = (const LADSPA_PortRangeHint *)psPortRangeHints; + psPortRangeHints[AMP_CONTROL].HintDescriptor + = (LADSPA_HINT_BOUNDED_BELOW + | LADSPA_HINT_LOGARITHMIC + | LADSPA_HINT_DEFAULT_1); + psPortRangeHints[AMP_CONTROL].LowerBound + = 0; + psPortRangeHints[AMP_INPUT1].HintDescriptor + = 0; + psPortRangeHints[AMP_OUTPUT1].HintDescriptor + = 0; + psPortRangeHints[AMP_INPUT2].HintDescriptor + = 0; + psPortRangeHints[AMP_OUTPUT2].HintDescriptor + = 0; + g_psStereoDescriptor->instantiate + = instantiateAmplifier; + g_psStereoDescriptor->connect_port + = connectPortToAmplifier; + g_psStereoDescriptor->activate + = NULL; + g_psStereoDescriptor->run + = runStereoAmplifier; + g_psStereoDescriptor->run_adding + = NULL; + g_psStereoDescriptor->set_run_adding_gain + = NULL; + g_psStereoDescriptor->deactivate + = NULL; + g_psStereoDescriptor->cleanup + = cleanupAmplifier; + } +} + +/*****************************************************************************/ + +void +deleteDescriptor(LADSPA_Descriptor * psDescriptor) { + unsigned long lIndex; + if (psDescriptor) { + free((char *)psDescriptor->Label); + free((char *)psDescriptor->Name); + free((char *)psDescriptor->Maker); + free((char *)psDescriptor->Copyright); + free((LADSPA_PortDescriptor *)psDescriptor->PortDescriptors); + for (lIndex = 0; lIndex < psDescriptor->PortCount; lIndex++) + free((char *)(psDescriptor->PortNames[lIndex])); + free((char **)psDescriptor->PortNames); + free((LADSPA_PortRangeHint *)psDescriptor->PortRangeHints); + free(psDescriptor); + } +} + +/*****************************************************************************/ + +/* _fini() is called automatically when the library is unloaded. */ +void +_fini() { + deleteDescriptor(g_psMonoDescriptor); + deleteDescriptor(g_psStereoDescriptor); +} + +/*****************************************************************************/ + +/* Return a descriptor of the requested plugin type. There are two + plugin types available in this library (mono and stereo). */ +const LADSPA_Descriptor * +ladspa_descriptor(unsigned long Index) { + /* Return the requested descriptor or null if the index is out of + range. */ + switch (Index) { + case 0: + return g_psMonoDescriptor; + case 1: + return g_psStereoDescriptor; + default: + return NULL; + } +} + +/*****************************************************************************/ + +/* EOF */ diff --git a/src/plugins/delay.c b/src/plugins/delay.c new file mode 100644 index 0000000..8b03979 --- /dev/null +++ b/src/plugins/delay.c @@ -0,0 +1,356 @@ +/* delay.c + + Free software by Richard W.E. Furse. Do with as you will. No + warranty. + + This LADSPA plugin provides a simple delay line implemented in + C. There is a fixed maximum delay length and no feedback is + provided. + + This file has poor memory protection. Failures during malloc() will + not recover nicely. */ + +/*****************************************************************************/ + +#include +#include + +/*****************************************************************************/ + +#include "ladspa.h" + +/*****************************************************************************/ + +/* The maximum delay valid for the delay line (in seconds). If you + change this, remember that the label is currently "delay_5s". */ +#define MAX_DELAY 5 + +/*****************************************************************************/ + +/* The port numbers for the plugin: */ + +#define SDL_DELAY_LENGTH 0 +#define SDL_DRY_WET 1 +#define SDL_INPUT 2 +#define SDL_OUTPUT 3 + +/*****************************************************************************/ + +#define LIMIT_BETWEEN_0_AND_1(x) \ +(((x) < 0) ? 0 : (((x) > 1) ? 1 : (x))) +#define LIMIT_BETWEEN_0_AND_MAX_DELAY(x) \ +(((x) < 0) ? 0 : (((x) > MAX_DELAY) ? MAX_DELAY : (x))) + +/*****************************************************************************/ + +/* Instance data for the simple delay line plugin. */ +typedef struct { + + LADSPA_Data m_fSampleRate; + + LADSPA_Data * m_pfBuffer; + + /* Buffer size, a power of two. */ + unsigned long m_lBufferSize; + + /* Write pointer in buffer. */ + unsigned long m_lWritePointer; + + /* Ports: + ------ */ + + /* Delay control, in seconds. Accepted between 0 and 1 (only 1 sec + of buffer is allocated by this crude delay line). */ + LADSPA_Data * m_pfDelay; + + /* Dry/wet control. 0 for entirely dry, 1 for entirely wet. */ + LADSPA_Data * m_pfDryWet; + + /* Input audio port data location. */ + LADSPA_Data * m_pfInput; + + /* Output audio port data location. */ + LADSPA_Data * m_pfOutput; + +} SimpleDelayLine; + +/*****************************************************************************/ + +/* Construct a new plugin instance. */ +LADSPA_Handle +instantiateSimpleDelayLine(const LADSPA_Descriptor * Descriptor, + unsigned long SampleRate) { + + unsigned long lMinimumBufferSize; + SimpleDelayLine * psDelayLine; + + psDelayLine + = (SimpleDelayLine *)malloc(sizeof(SimpleDelayLine)); + + if (psDelayLine == NULL) + return NULL; + + psDelayLine->m_fSampleRate = (LADSPA_Data)SampleRate; + + /* Buffer size is a power of two bigger than max delay time. */ + lMinimumBufferSize = (unsigned long)((LADSPA_Data)SampleRate * MAX_DELAY); + psDelayLine->m_lBufferSize = 1; + while (psDelayLine->m_lBufferSize < lMinimumBufferSize) + psDelayLine->m_lBufferSize <<= 1; + + psDelayLine->m_pfBuffer + = (LADSPA_Data *)calloc(psDelayLine->m_lBufferSize, sizeof(LADSPA_Data)); + if (psDelayLine->m_pfBuffer == NULL) { + free(psDelayLine); + return NULL; + } + + psDelayLine->m_lWritePointer = 0; + + return psDelayLine; +} + +/*****************************************************************************/ + +/* Initialise and activate a plugin instance. */ +void +activateSimpleDelayLine(LADSPA_Handle Instance) { + + SimpleDelayLine * psSimpleDelayLine; + psSimpleDelayLine = (SimpleDelayLine *)Instance; + + /* Need to reset the delay history in this function rather than + instantiate() in case deactivate() followed by activate() have + been called to reinitialise a delay line. */ + memset(psSimpleDelayLine->m_pfBuffer, + 0, + sizeof(LADSPA_Data) * psSimpleDelayLine->m_lBufferSize); +} + +/*****************************************************************************/ + +/* Connect a port to a data location. */ +void +connectPortToSimpleDelayLine(LADSPA_Handle Instance, + unsigned long Port, + LADSPA_Data * DataLocation) { + + SimpleDelayLine * psSimpleDelayLine; + + psSimpleDelayLine = (SimpleDelayLine *)Instance; + switch (Port) { + case SDL_DELAY_LENGTH: + psSimpleDelayLine->m_pfDelay = DataLocation; + break; + case SDL_DRY_WET: + psSimpleDelayLine->m_pfDryWet = DataLocation; + break; + case SDL_INPUT: + psSimpleDelayLine->m_pfInput = DataLocation; + break; + case SDL_OUTPUT: + psSimpleDelayLine->m_pfOutput = DataLocation; + break; + } +} + +/*****************************************************************************/ + +/* Run a delay line instance for a block of SampleCount samples. */ +void +runSimpleDelayLine(LADSPA_Handle Instance, + unsigned long SampleCount) { + + LADSPA_Data * pfBuffer; + LADSPA_Data * pfInput; + LADSPA_Data * pfOutput; + LADSPA_Data fDry; + LADSPA_Data fInputSample; + LADSPA_Data fWet; + SimpleDelayLine * psSimpleDelayLine; + unsigned long lBufferReadOffset; + unsigned long lBufferSizeMinusOne; + unsigned long lBufferWriteOffset; + unsigned long lDelay; + unsigned long lSampleIndex; + + psSimpleDelayLine = (SimpleDelayLine *)Instance; + + lBufferSizeMinusOne = psSimpleDelayLine->m_lBufferSize - 1; + lDelay = (unsigned long) + (LIMIT_BETWEEN_0_AND_MAX_DELAY(*(psSimpleDelayLine->m_pfDelay)) + * psSimpleDelayLine->m_fSampleRate); + + pfInput = psSimpleDelayLine->m_pfInput; + pfOutput = psSimpleDelayLine->m_pfOutput; + pfBuffer = psSimpleDelayLine->m_pfBuffer; + lBufferWriteOffset = psSimpleDelayLine->m_lWritePointer; + lBufferReadOffset + = lBufferWriteOffset + psSimpleDelayLine->m_lBufferSize - lDelay; + fWet = LIMIT_BETWEEN_0_AND_1(*(psSimpleDelayLine->m_pfDryWet)); + fDry = 1 - fWet; + + for (lSampleIndex = 0; + lSampleIndex < SampleCount; + lSampleIndex++) { + fInputSample = *(pfInput++); + *(pfOutput++) = (fDry * fInputSample + + fWet * pfBuffer[((lSampleIndex + lBufferReadOffset) + & lBufferSizeMinusOne)]); + pfBuffer[((lSampleIndex + lBufferWriteOffset) + & lBufferSizeMinusOne)] = fInputSample; + } + + psSimpleDelayLine->m_lWritePointer + = ((psSimpleDelayLine->m_lWritePointer + SampleCount) + & lBufferSizeMinusOne); +} + +/*****************************************************************************/ + +/* Throw away a simple delay line. */ +void +cleanupSimpleDelayLine(LADSPA_Handle Instance) { + + SimpleDelayLine * psSimpleDelayLine; + + psSimpleDelayLine = (SimpleDelayLine *)Instance; + + free(psSimpleDelayLine->m_pfBuffer); + free(psSimpleDelayLine); +} + +/*****************************************************************************/ + +LADSPA_Descriptor * g_psDescriptor = NULL; + +/*****************************************************************************/ + +/* _init() is called automatically when the plugin library is first + loaded. */ +void +_init() { + + char ** pcPortNames; + LADSPA_PortDescriptor * piPortDescriptors; + LADSPA_PortRangeHint * psPortRangeHints; + + g_psDescriptor + = (LADSPA_Descriptor *)malloc(sizeof(LADSPA_Descriptor)); + if (g_psDescriptor) { + g_psDescriptor->UniqueID + = 1043; + g_psDescriptor->Label + = strdup("delay_5s"); + g_psDescriptor->Properties + = LADSPA_PROPERTY_HARD_RT_CAPABLE; + g_psDescriptor->Name + = strdup("Simple Delay Line"); + g_psDescriptor->Maker + = strdup("Richard Furse (LADSPA example plugins)"); + g_psDescriptor->Copyright + = strdup("None"); + g_psDescriptor->PortCount + = 4; + piPortDescriptors + = (LADSPA_PortDescriptor *)calloc(4, sizeof(LADSPA_PortDescriptor)); + g_psDescriptor->PortDescriptors + = (const LADSPA_PortDescriptor *)piPortDescriptors; + piPortDescriptors[SDL_DELAY_LENGTH] + = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL; + piPortDescriptors[SDL_DRY_WET] + = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL; + piPortDescriptors[SDL_INPUT] + = LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO; + piPortDescriptors[SDL_OUTPUT] + = LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO; + pcPortNames + = (char **)calloc(4, sizeof(char *)); + g_psDescriptor->PortNames + = (const char **)pcPortNames; + pcPortNames[SDL_DELAY_LENGTH] + = strdup("Delay (Seconds)"); + pcPortNames[SDL_DRY_WET] + = strdup("Dry/Wet Balance"); + pcPortNames[SDL_INPUT] + = strdup("Input"); + pcPortNames[SDL_OUTPUT] + = strdup("Output"); + psPortRangeHints = ((LADSPA_PortRangeHint *) + calloc(4, sizeof(LADSPA_PortRangeHint))); + g_psDescriptor->PortRangeHints + = (const LADSPA_PortRangeHint *)psPortRangeHints; + psPortRangeHints[SDL_DELAY_LENGTH].HintDescriptor + = (LADSPA_HINT_BOUNDED_BELOW + | LADSPA_HINT_BOUNDED_ABOVE + | LADSPA_HINT_DEFAULT_1); + psPortRangeHints[SDL_DELAY_LENGTH].LowerBound + = 0; + psPortRangeHints[SDL_DELAY_LENGTH].UpperBound + = (LADSPA_Data)MAX_DELAY; + psPortRangeHints[SDL_DRY_WET].HintDescriptor + = (LADSPA_HINT_BOUNDED_BELOW + | LADSPA_HINT_BOUNDED_ABOVE + | LADSPA_HINT_DEFAULT_MIDDLE); + psPortRangeHints[SDL_DRY_WET].LowerBound + = 0; + psPortRangeHints[SDL_DRY_WET].UpperBound + = 1; + psPortRangeHints[SDL_INPUT].HintDescriptor + = 0; + psPortRangeHints[SDL_OUTPUT].HintDescriptor + = 0; + g_psDescriptor->instantiate + = instantiateSimpleDelayLine; + g_psDescriptor->connect_port + = connectPortToSimpleDelayLine; + g_psDescriptor->activate + = activateSimpleDelayLine; + g_psDescriptor->run + = runSimpleDelayLine; + g_psDescriptor->run_adding + = NULL; + g_psDescriptor->set_run_adding_gain + = NULL; + g_psDescriptor->deactivate + = NULL; + g_psDescriptor->cleanup + = cleanupSimpleDelayLine; + } +} + +/*****************************************************************************/ + +/* _fini() is called automatically when the library is unloaded. */ +void +_fini() { + long lIndex; + if (g_psDescriptor) { + free((char *)g_psDescriptor->Label); + free((char *)g_psDescriptor->Name); + free((char *)g_psDescriptor->Maker); + free((char *)g_psDescriptor->Copyright); + free((LADSPA_PortDescriptor *)g_psDescriptor->PortDescriptors); + for (lIndex = 0; lIndex < g_psDescriptor->PortCount; lIndex++) + free((char *)(g_psDescriptor->PortNames[lIndex])); + free((char **)g_psDescriptor->PortNames); + free((LADSPA_PortRangeHint *)g_psDescriptor->PortRangeHints); + free(g_psDescriptor); + } +} + +/*****************************************************************************/ + +/* Return a descriptor of the requested plugin type. Only one plugin + type is available in this library. */ +const LADSPA_Descriptor * +ladspa_descriptor(unsigned long Index) { + if (Index == 0) + return g_psDescriptor; + else + return NULL; +} + +/*****************************************************************************/ + +/* EOF */ diff --git a/src/plugins/filter.c b/src/plugins/filter.c new file mode 100644 index 0000000..3f50457 --- /dev/null +++ b/src/plugins/filter.c @@ -0,0 +1,460 @@ +/* filter.c + + Free software by Richard W.E. Furse. Do with as you will. No + warranty. + + This LADSPA plugin provides simple (one poll) low-pass and + high-pass filters implemented in C. + + This file has poor memory protection. Failures during malloc() will + not recover nicely. */ + +/*****************************************************************************/ + +#include +#include +#include + +/*****************************************************************************/ + +#include "ladspa.h" + +/*****************************************************************************/ + +#define SF_CUTOFF 0 +#define SF_INPUT 1 +#define SF_OUTPUT 2 + +/*****************************************************************************/ + +/* Instance data for the simple filter. We can get away with using + this structure for both low- and high-pass filters because the data + stored is the same. Note that the actual run() calls differ + however. */ +typedef struct { + + LADSPA_Data m_fSampleRate; + LADSPA_Data m_fTwoPiOverSampleRate; + + LADSPA_Data m_fLastOutput; + LADSPA_Data m_fLastCutoff; + LADSPA_Data m_fAmountOfCurrent; + LADSPA_Data m_fAmountOfLast; + + /* Ports: + ------ */ + + LADSPA_Data * m_pfCutoff; + LADSPA_Data * m_pfInput; + LADSPA_Data * m_pfOutput; + +} SimpleFilter; + +/*****************************************************************************/ + +/* Construct a new plugin instance. In this case, as the SimpleFilter + structure can be used for low- or high-pass filters we can get away + with only only writing one of these functions. Normally one would + be required for each plugin type. */ +LADSPA_Handle +instantiateSimpleFilter(const LADSPA_Descriptor * Descriptor, + unsigned long SampleRate) { + + SimpleFilter * psFilter; + + psFilter = (SimpleFilter *)malloc(sizeof(SimpleFilter)); + + if (psFilter) { + psFilter->m_fSampleRate = (LADSPA_Data)SampleRate; + psFilter->m_fTwoPiOverSampleRate = (2 * M_PI) / (LADSPA_Data)SampleRate; + psFilter->m_fLastOutput = 0; + psFilter->m_fLastCutoff = 0; + psFilter->m_fAmountOfCurrent = 0; + psFilter->m_fAmountOfLast = 0; + } + + return psFilter; +} + +/*****************************************************************************/ + +/* Initialise and activate a plugin instance. Normally separate + functions would have to be written for the different plugin types, + however we can get away with a single function in this case. */ +void +activateSimpleFilter(LADSPA_Handle Instance) { + SimpleFilter * psSimpleFilter; + psSimpleFilter = (SimpleFilter *)Instance; + psSimpleFilter->m_fLastOutput = 0; +} + +/*****************************************************************************/ + +/* Connect a port to a data location. Normally separate functions + would have to be written for the different plugin types, however we + can get away with a single function in this case. */ +void +connectPortToSimpleFilter(LADSPA_Handle Instance, + unsigned long Port, + LADSPA_Data * DataLocation) { + + SimpleFilter * psFilter; + + psFilter = (SimpleFilter *)Instance; + + switch (Port) { + case SF_CUTOFF: + psFilter->m_pfCutoff = DataLocation; + break; + case SF_INPUT: + psFilter->m_pfInput = DataLocation; + break; + case SF_OUTPUT: + psFilter->m_pfOutput = DataLocation; + break; + } +} + +/*****************************************************************************/ + +/* Run the LPF algorithm for a block of SampleCount samples. */ +void +runSimpleLowPassFilter(LADSPA_Handle Instance, + unsigned long SampleCount) { + + LADSPA_Data * pfInput; + LADSPA_Data * pfOutput; + LADSPA_Data fAmountOfCurrent; + LADSPA_Data fAmountOfLast; + LADSPA_Data fComp; + LADSPA_Data fLastOutput; + SimpleFilter * psFilter; + unsigned long lSampleIndex; + + psFilter = (SimpleFilter *)Instance; + + pfInput = psFilter->m_pfInput; + pfOutput = psFilter->m_pfOutput; + + if (*psFilter->m_pfCutoff != + psFilter->m_fLastCutoff) { + psFilter->m_fLastCutoff = *psFilter->m_pfCutoff; + if (psFilter->m_fLastCutoff <= 0) { + /* Reject everything. */ + psFilter->m_fAmountOfCurrent = psFilter->m_fAmountOfLast = 0; + } + else if (psFilter->m_fLastCutoff > psFilter->m_fSampleRate * 0.5) { + /* Above Nyquist frequency. Let everything through. */ + psFilter->m_fAmountOfCurrent = 1; + psFilter->m_fAmountOfLast = 0; + } + else { + psFilter->m_fAmountOfLast = 0; + fComp = 2 - cos(psFilter->m_fTwoPiOverSampleRate + * psFilter->m_fLastCutoff); + psFilter->m_fAmountOfLast = fComp - (LADSPA_Data)sqrt(fComp * fComp - 1); + psFilter->m_fAmountOfCurrent = 1 - psFilter->m_fAmountOfLast; + } + + } + + fAmountOfCurrent = psFilter->m_fAmountOfCurrent; + fAmountOfLast = psFilter->m_fAmountOfLast; + fLastOutput = psFilter->m_fLastOutput; + + for (lSampleIndex = 0; + lSampleIndex < SampleCount; + lSampleIndex++) { + *(pfOutput++) + = fLastOutput + = (fAmountOfCurrent * *(pfInput++) + + fAmountOfLast * fLastOutput); + } + + psFilter->m_fLastOutput = fLastOutput; +} + +/*****************************************************************************/ + +/* Run the HPF algorithm for a block of SampleCount samples. */ +void +runSimpleHighPassFilter(LADSPA_Handle Instance, + unsigned long SampleCount) { + + LADSPA_Data * pfInput; + LADSPA_Data * pfOutput; + LADSPA_Data fAmountOfCurrent; + LADSPA_Data fAmountOfLast; + LADSPA_Data fComp; + LADSPA_Data fLastOutput; + SimpleFilter * psFilter; + unsigned long lSampleIndex; + + psFilter = (SimpleFilter *)Instance; + + pfInput = psFilter->m_pfInput; + pfOutput = psFilter->m_pfOutput; + + if (*psFilter->m_pfCutoff != + psFilter->m_fLastCutoff) { + psFilter->m_fLastCutoff = *psFilter->m_pfCutoff; + if (psFilter->m_fLastCutoff <= 0) { + /* Let everything through. */ + psFilter->m_fAmountOfCurrent = 1; + psFilter->m_fAmountOfLast = 0; + } + else if (psFilter->m_fLastCutoff > psFilter->m_fSampleRate * 0.5) { + /* Above Nyquist frequency. Reject everything. */ + psFilter->m_fAmountOfCurrent = psFilter->m_fAmountOfLast = 0; + } + else { + psFilter->m_fAmountOfLast = 0; + fComp = 2 - cos(psFilter->m_fTwoPiOverSampleRate + * psFilter->m_fLastCutoff); + psFilter->m_fAmountOfLast = fComp - (LADSPA_Data)sqrt(fComp * fComp - 1); + psFilter->m_fAmountOfCurrent = 1 - psFilter->m_fAmountOfLast; + } + + } + + fAmountOfCurrent = psFilter->m_fAmountOfCurrent; + fAmountOfLast = psFilter->m_fAmountOfLast; + fLastOutput = psFilter->m_fLastOutput; + + for (lSampleIndex = 0; + lSampleIndex < SampleCount; + lSampleIndex++) { + fLastOutput + = (fAmountOfCurrent * *pfInput + + fAmountOfLast * fLastOutput); + *(pfOutput++) = *(pfInput++) - fLastOutput; + } + + psFilter->m_fLastOutput = fLastOutput; +} + +/*****************************************************************************/ + +/* Throw away a filter instance. Normally separate functions + would have to be written for the different plugin types, however we + can get away with a single function in this case. */ +void +cleanupSimpleFilter(LADSPA_Handle Instance) { + free(Instance); +} + +/*****************************************************************************/ + +LADSPA_Descriptor * g_psLPFDescriptor = NULL; +LADSPA_Descriptor * g_psHPFDescriptor = NULL; + +/*****************************************************************************/ + +/* _init() is called automatically when the plugin library is first + loaded. */ +void +_init() { + + char ** pcPortNames; + LADSPA_PortDescriptor * piPortDescriptors; + LADSPA_PortRangeHint * psPortRangeHints; + + g_psLPFDescriptor + = (LADSPA_Descriptor *)malloc(sizeof(LADSPA_Descriptor)); + g_psHPFDescriptor + = (LADSPA_Descriptor *)malloc(sizeof(LADSPA_Descriptor)); + + if (g_psLPFDescriptor != NULL) { + + g_psLPFDescriptor->UniqueID + = 1041; + g_psLPFDescriptor->Label + = strdup("lpf"); + g_psLPFDescriptor->Properties + = LADSPA_PROPERTY_HARD_RT_CAPABLE; + g_psLPFDescriptor->Name + = strdup("Simple Low Pass Filter"); + g_psLPFDescriptor->Maker + = strdup("Richard Furse (LADSPA example plugins)"); + g_psLPFDescriptor->Copyright + = strdup("None"); + g_psLPFDescriptor->PortCount + = 3; + piPortDescriptors + = (LADSPA_PortDescriptor *)calloc(3, sizeof(LADSPA_PortDescriptor)); + g_psLPFDescriptor->PortDescriptors + = (const LADSPA_PortDescriptor *)piPortDescriptors; + piPortDescriptors[SF_CUTOFF] + = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL; + piPortDescriptors[SF_INPUT] + = LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO; + piPortDescriptors[SF_OUTPUT] + = LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO; + pcPortNames + = (char **)calloc(3, sizeof(char *)); + g_psLPFDescriptor->PortNames + = (const char **)pcPortNames; + pcPortNames[SF_CUTOFF] + = strdup("Cutoff Frequency (Hz)"); + pcPortNames[SF_INPUT] + = strdup("Input"); + pcPortNames[SF_OUTPUT] + = strdup("Output"); + psPortRangeHints = ((LADSPA_PortRangeHint *) + calloc(3, sizeof(LADSPA_PortRangeHint))); + g_psLPFDescriptor->PortRangeHints + = (const LADSPA_PortRangeHint *)psPortRangeHints; + psPortRangeHints[SF_CUTOFF].HintDescriptor + = (LADSPA_HINT_BOUNDED_BELOW + | LADSPA_HINT_BOUNDED_ABOVE + | LADSPA_HINT_SAMPLE_RATE + | LADSPA_HINT_LOGARITHMIC + | LADSPA_HINT_DEFAULT_440); + psPortRangeHints[SF_CUTOFF].LowerBound + = 0; + psPortRangeHints[SF_CUTOFF].UpperBound + = 0.5; /* Nyquist frequency (half the sample rate) */ + psPortRangeHints[SF_INPUT].HintDescriptor + = 0; + psPortRangeHints[SF_OUTPUT].HintDescriptor + = 0; + g_psLPFDescriptor->instantiate + = instantiateSimpleFilter; + g_psLPFDescriptor->connect_port + = connectPortToSimpleFilter; + g_psLPFDescriptor->activate + = activateSimpleFilter; + g_psLPFDescriptor->run + = runSimpleLowPassFilter; + g_psLPFDescriptor->run_adding + = NULL; + g_psLPFDescriptor->set_run_adding_gain + = NULL; + g_psLPFDescriptor->deactivate + = NULL; + g_psLPFDescriptor->cleanup + = cleanupSimpleFilter; + } + + if (g_psHPFDescriptor != NULL) { + + g_psHPFDescriptor->UniqueID + = 1042; + g_psHPFDescriptor->Label + = strdup("hpf"); + g_psHPFDescriptor->Properties + = LADSPA_PROPERTY_HARD_RT_CAPABLE; + g_psHPFDescriptor->Name + = strdup("Simple High Pass Filter"); + g_psHPFDescriptor->Maker + = strdup("Richard Furse (LADSPA example plugins)"); + g_psHPFDescriptor->Copyright + = strdup("None"); + g_psHPFDescriptor->PortCount + = 3; + piPortDescriptors + = (LADSPA_PortDescriptor *)calloc(3, sizeof(LADSPA_PortDescriptor)); + g_psHPFDescriptor->PortDescriptors + = (const LADSPA_PortDescriptor *)piPortDescriptors; + piPortDescriptors[SF_CUTOFF] + = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL; + piPortDescriptors[SF_INPUT] + = LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO; + piPortDescriptors[SF_OUTPUT] + = LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO; + pcPortNames + = (char **)calloc(3, sizeof(char *)); + g_psHPFDescriptor->PortNames + = (const char **)pcPortNames; + pcPortNames[SF_CUTOFF] + = strdup("Cutoff Frequency (Hz)"); + pcPortNames[SF_INPUT] + = strdup("Input"); + pcPortNames[SF_OUTPUT] + = strdup("Output"); + psPortRangeHints = ((LADSPA_PortRangeHint *) + calloc(3, sizeof(LADSPA_PortRangeHint))); + g_psHPFDescriptor->PortRangeHints + = (const LADSPA_PortRangeHint *)psPortRangeHints; + psPortRangeHints[SF_CUTOFF].HintDescriptor + = (LADSPA_HINT_BOUNDED_BELOW + | LADSPA_HINT_BOUNDED_ABOVE + | LADSPA_HINT_SAMPLE_RATE + | LADSPA_HINT_LOGARITHMIC + | LADSPA_HINT_DEFAULT_440); + psPortRangeHints[SF_CUTOFF].LowerBound + = 0; + psPortRangeHints[SF_CUTOFF].UpperBound + = 0.5; /* Nyquist frequency (half the sample rate) */ + psPortRangeHints[SF_INPUT].HintDescriptor + = 0; + psPortRangeHints[SF_OUTPUT].HintDescriptor + = 0; + g_psHPFDescriptor->instantiate + = instantiateSimpleFilter; + g_psHPFDescriptor->connect_port + = connectPortToSimpleFilter; + g_psHPFDescriptor->activate + = activateSimpleFilter; + g_psHPFDescriptor->run + = runSimpleHighPassFilter; + g_psHPFDescriptor->run_adding + = NULL; + g_psHPFDescriptor->set_run_adding_gain + = NULL; + g_psHPFDescriptor->deactivate + = NULL; + g_psHPFDescriptor->cleanup + = cleanupSimpleFilter; + } +} + +/*****************************************************************************/ + +void +deleteDescriptor(LADSPA_Descriptor * psDescriptor) { + unsigned long lIndex; + if (psDescriptor) { + free((char *)psDescriptor->Label); + free((char *)psDescriptor->Name); + free((char *)psDescriptor->Maker); + free((char *)psDescriptor->Copyright); + free((LADSPA_PortDescriptor *)psDescriptor->PortDescriptors); + for (lIndex = 0; lIndex < psDescriptor->PortCount; lIndex++) + free((char *)(psDescriptor->PortNames[lIndex])); + free((char **)psDescriptor->PortNames); + free((LADSPA_PortRangeHint *)psDescriptor->PortRangeHints); + free(psDescriptor); + } +} + +/*****************************************************************************/ + +/* _fini() is called automatically when the library is unloaded. */ +void +_fini() { + deleteDescriptor(g_psLPFDescriptor); + deleteDescriptor(g_psHPFDescriptor); +} + +/*****************************************************************************/ + +/* Return a descriptor of the requested plugin type. There are two + plugin types available in this library. */ +const LADSPA_Descriptor * +ladspa_descriptor(unsigned long Index) { + /* Return the requested descriptor or null if the index is out of + range. */ + switch (Index) { + case 0: + return g_psLPFDescriptor; + case 1: + return g_psHPFDescriptor; + default: + return NULL; + } +} + +/*****************************************************************************/ + +/* EOF */ diff --git a/src/plugins/noise.c b/src/plugins/noise.c new file mode 100644 index 0000000..0fdd938 --- /dev/null +++ b/src/plugins/noise.c @@ -0,0 +1,252 @@ +/* noise.c + + Free software by Richard W.E. Furse. Do with as you will. No + warranty. + + This LADSPA plugin provides a simple mono noise source. + + This file has poor memory protection. Failures during malloc() will + not recover nicely. */ + +/*****************************************************************************/ + +#include +#include + +/*****************************************************************************/ + +#include "ladspa.h" + +/*****************************************************************************/ + +/* The port numbers for the plugin: */ + +#define NOISE_AMPLITUDE 0 +#define NOISE_OUTPUT 1 + +/*****************************************************************************/ + +/* The structure used to hold port connection information (and gain if + runAdding() is in use) and state (actually there's no further state + to store here). */ + +typedef struct { + + /* Ports: + ------ */ + + LADSPA_Data * m_pfAmplitudeValue; + LADSPA_Data * m_pfOutputBuffer; + + /* Run Adding Gain: + ---------------- */ + + LADSPA_Data m_fRunAddingGain; + +} NoiseSource; + +/*****************************************************************************/ + +/* Construct a new plugin instance. */ +LADSPA_Handle +instantiateNoiseSource(const LADSPA_Descriptor * Descriptor, + unsigned long SampleRate) { + return malloc(sizeof(NoiseSource)); +} + +/*****************************************************************************/ + +/* Connect a port to a data location. */ +void +connectPortToNoiseSource(LADSPA_Handle Instance, + unsigned long Port, + LADSPA_Data * DataLocation) { + switch (Port) { + case NOISE_AMPLITUDE: + ((NoiseSource *)Instance)->m_pfAmplitudeValue = DataLocation; + break; + case NOISE_OUTPUT: + ((NoiseSource *)Instance)->m_pfOutputBuffer = DataLocation; + break; + } +} + +/*****************************************************************************/ + +/* Run a delay line instance for a block of SampleCount samples. */ +void +runNoiseSource(LADSPA_Handle Instance, + unsigned long SampleCount) { + + NoiseSource * psNoiseSource; + LADSPA_Data * pfOutput; + LADSPA_Data fAmplitude; + unsigned long lSampleIndex; + + psNoiseSource = (NoiseSource *)Instance; + + fAmplitude + = *(psNoiseSource->m_pfAmplitudeValue) * (LADSPA_Data)(2.0 / RAND_MAX); + + pfOutput = psNoiseSource->m_pfOutputBuffer; + for (lSampleIndex = 0; lSampleIndex < SampleCount; lSampleIndex++) + *(pfOutput++) = (rand() - (RAND_MAX / 2)) * fAmplitude; +} + +/*****************************************************************************/ + +/* Run a delay line instance for a block of SampleCount samples. *ADD* + the output to the output buffer. */ +void +runAddingNoiseSource(LADSPA_Handle Instance, + unsigned long SampleCount) { + + NoiseSource * psNoiseSource; + LADSPA_Data * pfOutput; + LADSPA_Data fAmplitude; + unsigned long lSampleIndex; + + psNoiseSource = (NoiseSource *)Instance; + + fAmplitude + = (*(psNoiseSource->m_pfAmplitudeValue) + * psNoiseSource->m_fRunAddingGain + * (LADSPA_Data)(2.0 / RAND_MAX)); + + pfOutput = psNoiseSource->m_pfOutputBuffer; + for (lSampleIndex = 0; lSampleIndex < SampleCount; lSampleIndex++) + *(pfOutput++) += (rand() - (RAND_MAX / 2)) * fAmplitude; +} + +/*****************************************************************************/ + +void +setNoiseSourceRunAddingGain(LADSPA_Handle Instance, + LADSPA_Data Gain) { + ((NoiseSource *)Instance)->m_fRunAddingGain = Gain; +} + +/*****************************************************************************/ + +/* Throw away a simple delay line. */ +void +cleanupNoiseSource(LADSPA_Handle Instance) { + free(Instance); +} + +/*****************************************************************************/ + +LADSPA_Descriptor * g_psDescriptor; + +/*****************************************************************************/ + +/* _init() is called automatically when the plugin library is first + loaded. */ +void +_init() { + + char ** pcPortNames; + LADSPA_PortDescriptor * piPortDescriptors; + LADSPA_PortRangeHint * psPortRangeHints; + + g_psDescriptor + = (LADSPA_Descriptor *)malloc(sizeof(LADSPA_Descriptor)); + + if (g_psDescriptor) { + + g_psDescriptor->UniqueID + = 1050; + g_psDescriptor->Label + = strdup("noise_white"); + g_psDescriptor->Properties + = LADSPA_PROPERTY_HARD_RT_CAPABLE; + g_psDescriptor->Name + = strdup("White Noise Source"); + g_psDescriptor->Maker + = strdup("Richard Furse (LADSPA example plugins)"); + g_psDescriptor->Copyright + = strdup("None"); + g_psDescriptor->PortCount + = 2; + piPortDescriptors + = (LADSPA_PortDescriptor *)calloc(2, sizeof(LADSPA_PortDescriptor)); + g_psDescriptor->PortDescriptors + = (const LADSPA_PortDescriptor *)piPortDescriptors; + piPortDescriptors[NOISE_AMPLITUDE] + = (LADSPA_PORT_INPUT + | LADSPA_PORT_CONTROL); + piPortDescriptors[NOISE_OUTPUT] + = LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO; + pcPortNames + = (char **)calloc(2, sizeof(char *)); + g_psDescriptor->PortNames + = (const char **)pcPortNames; + pcPortNames[NOISE_AMPLITUDE] + = strdup("Amplitude"); + pcPortNames[NOISE_OUTPUT] + = strdup("Output"); + psPortRangeHints = ((LADSPA_PortRangeHint *) + calloc(2, sizeof(LADSPA_PortRangeHint))); + g_psDescriptor->PortRangeHints + = (const LADSPA_PortRangeHint *)psPortRangeHints; + psPortRangeHints[NOISE_AMPLITUDE].HintDescriptor + = (LADSPA_HINT_BOUNDED_BELOW + | LADSPA_HINT_LOGARITHMIC + | LADSPA_HINT_DEFAULT_1); + psPortRangeHints[NOISE_AMPLITUDE].LowerBound + = 0; + psPortRangeHints[NOISE_OUTPUT].HintDescriptor + = 0; + g_psDescriptor->instantiate + = instantiateNoiseSource; + g_psDescriptor->connect_port + = connectPortToNoiseSource; + g_psDescriptor->activate + = NULL; + g_psDescriptor->run + = runNoiseSource; + g_psDescriptor->run_adding + = runAddingNoiseSource; + g_psDescriptor->set_run_adding_gain + = setNoiseSourceRunAddingGain; + g_psDescriptor->deactivate + = NULL; + g_psDescriptor->cleanup + = cleanupNoiseSource; + } +} + +/*****************************************************************************/ + +/* _fini() is called automatically when the library is unloaded. */ +void +_fini() { + long lIndex; + if (g_psDescriptor) { + free((char *)g_psDescriptor->Label); + free((char *)g_psDescriptor->Name); + free((char *)g_psDescriptor->Maker); + free((char *)g_psDescriptor->Copyright); + free((LADSPA_PortDescriptor *)g_psDescriptor->PortDescriptors); + for (lIndex = 0; lIndex < g_psDescriptor->PortCount; lIndex++) + free((char *)(g_psDescriptor->PortNames[lIndex])); + free((char **)g_psDescriptor->PortNames); + free((LADSPA_PortRangeHint *)g_psDescriptor->PortRangeHints); + free(g_psDescriptor); + } +} + +/*****************************************************************************/ + +/* Return a descriptor of the requested plugin type. */ +const LADSPA_Descriptor * +ladspa_descriptor(unsigned long Index) { + if (Index == 0) + return g_psDescriptor; + else + return NULL; +} + +/*****************************************************************************/ + +/* EOF */ diff --git a/src/plugins/sine.cpp b/src/plugins/sine.cpp new file mode 100644 index 0000000..c3d1a6e --- /dev/null +++ b/src/plugins/sine.cpp @@ -0,0 +1,420 @@ +/* sine.cpp + + Free software by Richard W.E. Furse. Do with as you will. No + warranty. + + This LADSPA plugin is written in C++ and provides a class that + implements a sine oscillator using a wavetable. Band-limiting to + avoid aliasing is trivial because of the waveform in use. Four + versions of the oscillator are provided, allowing the amplitude and + frequency inputs of the oscillator to be audio signals rather than + controls (for use in AM and FM synthesis). + + This file has poor memory protection. Failures during new() will + not recover nicely. */ + +/*****************************************************************************/ + +#include +#include +#include + +/*****************************************************************************/ + +#include "ladspa.h" + +/*****************************************************************************/ + +/* The port numbers for the plugin: */ + +#define OSC_FREQUENCY 0 +#define OSC_AMPLITUDE 1 +#define OSC_OUTPUT 2 + +/*****************************************************************************/ + +/* Sine table size is given by (1<= 0 && fFrequency < m_fLimitFrequency) + m_lPhaseStep = (unsigned long)(m_fPhaseStepScalar * fFrequency); + else + m_lPhaseStep = 0; + m_fCachedFrequency = fFrequency; + } + } + + friend LADSPA_Handle instantiateSineOscillator(const LADSPA_Descriptor *, + unsigned long SampleRate); + friend void connectPortToSineOscillator(LADSPA_Handle Instance, + unsigned long Port, + LADSPA_Data * DataLocation); + friend void activateSineOscillator(void * pvHandle); + friend void runSineOscillator_FreqAudio_AmpAudio(LADSPA_Handle Instance, + unsigned long SampleCount); + friend void runSineOscillator_FreqAudio_AmpCtrl(LADSPA_Handle Instance, + unsigned long SampleCount); + friend void runSineOscillator_FreqCtrl_AmpAudio(LADSPA_Handle Instance, + unsigned long SampleCount); + friend void runSineOscillator_FreqCtrl_AmpCtrl(LADSPA_Handle Instance, + unsigned long SampleCount); + friend void cleanupSineOscillator(void *pvHandle); + +}; + +/*****************************************************************************/ + +LADSPA_Handle +instantiateSineOscillator(const LADSPA_Descriptor *, + unsigned long SampleRate) { + return new SineOscillator(SampleRate); +} + +/*****************************************************************************/ + +void +connectPortToSineOscillator(LADSPA_Handle Instance, + unsigned long Port, + LADSPA_Data * DataLocation) { + switch (Port) { + case OSC_FREQUENCY: + ((SineOscillator *)Instance)->m_pfFrequency = DataLocation; + break; + case OSC_AMPLITUDE: + ((SineOscillator *)Instance)->m_pfAmplitude = DataLocation; + break; + case OSC_OUTPUT: + ((SineOscillator *)Instance)->m_pfOutput = DataLocation; + break; + } +} + +/*****************************************************************************/ + +void +activateSineOscillator(void * pvHandle) { + ((SineOscillator *)pvHandle)->m_lPhase = 0; +} + +/*****************************************************************************/ + +void +runSineOscillator_FreqAudio_AmpAudio(LADSPA_Handle Instance, + unsigned long SampleCount) { + SineOscillator * poSineOscillator = (SineOscillator *)Instance; + for (unsigned long lIndex = 0; lIndex < SampleCount; lIndex++) { + /* Extract frequency at this point to guarantee inplace + support. */ + LADSPA_Data fFrequency + = (poSineOscillator->m_pfFrequency[lIndex]); + poSineOscillator->m_pfOutput[lIndex] + = (g_pfSineTable[poSineOscillator->m_lPhase >> SINE_TABLE_SHIFT] + * poSineOscillator->m_pfAmplitude[lIndex]); + poSineOscillator->setPhaseStepFromFrequency(fFrequency); + poSineOscillator->m_lPhase + += poSineOscillator->m_lPhaseStep; + } +} + +/*****************************************************************************/ + +void +runSineOscillator_FreqAudio_AmpCtrl(LADSPA_Handle Instance, + unsigned long SampleCount) { + SineOscillator * poSineOscillator = (SineOscillator *)Instance; + LADSPA_Data fAmplitude = *(poSineOscillator->m_pfAmplitude); + for (unsigned long lIndex = 0; lIndex < SampleCount; lIndex++) { + /* Extract frequency at this point to guarantee inplace + support. */ + LADSPA_Data fFrequency + = (poSineOscillator->m_pfFrequency[lIndex]); + poSineOscillator->m_pfOutput[lIndex] + = (g_pfSineTable[poSineOscillator->m_lPhase >> SINE_TABLE_SHIFT] + * fAmplitude); + poSineOscillator->setPhaseStepFromFrequency(fFrequency); + poSineOscillator->m_lPhase + += poSineOscillator->m_lPhaseStep; + } +} + +/*****************************************************************************/ + +void +runSineOscillator_FreqCtrl_AmpAudio(LADSPA_Handle Instance, + unsigned long SampleCount) { + SineOscillator * poSineOscillator = (SineOscillator *)Instance; + poSineOscillator->setPhaseStepFromFrequency + (*(poSineOscillator->m_pfFrequency)); + for (unsigned long lIndex = 0; lIndex < SampleCount; lIndex++) { + poSineOscillator->m_pfOutput[lIndex] + = (g_pfSineTable[poSineOscillator->m_lPhase >> SINE_TABLE_SHIFT] + * poSineOscillator->m_pfAmplitude[lIndex]); + poSineOscillator->m_lPhase + += poSineOscillator->m_lPhaseStep; + } +} + +/*****************************************************************************/ + +void +runSineOscillator_FreqCtrl_AmpCtrl(LADSPA_Handle Instance, + unsigned long SampleCount) { + SineOscillator * poSineOscillator = (SineOscillator *)Instance; + LADSPA_Data fAmplitude = *(poSineOscillator->m_pfAmplitude); + poSineOscillator->setPhaseStepFromFrequency + (*(poSineOscillator->m_pfFrequency)); + for (unsigned long lIndex = 0; lIndex < SampleCount; lIndex++) { + poSineOscillator->m_pfOutput[lIndex] + = (g_pfSineTable[poSineOscillator->m_lPhase >> SINE_TABLE_SHIFT] + * fAmplitude); + poSineOscillator->m_lPhase + += poSineOscillator->m_lPhaseStep; + } +} + +/*****************************************************************************/ + +void +cleanupSineOscillator(void *pvHandle) { + delete (SineOscillator *)pvHandle; +} + +/*****************************************************************************/ + +typedef char * char_ptr; + +LADSPA_Descriptor * g_psDescriptors[4] = { NULL, NULL, NULL, NULL }; + +/*****************************************************************************/ + +/* Global object used handle startup initialisation and shut down + tidying. Performs the function of the _init() and _fini() calls in + the C modules. */ +class StartupShutdownHandler { +public: + + StartupShutdownHandler() { + + char ** pcPortNames; + LADSPA_PortDescriptor * piPortDescriptors; + LADSPA_PortRangeHint * psPortRangeHints; + + initialise_sine_table(); + + for (long lPluginIndex = 0; lPluginIndex < 4; lPluginIndex++) { + + g_psDescriptors[lPluginIndex] = new LADSPA_Descriptor; + if (g_psDescriptors[lPluginIndex] == NULL) + break; + + g_psDescriptors[lPluginIndex]->UniqueID + = 1044 + lPluginIndex; /* 1044-1047. */ + g_psDescriptors[lPluginIndex]->Properties + = LADSPA_PROPERTY_HARD_RT_CAPABLE; + g_psDescriptors[lPluginIndex]->Maker + = localStrdup("Richard Furse (LADSPA example plugins)"); + g_psDescriptors[lPluginIndex]->Copyright + = localStrdup("None"); + g_psDescriptors[lPluginIndex]->PortCount + = 3; + piPortDescriptors + = new LADSPA_PortDescriptor[3]; + g_psDescriptors[lPluginIndex]->PortDescriptors + = (const LADSPA_PortDescriptor *)piPortDescriptors; + piPortDescriptors[OSC_OUTPUT] + = LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO; + pcPortNames + = new char_ptr[3]; + g_psDescriptors[lPluginIndex]->PortNames + = (const char **)pcPortNames; + pcPortNames[OSC_FREQUENCY] + = localStrdup("Frequency (Hz)"); + pcPortNames[OSC_AMPLITUDE] + = localStrdup("Amplitude"); + pcPortNames[OSC_OUTPUT] + = localStrdup("Output"); + psPortRangeHints + = new LADSPA_PortRangeHint[3]; + g_psDescriptors[lPluginIndex]->PortRangeHints + = (const LADSPA_PortRangeHint *)psPortRangeHints; + psPortRangeHints[OSC_FREQUENCY].HintDescriptor + = (LADSPA_HINT_BOUNDED_BELOW + | LADSPA_HINT_BOUNDED_ABOVE + | LADSPA_HINT_SAMPLE_RATE + | LADSPA_HINT_LOGARITHMIC + | LADSPA_HINT_DEFAULT_440); + psPortRangeHints[OSC_FREQUENCY].LowerBound + = 0; + psPortRangeHints[OSC_FREQUENCY].UpperBound + = 0.5; + psPortRangeHints[OSC_AMPLITUDE].HintDescriptor + = (LADSPA_HINT_BOUNDED_BELOW + | LADSPA_HINT_LOGARITHMIC + | LADSPA_HINT_DEFAULT_1); + psPortRangeHints[OSC_AMPLITUDE].LowerBound + = 0; + psPortRangeHints[OSC_OUTPUT].HintDescriptor + = 0; + g_psDescriptors[lPluginIndex]->instantiate + = instantiateSineOscillator; + g_psDescriptors[lPluginIndex]->connect_port + = connectPortToSineOscillator; + g_psDescriptors[lPluginIndex]->activate + = activateSineOscillator; + g_psDescriptors[lPluginIndex]->run_adding + = NULL; + g_psDescriptors[lPluginIndex]->set_run_adding_gain + = NULL; + g_psDescriptors[lPluginIndex]->deactivate + = NULL; + g_psDescriptors[lPluginIndex]->cleanup + = cleanupSineOscillator; + + switch (lPluginIndex) { + case 0: + g_psDescriptors[lPluginIndex]->Label + = localStrdup("sine_faaa"); + g_psDescriptors[lPluginIndex]->Name + = localStrdup("Sine Oscillator (Freq:audio, Amp:audio)"); + piPortDescriptors[OSC_FREQUENCY] + = LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO; + piPortDescriptors[OSC_AMPLITUDE] + = LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO; + g_psDescriptors[lPluginIndex]->run + = runSineOscillator_FreqAudio_AmpAudio; + break; + case 1: + g_psDescriptors[lPluginIndex]->Label + = localStrdup("sine_faac"); + g_psDescriptors[lPluginIndex]->Name + = localStrdup("Sine Oscillator (Freq:audio, Amp:control)"); + piPortDescriptors[OSC_FREQUENCY] + = LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO; + piPortDescriptors[OSC_AMPLITUDE] + = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL; + g_psDescriptors[lPluginIndex]->run + = runSineOscillator_FreqAudio_AmpCtrl; + break; + case 2: + g_psDescriptors[lPluginIndex]->Label + = localStrdup("sine_fcaa"); + g_psDescriptors[lPluginIndex]->Name + = localStrdup("Sine Oscillator (Freq:control, Amp:audio)"); + piPortDescriptors[OSC_FREQUENCY] + = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL; + piPortDescriptors[OSC_AMPLITUDE] + = LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO; + g_psDescriptors[lPluginIndex]->run + = runSineOscillator_FreqCtrl_AmpAudio; + break; + case 3: + g_psDescriptors[lPluginIndex]->Label + = localStrdup("sine_fcac"); + g_psDescriptors[lPluginIndex]->Name + = localStrdup("Sine Oscillator (Freq:control, Amp:control)"); + piPortDescriptors[OSC_FREQUENCY] + = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL; + piPortDescriptors[OSC_AMPLITUDE] + = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL; + g_psDescriptors[lPluginIndex]->run + = runSineOscillator_FreqCtrl_AmpCtrl; + break; + } + } + } + + void deleteDescriptor(LADSPA_Descriptor * psDescriptor) { + unsigned long lIndex; + if (psDescriptor) { + delete [] psDescriptor->Label; + delete [] psDescriptor->Name; + delete [] psDescriptor->Maker; + delete [] psDescriptor->Copyright; + delete [] psDescriptor->PortDescriptors; + for (lIndex = 0; lIndex < psDescriptor->PortCount; lIndex++) + delete [] psDescriptor->PortNames[lIndex]; + delete [] psDescriptor->PortNames; + delete [] psDescriptor->PortRangeHints; + delete psDescriptor; + } + } + + ~StartupShutdownHandler() { + deleteDescriptor(g_psDescriptors[0]); + deleteDescriptor(g_psDescriptors[1]); + deleteDescriptor(g_psDescriptors[2]); + deleteDescriptor(g_psDescriptors[3]); + delete [] g_pfSineTable; + } + +} g_oShutdownStartupHandler; + +/*****************************************************************************/ + +const LADSPA_Descriptor * +ladspa_descriptor(unsigned long Index) { + if (Index < 4) + return g_psDescriptors[Index]; + else + return NULL; +} + +/*****************************************************************************/ + +/* EOF */ diff --git a/src/search.c b/src/search.c new file mode 100644 index 0000000..0006712 --- /dev/null +++ b/src/search.c @@ -0,0 +1,130 @@ +/* search.c + + Free software by Richard W.E. Furse. Do with as you will. No + warranty. */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +/*****************************************************************************/ + +#include "ladspa.h" +#include "utils.h" + +/*****************************************************************************/ + +/* Search just the one directory. */ +static void +LADSPADirectoryPluginSearch +(const char * pcDirectory, + LADSPAPluginSearchCallbackFunction fCallbackFunction) { + + char * pcFilename; + DIR * psDirectory; + LADSPA_Descriptor_Function fDescriptorFunction; + long lDirLength; + long iNeedSlash; + struct dirent * psDirectoryEntry; + void * pvPluginHandle; + + lDirLength = strlen(pcDirectory); + if (!lDirLength) + return; + if (pcDirectory[lDirLength - 1] == '/') + iNeedSlash = 0; + else + iNeedSlash = 1; + + psDirectory = opendir(pcDirectory); + if (!psDirectory) + return; + + while (1) { + + psDirectoryEntry = readdir(psDirectory); + if (!psDirectoryEntry) { + closedir(psDirectory); + return; + } + + pcFilename = malloc(lDirLength + + strlen(psDirectoryEntry->d_name) + + 1 + iNeedSlash); + strcpy(pcFilename, pcDirectory); + if (iNeedSlash) + strcat(pcFilename, "/"); + strcat(pcFilename, psDirectoryEntry->d_name); + + pvPluginHandle = dlopen(pcFilename, RTLD_LAZY); + if (pvPluginHandle) { + /* This is a file and the file is a shared library! */ + + dlerror(); + fDescriptorFunction + = (LADSPA_Descriptor_Function)dlsym(pvPluginHandle, + "ladspa_descriptor"); + if (dlerror() == NULL && fDescriptorFunction) { + /* We've successfully found a ladspa_descriptor function. Pass + it to the callback function. */ + fCallbackFunction(pcFilename, + pvPluginHandle, + fDescriptorFunction); + free(pcFilename); + } + else { + /* It was a library, but not a LADSPA one. Unload it. */ + dlclose(pcFilename); + free(pcFilename); + } + } + } +} + +/*****************************************************************************/ + +void +LADSPAPluginSearch(LADSPAPluginSearchCallbackFunction fCallbackFunction) { + + char * pcBuffer; + const char * pcEnd; + const char * pcLADSPAPath; + const char * pcStart; + + pcLADSPAPath = getenv("LADSPA_PATH"); + if (!pcLADSPAPath) { + fprintf(stderr, + "Warning: You do not have a LADSPA_PATH " + "environment variable set.\n"); + return; + } + + pcStart = pcLADSPAPath; + while (*pcStart != '\0') { + pcEnd = pcStart; + while (*pcEnd != ':' && *pcEnd != '\0') + pcEnd++; + + pcBuffer = malloc(1 + pcEnd - pcStart); + if (pcEnd > pcStart) + strncpy(pcBuffer, pcStart, pcEnd - pcStart); + pcBuffer[pcEnd - pcStart] = '\0'; + + LADSPADirectoryPluginSearch(pcBuffer, fCallbackFunction); + free(pcBuffer); + + pcStart = pcEnd; + if (*pcStart == ':') + pcStart++; + } +} + +/*****************************************************************************/ + +/* EOF */ diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 0000000..d94c420 --- /dev/null +++ b/src/utils.h @@ -0,0 +1,72 @@ +/* utils.h + + Free software by Richard W.E. Furse. Do with as you will. No + warranty. */ + +#ifndef LADSPA_SDK_LOAD_PLUGIN_LIB +#define LADSPA_SDK_LOAD_PLUGIN_LIB + +/*****************************************************************************/ + +#include "ladspa.h" + +/*****************************************************************************/ + +/* Functions in load.c: */ + +/* This function call takes a plugin library filename, searches for + the library along the LADSPA_PATH, loads it with dlopen() and + returns a plugin handle for use with findPluginDescriptor() or + unloadLADSPAPluginLibrary(). Errors are handled by writing a + message to stderr and calling exit(1). It is alright (although + inefficient) to call this more than once for the same file. */ +void * loadLADSPAPluginLibrary(const char * pcPluginFilename); + +/* This function unloads a LADSPA plugin library. */ +void unloadLADSPAPluginLibrary(void * pvLADSPAPluginLibrary); + +/* This function locates a LADSPA plugin within a plugin library + loaded with loadLADSPAPluginLibrary(). Errors are handled by + writing a message to stderr and calling exit(1). Note that the + plugin library filename is only included to help provide + informative error messages. */ +const LADSPA_Descriptor * +findLADSPAPluginDescriptor(void * pvLADSPAPluginLibrary, + const char * pcPluginLibraryFilename, + const char * pcPluginLabel); + +/*****************************************************************************/ + +/* Functions in search.c: */ + +/* Callback function for use with LADSPAPluginSearch(). The callback + function passes the filename (full path), a plugin handle (dlopen() + style) and a LADSPA_DescriptorFunction (from which + LADSPA_Descriptors can be acquired). */ +typedef void LADSPAPluginSearchCallbackFunction +(const char * pcFullFilename, + void * pvPluginHandle, + LADSPA_Descriptor_Function fDescriptorFunction); + +/* Search through the $(LADSPA_PATH) (or a default path) for any + LADSPA plugin libraries. Each plugin library is tested using + dlopen() and dlsym(,"ladspa_descriptor"). After loading each + library, the callback function is called to process it. This + function leaves items passed to the callback function open. */ +void LADSPAPluginSearch(LADSPAPluginSearchCallbackFunction fCallbackFunction); + +/*****************************************************************************/ + +/* Function in default.c: */ + +/* Find the default value for a port. Return 0 if a default is found + and -1 if not. */ +int getLADSPADefault(const LADSPA_PortRangeHint * psPortRangeHint, + const unsigned long lSampleRate, + LADSPA_Data * pfResult); + +/*****************************************************************************/ + +#endif + +/* EOF */