/[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 2124 - (show annotations) (download)
Sat Sep 18 09:24:41 2010 UTC (13 years, 7 months ago) by schoenebeck
File size: 12695 byte(s)
* implemented support for internal LADSPA effects (work in progress)

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 };
71
72 ////////////////////////////////////////////////////////////////////////////
73 // class 'LadspaEffect'
74
75 LadspaEffect::LadspaEffect(EffectInfo* pInfo) throw (Exception) {
76 this->pInfo = dynamic_cast<LadspaEffectInfo*>(pInfo);
77 if (!this->pInfo)
78 throw Exception("Effect key does not represent a LADSPA effect");
79
80 // DynamicLibraryOpen() and DynamicLibraryClose() maintain a reference
81 // count, so its OK to open and close the respective DLL for each effect,
82 // even though some effects might share the same DLL.
83 hDLL = DynamicLibraryOpen(this->pInfo->dll);
84 if (!hDLL)
85 throw Exception("Could not open DLL '" + this->pInfo->dll + "' for LADSPA effect");
86
87 LADSPA_Descriptor_Function pDescriptorFunction = (LADSPA_Descriptor_Function)
88 DynamicLibraryGetSymbol(hDLL, "ladspa_descriptor");
89
90 if (!pDescriptorFunction)
91 throw Exception("'" + this->pInfo->dll + "' is not a LADSPA plugin library");
92
93 // search for requested effect in that LADSPA DLL
94 for (long lPluginIndex = 0; true; lPluginIndex++) {
95 pDescriptor = pDescriptorFunction(lPluginIndex);
96 if (!pDescriptor)
97 throw Exception(
98 "Effect '" + this->pInfo->label +
99 "' could not be found in LADSPA DLL '" + this->pInfo->dll + "'"
100 );
101
102 if (pDescriptor->Label == this->pInfo->label)
103 break; // found
104 }
105
106 // those will be set later in InitEffect()
107 hEffect = NULL;
108 pDevice = NULL;
109 }
110
111 LadspaEffect::~LadspaEffect() {
112 if (!hEffect) return;
113 if (pDescriptor->deactivate)
114 pDescriptor->deactivate(hEffect);
115 pDescriptor->cleanup(hEffect);
116 DynamicLibraryClose(hDLL);
117 }
118
119 void LadspaEffect::RenderAudio(uint Samples) {
120 // (re)assign audio input and audio output buffers
121 int iInputPort = 0;
122 int iOutputPort = 0;
123 for (int iPort = 0; iPort < pDescriptor->PortCount; iPort++) {
124 LADSPA_PortDescriptor pPortDescriptor = pDescriptor->PortDescriptors[iPort];
125 if (LADSPA_IS_PORT_AUDIO(pPortDescriptor)) {
126 if (LADSPA_IS_PORT_INPUT(pPortDescriptor)) {
127 pDescriptor->connect_port(hEffect, iPort, vInputChannels[iInputPort++]->Buffer());
128 } else if (LADSPA_IS_PORT_OUTPUT(pPortDescriptor)) {
129 pDescriptor->connect_port(hEffect, iPort, vOutputChannels[iOutputPort++]->Buffer());
130 }
131 }
132 }
133
134 // let the effect do its job
135 pDescriptor->run(hEffect, Samples);
136 }
137
138 void LadspaEffect::InitEffect(AudioOutputDevice* pDevice) throw (Exception) {
139 this->pDevice = pDevice;
140
141 const int iInChannels = _getPortCountByType(
142 pDescriptor,
143 LADSPA_PORT_AUDIO | LADSPA_PORT_INPUT
144 );
145 const int iOutChannels = _getPortCountByType(
146 pDescriptor,
147 LADSPA_PORT_AUDIO | LADSPA_PORT_OUTPUT
148 );
149 const int iInControls = _getPortCountByType(
150 pDescriptor,
151 LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT
152 );
153 const int iOutControls = _getPortCountByType(
154 pDescriptor,
155 LADSPA_PORT_CONTROL | LADSPA_PORT_OUTPUT
156 );
157
158 // now create the actual LADSPA effect instance ...
159 dmsg(1, ("Instantiating LADSPA effect '%s'.\n", pInfo->label.c_str()));
160 hEffect = pDescriptor->instantiate(pDescriptor, pDevice->SampleRate());
161 if (!hEffect)
162 throw Exception("Could not instantiate LADSPA effect '" + pInfo->label + "'");
163
164 // create audio input channels
165 vInputChannels.resize(iInChannels);
166 for (int i = 0; i < iInChannels; i++) {
167 vInputChannels[i] = new AudioChannel(i, pDevice->MaxSamplesPerCycle());
168 }
169
170 // create audio output channels
171 vOutputChannels.resize(iOutChannels);
172 for (int i = 0; i < iOutChannels; i++) {
173 vOutputChannels[i] = new AudioChannel(i, pDevice->MaxSamplesPerCycle());
174 }
175
176 // create and assign control input and control output variables (effect parameters)
177 vInputControls.resize(iInControls);
178 vOutputControls.resize(iOutControls);
179 int iInControl = 0;
180 int iOutControl = 0;
181 for (int iPort = 0; iPort < pDescriptor->PortCount; iPort++) {
182 LADSPA_PortDescriptor pPortDescriptor = pDescriptor->PortDescriptors[iPort];
183 if (LADSPA_IS_PORT_CONTROL(pPortDescriptor)) {
184 if (LADSPA_IS_PORT_INPUT(pPortDescriptor)) {
185 LadspaEffectControl* pEffectControl = new LadspaEffectControl();
186 vInputControls[iInControl++] = pEffectControl;
187 pDescriptor->connect_port(hEffect, iPort, &pEffectControl->Value());
188 const float lower = getLowerB(iPort);
189 const float upper = getUpperB(iPort);
190
191 // determine default value
192 float fDefault = 0.5f * lower + 0.5f * upper; // middle value by default
193 if (LADSPA_IS_HINT_HAS_DEFAULT(pPortDescriptor)) {
194 if (LADSPA_IS_HINT_DEFAULT_MINIMUM(pPortDescriptor)) {
195 fDefault = lower;
196 } else if (LADSPA_IS_HINT_DEFAULT_LOW(pPortDescriptor)) {
197 if (LADSPA_IS_HINT_LOGARITHMIC(pPortDescriptor)) {
198 fDefault = exp(log(lower) * 0.75 + log(upper) * 0.25);
199 } else {
200 fDefault = lower * 0.75 + upper * 0.25;
201 }
202 } else if (LADSPA_IS_HINT_DEFAULT_MIDDLE(pPortDescriptor)) {
203 if (LADSPA_IS_HINT_LOGARITHMIC(pPortDescriptor)) {
204 fDefault = exp(log(lower) * 0.5 + log(upper) * 0.5);
205 } else {
206 fDefault = 0.5f * lower + 0.5f * upper;
207 }
208 } else if (LADSPA_IS_HINT_DEFAULT_HIGH(pPortDescriptor)) {
209 if (LADSPA_IS_HINT_LOGARITHMIC(pPortDescriptor)) {
210 fDefault = exp(log(lower) * 0.25 + log(upper) * 0.75);
211 } else {
212 fDefault = lower * 0.25 + upper * 0.75;
213 }
214 } else if (LADSPA_IS_HINT_DEFAULT_MAXIMUM(pPortDescriptor)) {
215 fDefault = upper;
216 } else if (LADSPA_IS_HINT_DEFAULT_0(pPortDescriptor)) {
217 fDefault = 0.0f;
218 } else if (LADSPA_IS_HINT_DEFAULT_1(pPortDescriptor)) {
219 fDefault = 1.0f;
220 } else if (LADSPA_IS_HINT_DEFAULT_100(pPortDescriptor)) {
221 fDefault = 100.0f;
222 } else if (LADSPA_IS_HINT_DEFAULT_440(pPortDescriptor)) {
223 fDefault = 440.0f;
224 }
225
226 pEffectControl->SetDefaultValue(fDefault);
227 }
228 pEffectControl->SetValue(fDefault);
229
230 // determine value range type
231 EffectControl::Type_t type;
232 if (LADSPA_IS_HINT_INTEGER(pPortDescriptor)) {
233 type = EffectControl::TYPE_INT;
234 } else if (LADSPA_IS_HINT_TOGGLED(pPortDescriptor)) {
235 type = EffectControl::TYPE_BOOL;
236 } else {
237 type = EffectControl::TYPE_FLOAT;
238 }
239 pEffectControl->SetType(type);
240
241 // is there a minimum value?
242 if (LADSPA_IS_HINT_BOUNDED_BELOW(pPortDescriptor)) {
243 pEffectControl->SetMinValue(lower);
244 }
245
246 // is there a maximum value?
247 if (LADSPA_IS_HINT_BOUNDED_ABOVE(pPortDescriptor)) {
248 pEffectControl->SetMaxValue(upper);
249 }
250
251 // retrieve human readable description about port
252 pEffectControl->SetDescription(pDescriptor->PortNames[iPort]);
253
254 } else if (LADSPA_IS_PORT_OUTPUT(pPortDescriptor)) {
255 LadspaEffectControl* pEffectControl = new LadspaEffectControl();
256 vOutputControls[iOutControl++] = pEffectControl;
257 pDescriptor->connect_port(hEffect, iPort, &pEffectControl->Value());
258 }
259 }
260 }
261
262 // call LADSPA effect's activate function (if there is one)
263 if (pDescriptor->activate != NULL)
264 pDescriptor->activate(hEffect);
265
266 dmsg(1, ("LADSPA effect '%s' activated.\n", pInfo->label.c_str()));
267 }
268
269 /// Returns lowest allowed value for this LADSPA control port.
270 float LadspaEffect::getLowerB(int iPort) const {
271 float low =
272 (pDescriptor->PortRangeHints[iPort].HintDescriptor & LADSPA_HINT_BOUNDED_BELOW)
273 ? pDescriptor->PortRangeHints[iPort].LowerBound : 0.0f;
274
275 if (pDescriptor->PortRangeHints[iPort].HintDescriptor & LADSPA_HINT_SAMPLE_RATE)
276 low *= float(pDevice->SampleRate());
277
278 return low;
279 }
280
281 /// Returns biggest allowed value for this LADSPA control port.
282 float LadspaEffect::getUpperB(int iPort) const {
283 float up =
284 (pDescriptor->PortRangeHints[iPort].HintDescriptor & LADSPA_HINT_BOUNDED_ABOVE)
285 ? pDescriptor->PortRangeHints[iPort].UpperBound : 1.0f;
286
287 if (pDescriptor->PortRangeHints[iPort].HintDescriptor & LADSPA_HINT_SAMPLE_RATE)
288 up *= float(pDevice->SampleRate());
289
290 return up;
291 }
292
293 static void _foundLadspaDll(String filename, void* hDLL, void* pFunction, void* pCustom) {
294 LADSPA_Descriptor_Function fDescriptorFunction = (LADSPA_Descriptor_Function) pFunction;
295 std::vector<EffectInfo*>* pV = (std::vector<EffectInfo*>*) pCustom;
296 const LADSPA_Descriptor* psDescriptor;
297 for (long lIndex = 0; (psDescriptor = fDescriptorFunction(lIndex)) != NULL; lIndex++) {
298 //printf("\t%s (%lu/%s)\n", psDescriptor->Name, psDescriptor->UniqueID, psDescriptor->Label);
299 LadspaEffectInfo* pInfo = new LadspaEffectInfo;
300 pInfo->name = psDescriptor->Name;
301 pInfo->label = psDescriptor->Label;
302 pInfo->dll = filename;
303 pV->push_back(pInfo);
304 }
305 DynamicLibraryClose(hDLL);
306 }
307
308 static String defaultLadspaDir() {
309 #if defined(WIN32)
310 //FIXME: hmm... who knows what the common default LADSPA path on Windows is?
311 const String sysDir = getenv("PROGRAMFILES");
312 return sysDir + "\ladspa";
313 #else
314 return "/usr/lib/ladspa";
315 #endif
316 }
317
318 std::vector<EffectInfo*> LadspaEffect::AvailableEffects() {
319 std::vector<EffectInfo*> v; // will be filled in callback function _foundLadspaDll()
320
321 char* pcLadspaPath = getenv("LADSPA_PATH");
322 String ladspaDir = (pcLadspaPath) ? pcLadspaPath : defaultLadspaDir();
323
324 try {
325 DynamicLibrariesSearch(
326 ladspaDir.c_str(), "ladspa_descriptor", _foundLadspaDll, &v
327 );
328 } catch (Exception e) {
329 std::cerr << "Could not scan LADSPA effects: " << e.Message()
330 << std::endl << std::flush;
331 } catch (...) {
332 std::cerr << "Could not scan LADSPA effects: unknown exception\n"
333 << std::flush;
334 }
335
336 return v;
337 }
338
339 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC