1 //=========================================================
2 // MusE
3 // Linux Music Editor
4 //
5 // audio_converter_list.cpp
6 // (C) Copyright 2010-2020 Tim E. Real (terminator356 A T sourceforge D O T net)
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License
10 // as published by the Free Software Foundation; version 2 of
11 // the License, or (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, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 //
22 //=========================================================
23
24 #include "audio_converter_plugin.h"
25
26 #include <QDir>
27 #include <QString>
28 #include <QFileInfo>
29 #include <QFileInfoList>
30 #include <QByteArray>
31
32 #include <string.h>
33
34 #include <dlfcn.h>
35
36 // For debugging output: Uncomment the fprintf section.
37 #define ERROR_AUDIOCONVERT(dev, format, args...) fprintf(dev, format, ##args)
38 #define INFO_AUDIOCONVERT(dev, format, args...) fprintf(dev, format, ##args)
39 #define DEBUG_AUDIOCONVERT(dev, format, args...) // fprintf(dev, format, ##args)
40
41 namespace MusECore {
42
discover(const QString & museGlobalLib,bool debugMsg)43 void AudioConverterPluginList::discover(const QString& museGlobalLib, bool debugMsg)
44 {
45 QString s = museGlobalLib + "/converters";
46
47 QDir pluginDir(s, QString("*.so"));
48 if(debugMsg)
49 {
50 INFO_AUDIOCONVERT(stderr, "searching for audio converters in <%s>\n", s.toLatin1().constData());
51 }
52 if(pluginDir.exists())
53 {
54 QFileInfoList list = pluginDir.entryInfoList();
55 const QFileInfo* fi;
56 for(QFileInfoList::const_iterator it=list.cbegin(); it!=list.cend(); ++it)
57 {
58 fi = &*it;
59
60 QByteArray ba = fi->filePath().toLatin1();
61 const char* path = ba.constData();
62
63 void* handle = dlopen(path, RTLD_NOW);
64 if(!handle)
65 {
66 ERROR_AUDIOCONVERT(stderr, "AudioConverterList::discover(): dlopen(%s) failed: %s\n", path, dlerror());
67 continue;
68 }
69 Audio_Converter_Descriptor_Function desc_func =
70 (Audio_Converter_Descriptor_Function)dlsym(handle, "audio_converter_descriptor");
71
72 if(!desc_func)
73 {
74 const char *txt = dlerror();
75 if(txt)
76 {
77 ERROR_AUDIOCONVERT(stderr,
78 "Unable to find audio_converter_descriptor() function in plugin "
79 "library file \"%s\": %s.\n"
80 "Are you sure this is a MusE Audio Converter plugin file?\n",
81 path, txt);
82 }
83 dlclose(handle);
84 continue;
85 }
86
87 const AudioConverterDescriptor* descr;
88
89 for(unsigned long i = 0;; ++i)
90 {
91 descr = desc_func(i);
92 if(!descr)
93 break;
94 // Make sure it doesn't already exist.
95 if(find(descr->_name, descr->_ID))
96 continue;
97 add(fi, descr);
98 }
99
100 #ifndef __KEEP_LIBRARY_OPEN__
101 // The library must be kept open to use the shared object code.
102 dlclose(handle);
103 #endif
104
105 }
106 if(debugMsg)
107 {
108 INFO_AUDIOCONVERT(stderr, "%zd Audio converters found\n", size());
109 }
110 }
111 }
112
113 //---------------------------------------------------------
114 // AudioConverterPluginList
115 //---------------------------------------------------------
116
~AudioConverterPluginList()117 AudioConverterPluginList::~AudioConverterPluginList()
118 {
119 DEBUG_AUDIOCONVERT(stderr, "AudioConverterPluginList dtor: Deleting plugins...\\n");
120 // Must close the libraries.
121 for(const_iterator ip = cbegin(); ip != cend(); ++ip)
122 if(*ip)
123 delete *ip;
124 }
125
clearDelete()126 void AudioConverterPluginList::clearDelete()
127 {
128 DEBUG_AUDIOCONVERT(stderr, "AudioConverterPluginList clearDelete: Deleting plugins...\n");
129 // Must close the libraries.
130 for(const_iterator ip = cbegin(); ip != cend(); ++ip)
131 if(*ip)
132 delete *ip;
133 clear();
134 }
135
add(const QFileInfo * fi,const AudioConverterDescriptor * d)136 void AudioConverterPluginList::add(const QFileInfo* fi, const AudioConverterDescriptor* d)
137 {
138 push_back(new AudioConverterPlugin(fi, d));
139 }
140
141 //---------------------------------------------------------
142 // find
143 //---------------------------------------------------------
144
find(const char * name,int ID,int capabilities)145 AudioConverterPlugin* AudioConverterPluginList::find(const char* name, int ID, int capabilities)
146 {
147 const bool id_valid = (ID != -1);
148 const bool caps_valid = (capabilities != -1);
149 AudioConverterPlugin* cap_res = NULL;
150 for(const_iterator i = cbegin(); i != cend(); ++i)
151 {
152 AudioConverterPlugin* plugin = *i;
153 const bool name_match = (name && (strcmp(name, plugin->name().toLatin1().constData()) == 0));
154 const bool ID_match = (id_valid && ID == plugin->id());
155 const bool caps_match = (caps_valid && (plugin->capabilities() & capabilities) == capabilities);
156 if((name && id_valid && name_match && ID_match) ||
157 name_match ||
158 ID_match)
159 return plugin;
160 else if(caps_match)
161 {
162 if(!cap_res)
163 cap_res = plugin;
164 }
165 }
166 return cap_res;
167 }
168
169 //---------------------------------------------------------
170 // AudioConverterPlugin
171 //---------------------------------------------------------
172
AudioConverterPlugin(const QFileInfo * f,const AudioConverterDescriptor * d)173 AudioConverterPlugin::AudioConverterPlugin(const QFileInfo* f, const AudioConverterDescriptor* d)
174 {
175 fi = *f;
176 plugin = NULL;
177 _descriptorFunction = NULL;
178 _handle = NULL;
179 _references = 0;
180 _instNo = 0;
181 _label = QString(d->_label);
182 _name = QString(d->_name);
183 _uniqueID = d->_ID;
184 _maxChannels = d->_maxChannels;
185 _capabilities = d->_capabilities;
186
187 _minStretchRatio = d->_minStretchRatio;
188 _maxStretchRatio = d->_maxStretchRatio;
189 _minSamplerateRatio = d->_minSamplerateRatio;
190 _maxSamplerateRatio = d->_maxSamplerateRatio;
191 _minPitchShiftRatio = d->_minPitchShiftRatio;
192 _maxPitchShiftRatio = d->_maxPitchShiftRatio;
193 }
194
~AudioConverterPlugin()195 AudioConverterPlugin::~AudioConverterPlugin()
196 {
197 DEBUG_AUDIOCONVERT(stderr, "AudioConverterPlugin dtor: this:%p plugin:%p id:%d\n", this, plugin, id());
198
199 // Actually, not an error, currently. We are the ones who purposely leave the library open.
200 // Therefore we are the ones who must close it here.
201 // TODO: Try to find a way to NOT leave the library open?
202 //if(plugin)
203 //{
204 // ERROR_AUDIOCONVERT(stderr, "AudioConverterPlugin dtor: Error: plugin is not NULL\n");
205 //}
206
207 if(_handle)
208 {
209 DEBUG_AUDIOCONVERT(stderr, "AudioConverterPlugin dtor: _handle is not NULL, closing library...\n");
210 dlclose(_handle);
211 }
212
213 _handle = NULL;
214 _descriptorFunction = NULL;
215 plugin = NULL;
216 }
217
218
incReferences(int val)219 int AudioConverterPlugin::incReferences(int val)
220 {
221 DEBUG_AUDIOCONVERT(stderr, "AudioConverterPlugin::incReferences: this:%p id:%d _references:%d val:%d\n", this, id(), _references, val);
222
223 int newref = _references + val;
224
225 if(newref <= 0)
226 {
227 _references = 0;
228
229 #ifndef __KEEP_LIBRARY_OPEN__
230 if(_handle)
231 {
232 DEBUG_AUDIOCONVERT(stderr, " no more instances, closing library\n");
233 dlclose(_handle);
234 }
235
236 _handle = NULL;
237 _descriptorFunction = NULL;
238 plugin = NULL;
239
240 #endif
241
242 return 0;
243 }
244
245 if(!_handle)
246 {
247 _handle = dlopen(fi.filePath().toLatin1().constData(), RTLD_NOW);
248
249 if(!_handle)
250 {
251 ERROR_AUDIOCONVERT(stderr, "AudioConverterPlugin::incReferences dlopen(%s) failed: %s\n",
252 fi.filePath().toLatin1().constData(), dlerror());
253 return 0;
254 }
255
256 Audio_Converter_Descriptor_Function acdf = (Audio_Converter_Descriptor_Function)dlsym(_handle, "audio_converter_descriptor");
257 if(acdf)
258 {
259 const AudioConverterDescriptor* descr;
260 for(unsigned long i = 0;; ++i)
261 {
262 descr = acdf(i);
263 if(!descr)
264 break;
265
266 QString label(descr->_label);
267 if(label == _label)
268 {
269 _descriptorFunction = acdf;
270 plugin = descr;
271 break;
272 }
273 }
274 }
275
276 if(plugin)
277 {
278 _name = QString(plugin->_name);
279 _uniqueID = plugin->_ID;
280 }
281 }
282
283 if(!plugin)
284 {
285 dlclose(_handle);
286 _handle = NULL;
287 _references = 0;
288 ERROR_AUDIOCONVERT(stderr, "AudioConverterPlugin::incReferences Error: %s no plugin!\n", fi.filePath().toLatin1().constData());
289 return 0;
290 }
291
292 _references = newref;
293
294 return _references;
295 }
296
instantiate(AudioConverterPluginI *,int systemSampleRate,int channels,AudioConverterSettings * settings,AudioConverterSettings::ModeType mode)297 AudioConverterHandle AudioConverterPlugin::instantiate(AudioConverterPluginI* /*plugi*/,
298 int systemSampleRate,
299 int channels,
300 AudioConverterSettings* settings,
301 AudioConverterSettings::ModeType mode)
302 {
303 DEBUG_AUDIOCONVERT(stderr, "AudioConverterPlugin::instantiate\n");
304 AudioConverterHandle h = plugin->instantiate(systemSampleRate, plugin, channels, settings, mode);
305 if(!h)
306 {
307 ERROR_AUDIOCONVERT(stderr, "AudioConverterPlugin::instantiate() Error: plugin:%s instantiate failed!\n", plugin->_name);
308 return 0;
309 }
310
311 return h;
312 }
313
314
315 //---------------------------------------------------------
316 // AudioConverterPluginI
317 //---------------------------------------------------------
318
AudioConverterPluginI()319 AudioConverterPluginI::AudioConverterPluginI()
320 {
321 DEBUG_AUDIOCONVERT(stderr, "AudioConverterPluginI ctor\n");
322 init();
323 }
324
~AudioConverterPluginI()325 AudioConverterPluginI::~AudioConverterPluginI()
326 {
327 DEBUG_AUDIOCONVERT(stderr, "AudioConverterPluginI dtor: this:%p handle:%p _plugin:%p\n", this, handle, _plugin);
328 if(handle)
329 {
330 for(int i = 0; i < instances; ++i)
331 {
332 // Cleanup each of the converters.
333 if(_plugin)
334 _plugin->cleanup(handle[i]);
335 }
336 // Delete the array of converters.
337 delete[] handle;
338 }
339
340 if (_plugin)
341 {
342 _plugin->incReferences(-1);
343 }
344 }
345
init()346 void AudioConverterPluginI::init()
347 {
348 _plugin = NULL;
349 instances = 0;
350 handle = NULL;
351 _channels = 0;
352 }
353
initPluginInstance(AudioConverterPlugin * plug,int systemSampleRate,int channels,AudioConverterSettings * settings,AudioConverterSettings::ModeType mode)354 bool AudioConverterPluginI::initPluginInstance(AudioConverterPlugin* plug,
355 int systemSampleRate,
356 int channels,
357 AudioConverterSettings* settings,
358 AudioConverterSettings::ModeType mode)
359 {
360 if(!plug)
361 {
362 ERROR_AUDIOCONVERT(stderr, "AudioConverterPluginI::initPluginInstance: Error: plug is zero\n");
363 return true;
364 }
365
366 DEBUG_AUDIOCONVERT(stderr, "AudioConverterPluginI::initPluginInstance: plug:%p id:%d\n", plug, plug->id());
367
368 _plugin = plug;
369 _channels = channels;
370
371 // We are creating an object from the library. Increment the references
372 // so that the library may be opened.
373 if(_plugin->incReferences(1) == 0)
374 return true;
375
376
377 QString inst("-" + QString::number(_plugin->instNo()));
378 _name = _plugin->name() + inst;
379 _label = _plugin->label() + inst;
380
381 int max_chans = _plugin->maxChannels(); // -1 = don't care, infinite channels.
382
383 if(max_chans > 0)
384 {
385 instances = _channels / max_chans;
386 if(instances < 1)
387 instances = 1;
388 }
389 else
390 // Don't care. Unlimited channels.
391 instances = 1;
392
393 handle = new AudioConverterHandle[instances];
394 for(int i = 0; i < instances; ++i)
395 handle[i] = NULL;
396
397 for(int i = 0; i < instances; ++i)
398 {
399 DEBUG_AUDIOCONVERT(stderr, " instance:%d\n", i);
400
401 handle[i] = _plugin->instantiate(this, systemSampleRate, _channels, settings, mode);
402 if(!handle[i])
403 return true;
404 }
405
406 return false;
407 }
408
isValid() const409 bool AudioConverterPluginI::isValid() const
410 {
411 if(!handle)
412 return false;
413 for(int i = 0; i < instances; ++i)
414 if(!handle[i] || !handle[i]->isValid())
415 return false;
416 return true;
417 }
418
setChannels(int channels)419 void AudioConverterPluginI::setChannels(int channels)
420 {
421 if(!handle) return;
422 for(int i = 0; i < instances; ++i)
423 if(handle[i])
424 {
425 // FIXME: Multiple instances point to the SAME SndFile.
426 // Make seperate SndFile instances per-channel,
427 // make this function per-handle.
428 // Finish this. For now just return after the first handle found:
429 handle[i]->setChannels(channels);
430 return;
431 }
432 }
433
reset()434 void AudioConverterPluginI::reset()
435 {
436 if(!handle) return;
437 for(int i = 0; i < instances; ++i)
438 if(handle[i])
439 handle[i]->reset();
440 }
441
mode() const442 AudioConverterSettings::ModeType AudioConverterPluginI::mode() const
443 {
444 if(!handle)
445 return AudioConverterSettings::RealtimeMode;
446 AudioConverterSettings::ModeType fin_m = AudioConverterSettings::RealtimeMode;
447 AudioConverterSettings::ModeType m;
448 bool first = true;
449 for(int i = 0; i < instances; ++i)
450 if(handle[i])
451 {
452 m = handle[i]->mode();
453 if(m != fin_m)
454 {
455 // Check: Each instance should be in the same mode.
456 if(!first)
457 {
458 ERROR_AUDIOCONVERT(stderr,
459 "AudioConverterPluginI::mode(): Error: Different mode:%d than first:%d in instance\n", m, fin_m);
460 }
461 first = false;
462 fin_m = m;
463 }
464 }
465 return fin_m;
466 }
467
process(SNDFILE * sf_handle,const int sf_chans,const double sf_sr_ratio,const StretchList * sf_stretch_list,const sf_count_t pos,float ** buffer,const int channels,const int frames,const bool overwrite)468 int AudioConverterPluginI::process(
469 SNDFILE* sf_handle,
470 const int sf_chans, const double sf_sr_ratio, const StretchList* sf_stretch_list,
471 const sf_count_t pos,
472 float** buffer, const int channels, const int frames, const bool overwrite)
473 {
474 if(!handle) return 0;
475 for(int i = 0; i < instances; ++i)
476 if(handle[i])
477 {
478 int count = handle[i]->process(sf_handle, sf_chans, sf_sr_ratio, sf_stretch_list,
479 pos, buffer, channels, frames, overwrite);
480 // FIXME: Multiple instances point to the SAME SndFile.
481 // Make separate SndFile instances per-channel,
482 // make this function per-handle, and work with
483 // each count returned.
484 // No choice, for now just return after the first handle found:
485 return count;
486 }
487 return 0;
488 }
489
490
491 //---------------------------------------------------------
492 // AudioConverterSettingsI
493 //---------------------------------------------------------
494
AudioConverterSettingsI()495 AudioConverterSettingsI::AudioConverterSettingsI()
496 {
497 DEBUG_AUDIOCONVERT(stderr, "AudioConverterSettingsI ctor\n");
498 init();
499 }
500
~AudioConverterSettingsI()501 AudioConverterSettingsI::~AudioConverterSettingsI()
502 {
503 DEBUG_AUDIOCONVERT(stderr, "AudioConverterSettingsI dtor: this:%p\n", this);
504
505 if(_plugin)
506 {
507 DEBUG_AUDIOCONVERT(stderr, " _plugin:%p id:%d\n", _plugin, _plugin->id());
508 if(_settings)
509 _plugin->cleanupSettings(_settings);
510
511 _plugin->incReferences(-1);
512 }
513 }
514
init()515 void AudioConverterSettingsI::init()
516 {
517 _plugin = NULL;
518 _settings = NULL;
519 }
520
assign(const AudioConverterSettingsI & other)521 void AudioConverterSettingsI::assign(const AudioConverterSettingsI& other)
522 {
523 _plugin = other._plugin;
524 if(!_settings)
525 {
526 // We are creating an object from the library. Increment the references
527 // so that the library may be opened.
528 if(_plugin->incReferences(1) == 0)
529 return;
530
531 _settings = _plugin->createSettings(false); // Don't care about isLocal argument.
532 if(!_settings)
533 {
534 // Give up the reference.
535 _plugin->incReferences(-1);
536 return;
537 }
538 }
539 _settings->assign(*other._settings);
540 }
541
initSettingsInstance(AudioConverterPlugin * plug,bool isLocal)542 bool AudioConverterSettingsI::initSettingsInstance(AudioConverterPlugin* plug, bool isLocal)
543 {
544 if(!plug)
545 {
546 ERROR_AUDIOCONVERT(stderr, "AudioConverterSettingsI::createSettings: Error: plug is zero\n");
547 return true;
548 }
549
550 DEBUG_AUDIOCONVERT(stderr, "AudioConverterSettingsI::initSettingsInstance: this:%p plug:%p id:%d\n", this, plug, plug->id());
551
552 _plugin = plug;
553
554 // We are creating an object from the library. Increment the references
555 // so that the library may be opened.
556 if(_plugin->incReferences(1) == 0)
557 return true;
558
559 _settings = _plugin->createSettings(isLocal);
560 if(!_settings)
561 {
562 // Give up the reference.
563 _plugin->incReferences(-1);
564 return true;
565 }
566
567 return false;
568 }
569
570
571 } // namespace MusECore
572