/[svn]/linuxsampler/trunk/src/effects/LadspaEffect.cpp
ViewVC logotype

Contents of /linuxsampler/trunk/src/effects/LadspaEffect.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2135 - (show annotations) (download)
Thu Sep 30 20:00:43 2010 UTC (13 years, 6 months ago) by schoenebeck
File size: 14336 byte(s)
* added and implemented a set of 19 new LSCP commands for controlling
  internal effects:
  - added LSCP command "GET AVAILABLE_EFFECTS"
  - added LSCP command "LIST AVAILABLE_EFFECTS"
  - added LSCP command "GET EFFECT INFO <effect-index>"
  - added LSCP command "CREATE EFFECT_INSTANCE <effect-index>"
  - added LSCP command
    "CREATE EFFECT_INSTANCE <effect-system> <module> <effect-name>"
  - added LSCP command "DESTROY EFFECT_INSTANCE <effect-instance>"
  - added LSCP command "GET EFFECT_INSTANCES"
  - added LSCP command "LIST EFFECT_INSTANCES"
  - added LSCP command "GET EFFECT_INSTANCE INFO <effect-instance>"
  - added LSCP command "GET EFFECT_INSTANCE_INPUT_CONTROL INFO
    <effect-instance> <input-control>"
  - added LSCP command "SET EFFECT_INSTANCE_INPUT_CONTROL <effect-instance>
    <input-control> <value>"
  - added LSCP command "GET MASTER_EFFECT_CHAINS <audio-device>"
  - added LSCP command "LIST MASTER_EFFECT_CHAINS <audio-device>"
  - added LSCP command "ADD MASTER_EFFECT_CHAIN <audio-device>"
  - added LSCP command
    "REMOVE MASTER_EFFECT_CHAIN <audio-device> <effect-chain>"
  - added LSCP command
    "GET MASTER_EFFECT_CHAIN INFO <audio-device> <effect-chain>"
  - added LSCP command "APPEND MASTER_EFFECT_CHAIN EFFECT <audio-device>
    <effect-chain> <effect-instance>"
  - added LSCP command "INSERT MASTER_EFFECT_CHAIN EFFECT <audio-device>
    <effect-chain> <effect-instance> <effect-chain-pos>"
  - added LSCP command "REMOVE MASTER_EFFECT_CHAIN EFFECT <audio-device>
    <effect-chain> <effect-instance>"
* bumped version to 1.0.0.cvs7

1 /*
2 Copyright (C) 2010 Christian Schoenebeck
3 */
4
5 #include "LadspaEffect.h"
6 #include "../common/DynamicLibraries.h"
7 #include "../common/global_private.h"
8 #include "../drivers/audio/AudioOutputDevice.h"
9 #include <math.h>
10
11 namespace LinuxSampler {
12
13 ////////////////////////////////////////////////////////////////////////////
14 // private helper functions
15
16 /// Returns total amount of ports for the given effect and port type.
17 static unsigned long _getPortCountByType(const LADSPA_Descriptor* psDescriptor, const LADSPA_PortDescriptor iType) {
18 unsigned long lCount = 0;
19 unsigned long lIndex;
20 for (lIndex = 0; lIndex < psDescriptor->PortCount; lIndex++)
21 if ((psDescriptor->PortDescriptors[lIndex] & iType) == iType)
22 lCount++;
23
24 return lCount;
25 }
26
27 ////////////////////////////////////////////////////////////////////////////
28 // class 'LadspaEffectInfo'
29
30 /**
31 * Identifier of exactly one LADSPA effect, used as unique key, e.g. for the
32 * respective LADSPA effect to be loaded.
33 */
34 class LadspaEffectInfo : public EffectInfo {
35 public:
36 String dll;
37 String label;
38 String name;
39
40 String EffectSystem() {
41 return "LADSPA";
42 }
43
44 String Name() {
45 return label;
46 }
47
48 String Module() {
49 return dll;
50 }
51
52 String Description() {
53 return name;
54 }
55 };
56
57 ////////////////////////////////////////////////////////////////////////////
58 // class 'LadspaEffectControl'
59
60 /**
61 * We just open access to protected members of EffectControl here.
62 */
63 class LadspaEffectControl : public EffectControl {
64 public:
65 EffectControl::SetDefaultValue;
66 EffectControl::SetMinValue;
67 EffectControl::SetMaxValue;
68 EffectControl::SetType;
69 EffectControl::SetDescription;
70 EffectControl::SetPossibilities;
71 };
72
73 ////////////////////////////////////////////////////////////////////////////
74 // class 'LadspaEffect'
75
76 LadspaEffect::LadspaEffect(EffectInfo* pInfo) throw (Exception) : Effect() {
77 this->pInfo = dynamic_cast<LadspaEffectInfo*>(pInfo);
78 if (!this->pInfo)
79 throw Exception("Effect key does not represent a LADSPA effect");
80
81 // DynamicLibraryOpen() and DynamicLibraryClose() maintain a reference
82 // count, so its OK to open and close the respective DLL for each effect,
83 // even though some effects might share the same DLL.
84 hDLL = DynamicLibraryOpen(this->pInfo->dll);
85 if (!hDLL)
86 throw Exception("Could not open DLL '" + this->pInfo->dll + "' for LADSPA effect");
87
88 LADSPA_Descriptor_Function pDescriptorFunction = (LADSPA_Descriptor_Function)
89 DynamicLibraryGetSymbol(hDLL, "ladspa_descriptor");
90
91 if (!pDescriptorFunction)
92 throw Exception("'" + this->pInfo->dll + "' is not a LADSPA plugin library");
93
94 // search for requested effect in that LADSPA DLL
95 for (long lPluginIndex = 0; true; lPluginIndex++) {
96 pDescriptor = pDescriptorFunction(lPluginIndex);
97 if (!pDescriptor)
98 throw Exception(
99 "Effect '" + this->pInfo->label +
100 "' could not be found in LADSPA DLL '" + this->pInfo->dll + "'"
101 );
102
103 if (pDescriptor->Label == this->pInfo->label)
104 break; // found
105 }
106
107 // those will be set later in InitEffect()
108 hEffect = NULL;
109 pDevice = NULL;
110
111 // create control input and control output variables (effect parameters)
112 // (they are going to be assigned to the actual LADSPA effect instance
113 // later in InitEffect() )
114 const int iInControls = _getPortCountByType(
115 pDescriptor,
116 LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT
117 );
118 const int iOutControls = _getPortCountByType(
119 pDescriptor,
120 LADSPA_PORT_CONTROL | LADSPA_PORT_OUTPUT
121 );
122 vInputControls.resize(iInControls);
123 vOutputControls.resize(iOutControls);
124 // create LadspaEffectControl instances and determine its informations
125 // (value range, description, default value)
126 int iInControl = 0;
127 int iOutControl = 0;
128 for (int iPort = 0; iPort < pDescriptor->PortCount; iPort++) {
129 LADSPA_PortDescriptor pPortDescriptor = pDescriptor->PortDescriptors[iPort];
130 if (LADSPA_IS_PORT_CONTROL(pPortDescriptor)) {
131 if (LADSPA_IS_PORT_INPUT(pPortDescriptor)) {
132 LadspaEffectControl* pEffectControl = new LadspaEffectControl();
133 vInputControls[iInControl++] = pEffectControl;
134
135 const float lower = getLowerB(iPort);
136 const float upper = getUpperB(iPort);
137
138 // determine default value
139 float fDefault = 0.5f * lower + 0.5f * upper; // middle value by default
140 if (LADSPA_IS_HINT_HAS_DEFAULT(pPortDescriptor)) {
141 if (LADSPA_IS_HINT_DEFAULT_MINIMUM(pPortDescriptor)) {
142 fDefault = lower;
143 } else if (LADSPA_IS_HINT_DEFAULT_LOW(pPortDescriptor)) {
144 if (LADSPA_IS_HINT_LOGARITHMIC(pPortDescriptor)) {
145 fDefault = exp(log(lower) * 0.75 + log(upper) * 0.25);
146 } else {
147 fDefault = lower * 0.75 + upper * 0.25;
148 }
149 } else if (LADSPA_IS_HINT_DEFAULT_MIDDLE(pPortDescriptor)) {
150 if (LADSPA_IS_HINT_LOGARITHMIC(pPortDescriptor)) {
151 fDefault = exp(log(lower) * 0.5 + log(upper) * 0.5);
152 } else {
153 fDefault = 0.5f * lower + 0.5f * upper;
154 }
155 } else if (LADSPA_IS_HINT_DEFAULT_HIGH(pPortDescriptor)) {
156 if (LADSPA_IS_HINT_LOGARITHMIC(pPortDescriptor)) {
157 fDefault = exp(log(lower) * 0.25 + log(upper) * 0.75);
158 } else {
159 fDefault = lower * 0.25 + upper * 0.75;
160 }
161 } else if (LADSPA_IS_HINT_DEFAULT_MAXIMUM(pPortDescriptor)) {
162 fDefault = upper;
163 } else if (LADSPA_IS_HINT_DEFAULT_0(pPortDescriptor)) {
164 fDefault = 0.0f;
165 } else if (LADSPA_IS_HINT_DEFAULT_1(pPortDescriptor)) {
166 fDefault = 1.0f;
167 } else if (LADSPA_IS_HINT_DEFAULT_100(pPortDescriptor)) {
168 fDefault = 100.0f;
169 } else if (LADSPA_IS_HINT_DEFAULT_440(pPortDescriptor)) {
170 fDefault = 440.0f;
171 }
172
173 pEffectControl->SetDefaultValue(fDefault);
174 }
175 pEffectControl->SetValue(fDefault);
176
177 // determine value range type
178 EffectControl::Type_t type;
179 if (LADSPA_IS_HINT_INTEGER(pPortDescriptor)) {
180 type = EffectControl::TYPE_INT;
181 } else if (LADSPA_IS_HINT_TOGGLED(pPortDescriptor)) {
182 type = EffectControl::TYPE_BOOL;
183 } else {
184 type = EffectControl::TYPE_FLOAT;
185 }
186 pEffectControl->SetType(type);
187
188 // is there a minimum value?
189 if (LADSPA_IS_HINT_BOUNDED_BELOW(pPortDescriptor)) {
190 pEffectControl->SetMinValue(lower);
191 }
192
193 // is there a maximum value?
194 if (LADSPA_IS_HINT_BOUNDED_ABOVE(pPortDescriptor)) {
195 pEffectControl->SetMaxValue(upper);
196 }
197
198 // boolean type?
199 if (LADSPA_IS_HINT_TOGGLED(pPortDescriptor)) {
200 std::vector<float> vPossibilities;
201 vPossibilities.push_back(0.0f);
202 vPossibilities.push_back(1.0f);
203 pEffectControl->SetPossibilities(vPossibilities);
204 }
205
206 // retrieve human readable description about port
207 pEffectControl->SetDescription(pDescriptor->PortNames[iPort]);
208
209 } else if (LADSPA_IS_PORT_OUTPUT(pPortDescriptor)) {
210 LadspaEffectControl* pEffectControl = new LadspaEffectControl();
211 vOutputControls[iOutControl++] = pEffectControl;
212
213 //TODO: init output controls like input controls above
214 }
215 }
216 }
217 }
218
219 LadspaEffect::~LadspaEffect() {
220 if (!hEffect) return;
221 if (pDescriptor->deactivate)
222 pDescriptor->deactivate(hEffect);
223 pDescriptor->cleanup(hEffect);
224 DynamicLibraryClose(hDLL);
225 }
226
227 EffectInfo* LadspaEffect::GetEffectInfo() {
228 return pInfo;
229 }
230
231 void LadspaEffect::RenderAudio(uint Samples) {
232 // (re)assign audio input and audio output buffers
233 int iInputPort = 0;
234 int iOutputPort = 0;
235 for (int iPort = 0; iPort < pDescriptor->PortCount; iPort++) {
236 LADSPA_PortDescriptor pPortDescriptor = pDescriptor->PortDescriptors[iPort];
237 if (LADSPA_IS_PORT_AUDIO(pPortDescriptor)) {
238 if (LADSPA_IS_PORT_INPUT(pPortDescriptor)) {
239 pDescriptor->connect_port(hEffect, iPort, vInputChannels[iInputPort++]->Buffer());
240 } else if (LADSPA_IS_PORT_OUTPUT(pPortDescriptor)) {
241 pDescriptor->connect_port(hEffect, iPort, vOutputChannels[iOutputPort++]->Buffer());
242 }
243 }
244 }
245
246 // let the effect do its job
247 pDescriptor->run(hEffect, Samples);
248 }
249
250 void LadspaEffect::InitEffect(AudioOutputDevice* pDevice) throw (Exception) {
251 this->pDevice = pDevice;
252
253 const int iInChannels = _getPortCountByType(
254 pDescriptor,
255 LADSPA_PORT_AUDIO | LADSPA_PORT_INPUT
256 );
257 const int iOutChannels = _getPortCountByType(
258 pDescriptor,
259 LADSPA_PORT_AUDIO | LADSPA_PORT_OUTPUT
260 );
261 const int iInControls = vInputControls.size();
262 const int iOutControls = vOutputControls.size();
263
264 // now create the actual LADSPA effect instance ...
265 dmsg(1, ("Instantiating LADSPA effect '%s'.\n", pInfo->label.c_str()));
266 hEffect = pDescriptor->instantiate(pDescriptor, pDevice->SampleRate());
267 if (!hEffect)
268 throw Exception("Could not instantiate LADSPA effect '" + pInfo->label + "'");
269
270 // create audio input channels
271 vInputChannels.resize(iInChannels);
272 for (int i = 0; i < iInChannels; i++) {
273 vInputChannels[i] = new AudioChannel(i, pDevice->MaxSamplesPerCycle());
274 }
275
276 // create audio output channels
277 vOutputChannels.resize(iOutChannels);
278 for (int i = 0; i < iOutChannels; i++) {
279 vOutputChannels[i] = new AudioChannel(i, pDevice->MaxSamplesPerCycle());
280 }
281
282 // assign (already created and initialized) control input and control
283 // output variables (effect parameters)
284 int iInControl = 0;
285 int iOutControl = 0;
286 for (int iPort = 0; iPort < pDescriptor->PortCount; iPort++) {
287 LADSPA_PortDescriptor pPortDescriptor = pDescriptor->PortDescriptors[iPort];
288 if (LADSPA_IS_PORT_CONTROL(pPortDescriptor)) {
289 if (LADSPA_IS_PORT_INPUT(pPortDescriptor)) {
290 LadspaEffectControl* pEffectControl =
291 (LadspaEffectControl*) vInputControls[iInControl++];
292 pDescriptor->connect_port(hEffect, iPort, &pEffectControl->Value());
293 } else if (LADSPA_IS_PORT_OUTPUT(pPortDescriptor)) {
294 LadspaEffectControl* pEffectControl =
295 (LadspaEffectControl*) vOutputControls[iOutControl++];
296 pDescriptor->connect_port(hEffect, iPort, &pEffectControl->Value());
297 }
298 }
299 }
300
301 // call LADSPA effect's activate function (if there is one)
302 if (pDescriptor->activate != NULL)
303 pDescriptor->activate(hEffect);
304
305 dmsg(1, ("LADSPA effect '%s' activated.\n", pInfo->label.c_str()));
306 }
307
308 /// Returns lowest allowed value for this LADSPA control port.
309 float LadspaEffect::getLowerB(int iPort) const {
310 float low =
311 (pDescriptor->PortRangeHints[iPort].HintDescriptor & LADSPA_HINT_BOUNDED_BELOW)
312 ? pDescriptor->PortRangeHints[iPort].LowerBound : 0.0f;
313
314 if (pDescriptor->PortRangeHints[iPort].HintDescriptor & LADSPA_HINT_SAMPLE_RATE)
315 low *= float(pDevice->SampleRate());
316
317 return low;
318 }
319
320 /// Returns biggest allowed value for this LADSPA control port.
321 float LadspaEffect::getUpperB(int iPort) const {
322 float up =
323 (pDescriptor->PortRangeHints[iPort].HintDescriptor & LADSPA_HINT_BOUNDED_ABOVE)
324 ? pDescriptor->PortRangeHints[iPort].UpperBound : 1.0f;
325
326 if (pDescriptor->PortRangeHints[iPort].HintDescriptor & LADSPA_HINT_SAMPLE_RATE)
327 up *= float(pDevice->SampleRate());
328
329 return up;
330 }
331
332 static void _foundLadspaDll(String filename, void* hDLL, void* pFunction, void* pCustom) {
333 LADSPA_Descriptor_Function fDescriptorFunction = (LADSPA_Descriptor_Function) pFunction;
334 std::vector<EffectInfo*>* pV = (std::vector<EffectInfo*>*) pCustom;
335 const LADSPA_Descriptor* psDescriptor;
336 for (long lIndex = 0; (psDescriptor = fDescriptorFunction(lIndex)) != NULL; lIndex++) {
337 //printf("\t%s (%lu/%s)\n", psDescriptor->Name, psDescriptor->UniqueID, psDescriptor->Label);
338 LadspaEffectInfo* pInfo = new LadspaEffectInfo;
339 pInfo->name = psDescriptor->Name;
340 pInfo->label = psDescriptor->Label;
341 pInfo->dll = filename;
342 pV->push_back(pInfo);
343 }
344 DynamicLibraryClose(hDLL);
345 }
346
347 static String defaultLadspaDir() {
348 #if defined(WIN32)
349 //FIXME: hmm... who knows what the common default LADSPA path on Windows is?
350 const String sysDir = getenv("PROGRAMFILES");
351 return sysDir + "\ladspa";
352 #else
353 return "/usr/lib/ladspa";
354 #endif
355 }
356
357 std::vector<EffectInfo*> LadspaEffect::AvailableEffects() {
358 std::vector<EffectInfo*> v; // will be filled in callback function _foundLadspaDll()
359
360 char* pcLadspaPath = getenv("LADSPA_PATH");
361 String ladspaDir = (pcLadspaPath) ? pcLadspaPath : defaultLadspaDir();
362
363 try {
364 DynamicLibrariesSearch(
365 ladspaDir.c_str(), "ladspa_descriptor", _foundLadspaDll, &v
366 );
367 } catch (Exception e) {
368 std::cerr << "Could not scan LADSPA effects: " << e.Message()
369 << std::endl << std::flush;
370 } catch (...) {
371 std::cerr << "Could not scan LADSPA effects: unknown exception\n"
372 << std::flush;
373 }
374
375 return v;
376 }
377
378 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC