1 /*
2 * Hydrogen
3 * Copyright(c) 2002-2008 by Alex >Comix< Cominu [comix@users.sourceforge.net]
4 *
5 * http://www.hydrogen-music.org
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY, without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22 #include <hydrogen/fx/Effects.h>
23
24 #if defined(H2CORE_HAVE_LADSPA) || _DOXYGEN_
25
26 #include <hydrogen/Preferences.h>
27 #include <hydrogen/fx/LadspaFX.h>
28 #include <hydrogen/audio_engine.h>
29 #include <hydrogen/helpers/filesystem.h>
30
31 #include <algorithm>
32 #include <QDir>
33 #include <QLibrary>
34 #include <cassert>
35
36 #ifdef H2CORE_HAVE_LRDF
37 #include <lrdf.h>
38 #endif
39
40 using namespace std;
41
42 namespace H2Core
43 {
44
45 // static data
46 Effects* Effects::__instance = nullptr;
47 const char* Effects::__class_name = "Effects";
48
Effects()49 Effects::Effects()
50 : Object( __class_name )
51 , m_pRootGroup( nullptr )
52 , m_pRecentGroup( nullptr )
53 {
54 __instance = this;
55
56 for ( int nFX = 0; nFX < MAX_FX; ++nFX ) {
57 m_FXList[ nFX ] = nullptr;
58 }
59
60 getPluginList();
61 }
62
63
64
create_instance()65 void Effects::create_instance()
66 {
67 if ( __instance == nullptr ) {
68 __instance = new Effects;
69 }
70 }
71
72
73
74
~Effects()75 Effects::~Effects()
76 {
77 //INFOLOG( "DESTROY" );
78 if ( m_pRootGroup != nullptr ) delete m_pRootGroup;
79
80 //INFOLOG( "destroying " + to_string( m_pluginList.size() ) + " LADSPA plugins" );
81 for ( unsigned i = 0; i < m_pluginList.size(); i++ ) {
82 delete m_pluginList[i];
83 }
84 m_pluginList.clear();
85
86 for ( int nFX = 0; nFX < MAX_FX; ++nFX ) {
87 delete m_FXList[ nFX ];
88 }
89 }
90
91
92
getLadspaFX(int nFX)93 LadspaFX* Effects::getLadspaFX( int nFX )
94 {
95 assert( nFX < MAX_FX );
96 return m_FXList[ nFX ];
97 }
98
99
100
setLadspaFX(LadspaFX * pFX,int nFX)101 void Effects::setLadspaFX( LadspaFX* pFX, int nFX )
102 {
103 assert( nFX < MAX_FX );
104 //INFOLOG( "[setLadspaFX] FX: " + pFX->getPluginLabel() + ", " + to_string( nFX ) );
105
106 AudioEngine::get_instance()->lock( RIGHT_HERE );
107
108
109 if ( m_FXList[ nFX ] ) {
110 ( m_FXList[ nFX ] )->deactivate();
111 delete m_FXList[ nFX ];
112 }
113
114 m_FXList[ nFX ] = pFX;
115
116 if ( pFX != nullptr ) {
117 Preferences::get_instance()->setMostRecentFX( pFX->getPluginName() );
118 updateRecentGroup();
119 }
120
121
122 AudioEngine::get_instance()->unlock();
123 }
124
125
126
127 ///
128 /// Loads only usable plugins
129 ///
getPluginList()130 std::vector<LadspaFXInfo*> Effects::getPluginList()
131 {
132 if ( m_pluginList.size() != 0 ) {
133 return m_pluginList;
134 }
135
136 foreach ( const QString& sPluginDir, Filesystem::ladspa_paths() ) {
137 INFOLOG( "*** [getPluginList] reading directory: " + sPluginDir );
138
139 QDir dir( sPluginDir );
140 if ( !dir.exists() ) {
141 INFOLOG( "Directory " + sPluginDir + " not found" );
142 continue;
143 }
144
145 QFileInfoList list = dir.entryInfoList();
146 for ( int i = 0; i < list.size(); ++i ) {
147 QString sPluginName = list.at( i ).fileName();
148
149 if ( ( sPluginName == "." ) || ( sPluginName == ".." ) ) {
150 continue;
151 }
152
153 // if the file ends with .so or .dll is a plugin, else...
154 #ifdef WIN32
155 int pos = sPluginName.indexOf( ".dll" );
156 #else
157 #ifdef Q_OS_MACX
158 int pos = sPluginName.indexOf( ".dylib" );
159 #else
160 int pos = sPluginName.indexOf( ".so" );
161 #endif
162 #endif
163 if ( pos == -1 ) {
164 continue;
165 }
166 //warningLog( "[getPluginList] Loading: " + sPluginName );
167
168 QString sAbsPath = QString( "%1/%2" ).arg( sPluginDir ).arg( sPluginName );
169
170 QLibrary lib( sAbsPath );
171 LADSPA_Descriptor_Function desc_func = ( LADSPA_Descriptor_Function )lib.resolve( "ladspa_descriptor" );
172 if ( desc_func == nullptr ) {
173 ERRORLOG( "Error loading the library. (" + sAbsPath + ")" );
174 continue;
175 }
176 const LADSPA_Descriptor * d;
177 if ( desc_func ) {
178 for ( unsigned i = 0; ( d = desc_func ( i ) ) != nullptr; i++ ) {
179 LadspaFXInfo* pFX = new LadspaFXInfo( QString::fromLocal8Bit(d->Name) );
180 pFX->m_sFilename = sAbsPath;
181 pFX->m_sLabel = QString::fromLocal8Bit(d->Label);
182 pFX->m_sID = QString::number(d->UniqueID);
183 pFX->m_sMaker = QString::fromLocal8Bit(d->Maker);
184 pFX->m_sCopyright = QString::fromLocal8Bit(d->Copyright);
185
186 //INFOLOG( "Loading: " + pFX->m_sLabel );
187
188 for ( unsigned j = 0; j < d->PortCount; j++ ) {
189 LADSPA_PortDescriptor pd = d->PortDescriptors[j];
190 if ( LADSPA_IS_PORT_INPUT( pd ) && LADSPA_IS_PORT_CONTROL( pd ) ) {
191 pFX->m_nICPorts++;
192 } else if ( LADSPA_IS_PORT_INPUT( pd ) && LADSPA_IS_PORT_AUDIO( pd ) ) {
193 pFX->m_nIAPorts++;
194 } else if ( LADSPA_IS_PORT_OUTPUT( pd ) && LADSPA_IS_PORT_CONTROL( pd ) ) {
195 pFX->m_nOCPorts++;
196 } else if ( LADSPA_IS_PORT_OUTPUT( pd ) && LADSPA_IS_PORT_AUDIO( pd ) ) {
197 pFX->m_nOAPorts++;
198 } else {
199 // string sPortName = d->PortNames[ j ];
200 QString sPortName;
201 ERRORLOG( QString( "%1::%2 unknown port type" ).arg( pFX->m_sLabel ).arg( sPortName ) );
202 }
203 }
204 if ( ( pFX->m_nIAPorts == 2 ) && ( pFX->m_nOAPorts == 2 ) ) { // Stereo plugin
205 m_pluginList.push_back( pFX );
206 } else if ( ( pFX->m_nIAPorts == 1 ) && ( pFX->m_nOAPorts == 1 ) ) { // Mono plugin
207 m_pluginList.push_back( pFX );
208 } else { // not supported plugin
209 //WARNINGLOG( "Plugin not supported: " + sPluginName );
210 delete pFX;
211 }
212 }
213 } else {
214 ERRORLOG( "Error loading: " + sPluginName );
215 }
216 }
217 }
218
219 INFOLOG( QString( "Loaded %1 LADSPA plugins" ).arg( m_pluginList.size() ) );
220 std::sort( m_pluginList.begin(), m_pluginList.end(), LadspaFXInfo::alphabeticOrder );
221 return m_pluginList;
222 }
223
224
225
getLadspaFXGroup()226 LadspaFXGroup* Effects::getLadspaFXGroup()
227 {
228 INFOLOG( "[getLadspaFXGroup]" );
229
230 // LadspaFX::getPluginList(); // load the list
231
232 if ( m_pRootGroup ) {
233 return m_pRootGroup;
234 }
235
236 m_pRootGroup = new LadspaFXGroup( "Root" );
237
238 // Adding recent FX.
239 m_pRecentGroup = new LadspaFXGroup( "Recently Used" );
240 m_pRootGroup->addChild( m_pRecentGroup );
241 updateRecentGroup();
242
243 LadspaFXGroup *pUncategorizedGroup = new LadspaFXGroup( "Uncategorized" );
244 m_pRootGroup->addChild( pUncategorizedGroup );
245
246 char C = 0;
247 LadspaFXGroup* pGroup = nullptr;
248 for ( std::vector<LadspaFXInfo*>::iterator i = m_pluginList.begin(); i < m_pluginList.end(); i++ ) {
249 char ch = (*i)->m_sName.toLocal8Bit().at(0);
250 if ( ch != C ) {
251 C = ch;
252 pGroup = new LadspaFXGroup( QString( C ) );
253 pUncategorizedGroup->addChild( pGroup );
254 }
255
256 if(pGroup){
257 pGroup->addLadspaInfo( *i );
258 }
259 }
260
261
262 #ifdef H2CORE_HAVE_LRDF
263 LadspaFXGroup *pLRDFGroup = new LadspaFXGroup( "Categorized(LRDF)" );
264 m_pRootGroup->addChild( pLRDFGroup );
265 getRDF( pLRDFGroup, m_pluginList );
266 #endif
267
268 return m_pRootGroup;
269 }
270
updateRecentGroup()271 void Effects::updateRecentGroup()
272 {
273 if ( m_pRecentGroup == nullptr ) {
274 return; // Too early :s
275 }
276
277 m_pRecentGroup->clear();
278
279
280 QString sRecent; // The recent fx names sit in the preferences object
281 foreach ( sRecent, Preferences::get_instance()->getRecentFX() ) {
282 for ( std::vector<LadspaFXInfo*>::iterator i = m_pluginList.begin(); i < m_pluginList.end(); i++ ) {
283 if ( sRecent == (*i)->m_sName ) {
284 m_pRecentGroup->addLadspaInfo( *i );
285 break;
286 }
287 }
288 }
289 }
290
291 #ifdef H2CORE_HAVE_LRDF
292
293
getRDF(LadspaFXGroup * pGroup,vector<LadspaFXInfo * > pluginList)294 void Effects::getRDF( LadspaFXGroup *pGroup, vector<LadspaFXInfo*> pluginList )
295 {
296 lrdf_init();
297
298 QString sDir = "/usr/share/ladspa/rdf";
299
300 QDir dir( sDir );
301 if ( !dir.exists() ) {
302 WARNINGLOG( QString( "Directory %1 not found" ).arg( sDir ) );
303 return;
304 }
305
306 QFileInfoList list = dir.entryInfoList();
307 for ( int i = 0; i < list.size(); ++i ) {
308 QString sFilename = list.at( i ).fileName();
309 int pos = sFilename.indexOf( ".rdf" );
310 if ( pos == -1 ) {
311 continue;
312 }
313
314 QString sRDFFile = QString( "file://%1/%2" ).arg( sDir ).arg( sFilename );
315
316 int err = lrdf_read_file( sRDFFile.toLocal8Bit() );
317 if ( err ) {
318 ERRORLOG( "Error parsing rdf file " + sFilename );
319 }
320
321 QString sBase = "http://ladspa.org/ontology#Plugin";
322 RDFDescend( sBase, pGroup, pluginList );
323 }
324 }
325
326
327
328 // funzione ricorsiva
RDFDescend(const QString & sBase,LadspaFXGroup * pGroup,vector<LadspaFXInfo * > pluginList)329 void Effects::RDFDescend( const QString& sBase, LadspaFXGroup *pGroup, vector<LadspaFXInfo*> pluginList )
330 {
331 //cout << "LadspaFX::RDFDescend " << sBase.toLocal8Bit().constData() << endl;
332
333 lrdf_uris* uris = lrdf_get_subclasses( sBase.toLocal8Bit() );
334 if ( uris ) {
335 for ( int i = 0; i < ( int )uris->count; i++ ) {
336 QString sGroup = QString::fromLocal8Bit(lrdf_get_label( uris->items[ i ] ));
337
338 LadspaFXGroup *pNewGroup = nullptr;
339 // verifico se esiste gia una categoria con lo stesso nome
340 vector<LadspaFXGroup*> childGroups = pGroup-> getChildList();
341 for ( unsigned nGroup = 0; nGroup < childGroups.size(); nGroup++ ) {
342 LadspaFXGroup *pOldGroup = childGroups[nGroup];
343 if ( pOldGroup->getName() == sGroup ) {
344 pNewGroup = pOldGroup;
345 break;
346 }
347 }
348 if ( pNewGroup == nullptr ) { // il gruppo non esiste, lo creo
349 pNewGroup = new LadspaFXGroup( sGroup );
350 pGroup->addChild( pNewGroup );
351 }
352 RDFDescend( QString::fromLocal8Bit(uris->items[i]), pNewGroup, pluginList );
353 }
354 lrdf_free_uris ( uris );
355 }
356
357 uris = lrdf_get_instances( sBase.toLocal8Bit() );
358 if ( uris ) {
359 for ( int i = 0; i < ( int )uris->count; i++ ) {
360 int uid = lrdf_get_uid ( uris->items[i] );
361
362 // verifico che il plugin non sia gia nella lista
363 bool bExists = false;
364 vector<LadspaFXInfo*> fxVect = pGroup->getLadspaInfo();
365 for ( unsigned nFX = 0; nFX < fxVect.size(); nFX++ ) {
366 LadspaFXInfo *pFX = fxVect[nFX];
367 if ( pFX->m_sID.toInt() == uid ) {
368 bExists = true;
369 continue;
370 }
371 }
372
373 if ( bExists == false ) {
374 // find the ladspaFXInfo
375 for ( unsigned i = 0; i < pluginList.size(); i++ ) {
376 LadspaFXInfo *pInfo = pluginList[i];
377
378 if ( pInfo->m_sID.toInt() == uid ) {
379 pGroup->addLadspaInfo( pInfo ); // copy the LadspaFXInfo
380 }
381 }
382 }
383 }
384 lrdf_free_uris ( uris );
385 }
386 pGroup->sort();
387 }
388
389
390 #endif // H2CORE_HAVE_LRDF
391
392 };
393
394 #endif // H2CORE_HAVE_LADSPA
395