hydrogen 1.2.6
LadspaFx.cpp
Go to the documentation of this file.
1/*
2 * Hydrogen
3 * Copyright(c) 2002-2008 by Alex >Comix< Cominu [comix@users.sourceforge.net]
4 * Copyright(c) 2008-2025 The hydrogen development team [hydrogen-devel@lists.sourceforge.net]
5 *
6 * http://www.hydrogen-music.org
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY, without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see https://www.gnu.org/licenses
20 *
21 */
22
23#include <core/FX/LadspaFX.h>
24
25#if defined(H2CORE_HAVE_LADSPA) || _DOXYGEN_
27#include <core/Hydrogen.h>
28#include <core/Basics/Song.h>
29
30#include <QDir>
31
32#define LADSPA_IS_CONTROL_INPUT(x) (LADSPA_IS_PORT_INPUT(x) && LADSPA_IS_PORT_CONTROL(x))
33#define LADSPA_IS_AUDIO_INPUT(x) (LADSPA_IS_PORT_INPUT(x) && LADSPA_IS_PORT_AUDIO(x))
34#define LADSPA_IS_CONTROL_OUTPUT(x) (LADSPA_IS_PORT_OUTPUT(x) && LADSPA_IS_PORT_CONTROL(x))
35#define LADSPA_IS_AUDIO_OUTPUT(x) (LADSPA_IS_PORT_OUTPUT(x) && LADSPA_IS_PORT_AUDIO(x))
36
37namespace H2Core
38{
39
40LadspaFXGroup::LadspaFXGroup( const QString& sName )
41{
42// infoLog( "INIT - " + sName );
43 m_sName = sName;
44}
45
46
48{
49// infoLog( "DESTROY - " + m_sName );
50
51 for ( int i = 0; i < ( int )m_childGroups.size(); ++i ) {
52 delete m_childGroups[ i ];
53 }
54}
55
56
57
59 m_childGroups.clear();
60 m_ladspaList.clear();
62}
63
65{
66 m_ladspaList.push_back( pInfo );
68}
69
70
72{
73 m_childGroups.push_back( pChild );
75}
76
78{
79 return ( a->getName() < b->getName() );
80}
81
88
89
90
92
93
94LadspaFXInfo::LadspaFXInfo( const QString& sName )
95{
96// infoLog( "INIT - " + sName );
97 m_sFilename = "";
98 m_sLabel = "";
99 m_sName = sName;
100 m_nICPorts = 0;
101 m_nOCPorts = 0;
102 m_nIAPorts = 0;
103 m_nOAPorts = 0;
104}
105
106
108{
109// infoLog( "DESTROY " + m_sName );
110}
111
113{
114 return ( a->m_sName < b->m_sName );
115}
116
117
119
120
121// ctor
122LadspaFX::LadspaFX( const QString& sLibraryPath, const QString& sPluginLabel )
123//, m_nBufferSize( 0 )
124 : m_pBuffer_L( nullptr )
125 , m_pBuffer_R( nullptr )
127 , m_bEnabled( false )
128 , m_bActivated( false )
129 , m_sLabel( sPluginLabel )
130 , m_sLibraryPath( sLibraryPath )
131 , m_pLibrary( nullptr )
132 , m_d( nullptr )
133 , m_handle( nullptr )
134 , m_fVolume( 1.0f )
135 , m_nICPorts( 0 )
136 , m_nOCPorts( 0 )
137 , m_nIAPorts( 0 )
138 , m_nOAPorts( 0 )
139{
140 INFOLOG( QString( "INIT - %1 - %2" ).arg( sLibraryPath ).arg( sPluginLabel ) );
141
142
143 m_pBuffer_L = new float[MAX_BUFFER_SIZE];
144 m_pBuffer_R = new float[MAX_BUFFER_SIZE];
145
146
147 // Touch all the memory (is this really necessary?)
148 for ( unsigned i = 0; i < MAX_BUFFER_SIZE; ++i ) {
149 m_pBuffer_L[ i ] = 0;
150 m_pBuffer_R[ i ] = 0;
151 }
152
153}
154
155
156// dtor
158{
159 // dealloca il plugin
160 INFOLOG( QString( "DESTROY - %1 - %2" ).arg( m_sLibraryPath ).arg( m_sLabel ) );
161
162 if ( m_d ) {
163 /*
164 if ( m_d->deactivate ) {
165 if ( m_handle ) {
166 INFOLOG( "deactivate" );
167 m_d->deactivate( m_handle );
168 }
169 }*/
170 deactivate();
171
172 if ( m_d->cleanup ) {
173 if ( m_handle ) {
174 INFOLOG( "Cleanup" );
176 m_d->cleanup( m_handle );
177 }
178 }
179 }
180 delete m_pLibrary;
181
182 for ( unsigned i = 0; i < inputControlPorts.size(); i++ ) {
183 delete inputControlPorts[i];
184 }
185 for ( unsigned i = 0; i < outputControlPorts.size(); i++ ) {
186 delete outputControlPorts[i];
187 }
188
189 delete[] m_pBuffer_L;
190 delete[] m_pBuffer_R;
191}
192
193
194void LadspaFX::setPluginName( const QString& sName ) {
195 m_sName = sName;
196
197 if ( Hydrogen::get_instance()->getSong() != nullptr ) {
199 }
200}
201void LadspaFX::setEnabled( bool value ) {
202 m_bEnabled = value;
203
204 if ( Hydrogen::get_instance()->getSong() != nullptr ) {
206 }
207}
208
209
210// Static
211LadspaFX* LadspaFX::load( const QString& sLibraryPath, const QString& sPluginLabel, long nSampleRate )
212{
213 LadspaFX* pFX = new LadspaFX( sLibraryPath, sPluginLabel );
214
215 _INFOLOG( "INIT - " + sLibraryPath + " - " + sPluginLabel );
216
217 Logger::CrashContext ctx( QString( "Initialising LADSPA plugin " ) + sLibraryPath + " - " + sPluginLabel);
218
219 pFX->m_pLibrary = new QLibrary( sLibraryPath );
220 LADSPA_Descriptor_Function desc_func = ( LADSPA_Descriptor_Function )pFX->m_pLibrary->resolve( "ladspa_descriptor" );
221 if ( desc_func == nullptr ) {
222 _ERRORLOG( "Error loading the library. (" + sLibraryPath + ")" );
223 delete pFX;
224 return nullptr;
225 }
226 if ( desc_func ) {
227 for ( unsigned i = 0; ( pFX->m_d = desc_func( i ) ) != nullptr; i++ ) {
228 QString sName = QString::fromLocal8Bit(pFX->m_d->Name);
229 QString sLabel = QString::fromLocal8Bit(pFX->m_d->Label);
230
231 if ( sLabel != sPluginLabel ) {
232 continue;
233 }
234 pFX->setPluginName( sName );
235
236 for ( unsigned j = 0; j < pFX->m_d->PortCount; j++ ) {
237 LADSPA_PortDescriptor pd = pFX->m_d->PortDescriptors[j];
238 if ( LADSPA_IS_PORT_INPUT( pd ) && LADSPA_IS_PORT_CONTROL( pd ) ) {
239 pFX->m_nICPorts++;
240 } else if ( LADSPA_IS_PORT_INPUT( pd ) && LADSPA_IS_PORT_AUDIO( pd ) ) {
241 pFX->m_nIAPorts++;
242 } else if ( LADSPA_IS_PORT_OUTPUT( pd ) && LADSPA_IS_PORT_CONTROL( pd ) ) {
243 pFX->m_nOCPorts++;
244 } else if ( LADSPA_IS_PORT_OUTPUT( pd ) && LADSPA_IS_PORT_AUDIO( pd ) ) {
245 pFX->m_nOAPorts++;
246 } else {
247 _ERRORLOG( "Unknown port type" );
248 }
249 }
250 break;
251 }
252 } else {
253 _ERRORLOG( "Error in dlsym" );
254 delete pFX;
255 return nullptr;
256 }
257
258 if ( ( pFX->m_nIAPorts == 2 ) && ( pFX->m_nOAPorts == 2 ) ) { // Stereo plugin
259 pFX->m_pluginType = STEREO_FX;
260 } else if ( ( pFX->m_nIAPorts == 1 ) && ( pFX->m_nOAPorts == 1 ) ) { // Mono plugin
261 pFX->m_pluginType = MONO_FX;
262 } else {
263 _ERRORLOG( "Wrong number of ports" );
264 _ERRORLOG( QString( "in audio = %1" ).arg( pFX->m_nIAPorts ) );
265 _ERRORLOG( QString( "out audio = %1" ).arg( pFX->m_nOAPorts ) );
266 }
267
268 //pFX->infoLog( "[LadspaFX::load] instantiate " + pFX->getPluginName() );
269 pFX->m_handle = pFX->m_d->instantiate( pFX->m_d, nSampleRate );
270
271 for ( unsigned nPort = 0; nPort < pFX->m_d->PortCount; nPort++ ) {
272 LADSPA_PortDescriptor pd = pFX->m_d->PortDescriptors[ nPort ];
273
274 if ( LADSPA_IS_CONTROL_INPUT( pd ) ) {
275 QString sName = QString::fromLocal8Bit(pFX->m_d->PortNames[ nPort ]);
276 float fMin = 0.0;
277 float fMax = 0.0;
278 float fDefault = 0.0;
279 bool isToggle = false;
280 bool isInteger = false;
281
282 LADSPA_PortRangeHint rangeHints = pFX->m_d->PortRangeHints[ nPort ];
283 if ( LADSPA_IS_HINT_BOUNDED_BELOW( rangeHints.HintDescriptor ) ) {
284 fMin = ( pFX->m_d->PortRangeHints[ nPort ] ).LowerBound;
285 }
286 if ( LADSPA_IS_HINT_BOUNDED_ABOVE( rangeHints.HintDescriptor ) ) {
287 fMax = ( pFX->m_d->PortRangeHints[ nPort ] ).UpperBound;
288 }
289 if ( LADSPA_IS_HINT_TOGGLED( rangeHints.HintDescriptor ) ) {
290 isToggle = true;
291
292 // this way the fader will act like a toggle (0, 1)
293 isInteger = true;
294 fMin = 0.0;
295 fMax = 1.0;
296 }
297 if ( LADSPA_IS_HINT_SAMPLE_RATE( rangeHints.HintDescriptor ) ) {
298 _WARNINGLOG( "samplerate hint not implemented yet" );
299 }
300 if ( LADSPA_IS_HINT_LOGARITHMIC( rangeHints.HintDescriptor ) ) {
301 _WARNINGLOG( "logarithmic hint not implemented yet" );
302 }
303 if ( LADSPA_IS_HINT_INTEGER( rangeHints.HintDescriptor ) ) {
304 isInteger = true;
305 }
306 if ( LADSPA_IS_HINT_HAS_DEFAULT( rangeHints.HintDescriptor ) ) {
307 if ( LADSPA_IS_HINT_DEFAULT_MINIMUM( rangeHints.HintDescriptor ) ) {
308 fDefault = fMin;
309 }
310 if ( LADSPA_IS_HINT_DEFAULT_LOW( rangeHints.HintDescriptor ) ) {
311 // TODO: bisogna gestire diversamente se viene specificato di usare la scala logaritmica
312 fDefault = ( fMin * 0.75 + fMax * 0.25 );
313 }
314 if ( LADSPA_IS_HINT_DEFAULT_MIDDLE( rangeHints.HintDescriptor ) ) {
315 fDefault = ( fMax - fMin ) / 2.0;
316 }
317 if ( LADSPA_IS_HINT_DEFAULT_HIGH( rangeHints.HintDescriptor ) ) {
318 // TODO: bisogna gestire diversamente se viene specificato di usare la scala logaritmica
319 fDefault = ( fMin * 0.25 + fMax * 0.75 );
320 }
321 if ( LADSPA_IS_HINT_DEFAULT_MAXIMUM( rangeHints.HintDescriptor ) ) {
322 fDefault = fMax;
323 }
324 if ( LADSPA_IS_HINT_DEFAULT_0( rangeHints.HintDescriptor ) ) {
325 fDefault = 0.0;
326 }
327 if ( LADSPA_IS_HINT_DEFAULT_1( rangeHints.HintDescriptor ) ) {
328 fDefault = 1.0;
329 }
330 if ( LADSPA_IS_HINT_DEFAULT_100( rangeHints.HintDescriptor ) ) {
331 fDefault = 100.0;
332 }
333 if ( LADSPA_IS_HINT_DEFAULT_440( rangeHints.HintDescriptor ) ) {
334 fDefault = 440.0;
335 }
336 }
337
338 LadspaControlPort* pControl = new LadspaControlPort();
339 pControl->sName = sName;
340 pControl->fLowerBound = fMin;
341 pControl->fUpperBound = fMax;
342 pControl->fControlValue = fDefault;
343 pControl->fDefaultValue = fDefault;
344 pControl->isToggle = isToggle;
345 pControl->m_bIsInteger = isInteger;
346
347 _INFOLOG( QString( "Input control port\t[%1]\tmin=%2,\tmax=%3,\tcontrolValue=%4" ).arg( sName ).arg( fMin ).arg( fMax ).arg( pControl->fControlValue ) );
348
349 pFX->inputControlPorts.push_back( pControl );
350 pFX->m_d->connect_port( pFX->m_handle, nPort, &( pControl->fControlValue ) );
351 } else if ( LADSPA_IS_CONTROL_OUTPUT( pd ) ) {
352 QString sName = QString::fromLocal8Bit(pFX->m_d->PortNames[ nPort ]);
353 float fMin = 0.0;
354 float fMax = 0.0;
355 float fDefault = 0.0;
356
357 LADSPA_PortRangeHint rangeHints = pFX->m_d->PortRangeHints[ nPort ];
358 if ( LADSPA_IS_HINT_BOUNDED_BELOW( rangeHints.HintDescriptor ) ) {
359 fMin = ( pFX->m_d->PortRangeHints[ nPort ] ).LowerBound;
360 }
361 if ( LADSPA_IS_HINT_BOUNDED_ABOVE( rangeHints.HintDescriptor ) ) {
362 fMax = ( pFX->m_d->PortRangeHints[ nPort ] ).UpperBound;
363 }
364
365 /* LadspaControlPort* pControl = new LadspaControlPort();
366 pControl->sName = pFX->m_d->PortNames[ nPort ];
367 pControl->fLowerBound = ( pFX->m_d->PortRangeHints[ nPort ] ).LowerBound;
368 pControl->fUpperBound = ( pFX->m_d->PortRangeHints[ nPort ] ).UpperBound;
369 pControl->fControlValue = pControl->fUpperBound / 2.0;
370 */
371 // always middle
372 fDefault = ( fMax - fMin ) / 2.0;
373
374 LadspaControlPort* pControl = new LadspaControlPort();
375 pControl->sName = sName;
376 pControl->fLowerBound = fMin;
377 pControl->fUpperBound = fMax;
378 pControl->fControlValue = fDefault;
379 pControl->fDefaultValue = fDefault;
380 //pFX->infoLog( "[LadspaFX::load] Output control port\t[" + sName + "]\tmin=" + to_string(fMin) + ",\tmax=" + to_string(fMax) + ",\tcontrolValue=" + to_string(pControl->fControlValue) );
381
382 pFX->outputControlPorts.push_back( pControl );
383 pFX->m_d->connect_port( pFX->m_handle, nPort, &( pControl->fControlValue ) );
384 } else if ( LADSPA_IS_AUDIO_INPUT( pd ) ) {
385 } else if ( LADSPA_IS_AUDIO_OUTPUT( pd ) ) {
386 } else {
387 _ERRORLOG( "unknown port" );
388 }
389 }
390
391 if ( Hydrogen::get_instance()->getSong() != nullptr ) {
393 }
394 return pFX;
395}
396
397
398
399void LadspaFX::connectAudioPorts( float* pIn_L, float* pIn_R, float* pOut_L, float* pOut_R )
400{
401 INFOLOG( "[connectAudioPorts]" );
402 Logger::CrashContext ctx( QString( "Connecting ports on LADSPA plugin " ) + m_sLibraryPath + " - " + m_sLabel);
403 unsigned nAIConn = 0;
404 unsigned nAOConn = 0;
405 for ( unsigned nPort = 0; nPort < m_d->PortCount; nPort++ ) {
406 LADSPA_PortDescriptor pd = m_d->PortDescriptors[ nPort ];
407 if ( LADSPA_IS_CONTROL_INPUT( pd ) ) {
408 } else if ( LADSPA_IS_CONTROL_OUTPUT( pd ) ) {
409 } else if ( LADSPA_IS_AUDIO_INPUT( pd ) ) {
410 if ( nAIConn == 0 ) {
411 m_d->connect_port( m_handle, nPort, pIn_L );
412 //infoLog( "connect input port (L): " + string( m_d->PortNames[ nPort ] ) );
413 } else if ( nAIConn == 1 ) {
414 m_d->connect_port( m_handle, nPort, pIn_R );
415 //infoLog( "connect input port (R): " + string( m_d->PortNames[ nPort ] ) );
416 } else {
417 ERRORLOG( "too many input ports.." );
418 }
419 nAIConn++;
420 } else if ( LADSPA_IS_AUDIO_OUTPUT( pd ) ) {
421 if ( nAOConn == 0 ) {
422 m_d->connect_port( m_handle, nPort, pOut_L );
423 //infoLog( "connect output port (L): " + string( m_d->PortNames[ nPort ] ) );
424 } else if ( nAOConn == 1 ) {
425 m_d->connect_port( m_handle, nPort, pOut_R );
426 //infoLog( "connect output port (R): " + string( m_d->PortNames[ nPort ] ) );
427 } else {
428 ERRORLOG( "too many output ports.." );
429 }
430 nAOConn++;
431 } else {
432 ERRORLOG( "unknown port" );
433 }
434 }
435}
436
437
438
439void LadspaFX::processFX( unsigned nFrames )
440{
441// infoLog( "[LadspaFX::applyFX()]" );
442 if( m_bActivated ) {
444 m_d->run( m_handle, nFrames );
445 }
446}
447
449{
450 if ( m_d->activate ) {
451 INFOLOG( "activate " + getPluginName() );
452 m_bActivated = true;
454 m_d->activate( m_handle );
456 }
457}
458
459
461{
462 if ( m_d->deactivate && m_bActivated ) {
463 INFOLOG( "deactivate " + getPluginName() );
464 m_bActivated = false;
466 m_d->deactivate( m_handle );
468 }
469}
470
471
472void LadspaFX::setVolume( float fValue )
473{
474 if ( fValue > 2.0 ) {
475 fValue = 2.0;
476 } else if ( fValue < 0.0 ) {
477 fValue = 0.0;
478 }
479 m_fVolume = fValue;
480
481 if ( Hydrogen::get_instance()->getSong() != nullptr ) {
483 }
484}
485
486
487};
488
489
490#endif // H2CORE_HAVE_LADSPA
#define LADSPA_IS_CONTROL_OUTPUT(x)
Definition LadspaFx.cpp:34
#define LADSPA_IS_AUDIO_OUTPUT(x)
Definition LadspaFx.cpp:35
#define LADSPA_IS_CONTROL_INPUT(x)
Definition LadspaFx.cpp:32
#define LADSPA_IS_AUDIO_INPUT(x)
Definition LadspaFx.cpp:33
#define _WARNINGLOG(x)
Definition Object.h:247
#define INFOLOG(x)
Definition Object.h:240
#define _INFOLOG(x)
Definition Object.h:246
#define ERRORLOG(x)
Definition Object.h:242
#define _ERRORLOG(x)
Definition Object.h:248
static Hydrogen * get_instance()
Returns the current Hydrogen instance __instance.
Definition Hydrogen.h:84
void setIsModified(bool bIsModified)
Wrapper around Song::setIsModified() that checks whether a song is set.
LADSPA_Data fDefaultValue
Definition LadspaFX.h:105
LADSPA_Data fControlValue
Definition LadspaFX.h:106
const QString & getName() const
Definition LadspaFX.h:69
void addChild(LadspaFXGroup *pChild)
Definition LadspaFx.cpp:71
void addLadspaInfo(LadspaFXInfo *pInfo)
Definition LadspaFx.cpp:64
std::vector< LadspaFXInfo * > m_ladspaList
Definition LadspaFX.h:91
static bool alphabeticOrder(LadspaFXGroup *, LadspaFXGroup *)
Definition LadspaFx.cpp:77
LadspaFXGroup(const QString &sName)
Definition LadspaFx.cpp:40
std::vector< LadspaFXGroup * > m_childGroups
Definition LadspaFX.h:92
QString m_sFilename
plugin filename
Definition LadspaFX.h:46
unsigned m_nIAPorts
input audio port
Definition LadspaFX.h:54
unsigned m_nOAPorts
output audio port
Definition LadspaFX.h:55
LadspaFXInfo(const QString &sName)
Definition LadspaFx.cpp:94
static bool alphabeticOrder(LadspaFXInfo *a, LadspaFXInfo *b)
Definition LadspaFx.cpp:112
unsigned m_nICPorts
input control port
Definition LadspaFX.h:52
unsigned m_nOCPorts
output control port
Definition LadspaFX.h:53
LADSPA_Handle m_handle
Definition LadspaFX.h:183
std::vector< LadspaControlPort * > inputControlPorts
Definition LadspaFX.h:131
QString m_sName
Definition LadspaFX.h:177
float * m_pBuffer_L
Definition LadspaFX.h:128
const LADSPA_Descriptor * m_d
Definition LadspaFX.h:182
void setEnabled(bool bEnabled)
Definition LadspaFx.cpp:201
const QString & getPluginName() const
Definition LadspaFX.h:146
unsigned m_nIAPorts
input audio port
Definition LadspaFX.h:188
unsigned m_nOAPorts
output audio port
Definition LadspaFX.h:189
float * m_pBuffer_R
Definition LadspaFX.h:129
void setVolume(float fVolume)
Definition LadspaFx.cpp:472
std::vector< LadspaControlPort * > outputControlPorts
Definition LadspaFX.h:132
unsigned m_nICPorts
input control port
Definition LadspaFX.h:186
LadspaFX(const QString &sLibraryPath, const QString &sPluginLabel)
Definition LadspaFx.cpp:122
void setPluginName(const QString &sName)
Definition LadspaFx.cpp:194
void connectAudioPorts(float *pIn_L, float *pIn_R, float *pOut_L, float *pOut_R)
Definition LadspaFx.cpp:399
QLibrary * m_pLibrary
Definition LadspaFX.h:180
QString m_sLabel
Definition LadspaFX.h:176
unsigned m_nOCPorts
output control port
Definition LadspaFX.h:187
void processFX(unsigned nFrames)
Definition LadspaFx.cpp:439
QString m_sLibraryPath
Definition LadspaFX.h:178
static LadspaFX * load(const QString &sLibraryPath, const QString &sPluginLabel, long nSampleRate)
Definition LadspaFx.cpp:211
Helper class to preserve and restore recursive crash context strings using an RAAI pattern.
Definition Logger.h:151
#define MAX_BUFFER_SIZE
Maximum buffer size.
Definition config.dox:87