better comments
[ardour.git] / libs / ardour / source_factory.cc
1 /*
2  * Copyright (C) 2006-2014 David Robillard <d@drobilla.net>
3  * Copyright (C) 2007-2009 Taybin Rutkin <taybin@taybin.com>
4  * Copyright (C) 2007-2018 Paul Davis <paul@linuxaudiosystems.com>
5  * Copyright (C) 2009-2011 Carl Hetherington <carl@carlh.net>
6  * Copyright (C) 2014-2015 Robin Gareus <robin@gareus.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 along
19  * with this program; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  */
22
23 #ifdef WAF_BUILD
24 #include "libardour-config.h"
25 #endif
26
27 #include "pbd/error.h"
28 #include "pbd/convert.h"
29 #include "pbd/pthread_utils.h"
30 #include "pbd/stacktrace.h"
31
32 #include "ardour/audioplaylist.h"
33 #include "ardour/audio_playlist_source.h"
34 #include "ardour/boost_debug.h"
35 #include "ardour/midi_playlist.h"
36 #include "ardour/midi_playlist_source.h"
37 #include "ardour/source.h"
38 #include "ardour/source_factory.h"
39 #include "ardour/sndfilesource.h"
40 #include "ardour/silentfilesource.h"
41 #include "ardour/smf_source.h"
42 #include "ardour/session.h"
43
44 #ifdef  HAVE_COREAUDIO
45 #include "ardour/coreaudiosource.h"
46 #endif
47
48
49 #include "pbd/i18n.h"
50
51 using namespace ARDOUR;
52 using namespace std;
53 using namespace PBD;
54
55 PBD::Signal1<void,boost::shared_ptr<Source> > SourceFactory::SourceCreated;
56 Glib::Threads::Cond SourceFactory::PeaksToBuild;
57 Glib::Threads::Mutex SourceFactory::peak_building_lock;
58 std::list<boost::weak_ptr<AudioSource> > SourceFactory::files_with_peaks;
59
60 static int active_threads = 0;
61
62 static void
63 peak_thread_work ()
64 {
65         SessionEvent::create_per_thread_pool (X_("PeakFile Builder "), 64);
66
67         while (true) {
68
69                 SourceFactory::peak_building_lock.lock ();
70
71           wait:
72                 if (SourceFactory::files_with_peaks.empty()) {
73                         SourceFactory::PeaksToBuild.wait (SourceFactory::peak_building_lock);
74                 }
75
76                 if (SourceFactory::files_with_peaks.empty()) {
77                         goto wait;
78                 }
79
80                 boost::shared_ptr<AudioSource> as (SourceFactory::files_with_peaks.front().lock());
81                 SourceFactory::files_with_peaks.pop_front ();
82                 ++active_threads;
83                 SourceFactory::peak_building_lock.unlock ();
84
85                 if (!as) {
86                         continue;
87                 }
88
89                 as->setup_peakfile ();
90                 SourceFactory::peak_building_lock.lock ();
91                 --active_threads;
92                 SourceFactory::peak_building_lock.unlock ();
93         }
94 }
95
96 int
97 SourceFactory::peak_work_queue_length ()
98 {
99         // ideally we'd loop over the queue and check for duplicates
100         // and existing valid peak-files..
101         return SourceFactory::files_with_peaks.size () + active_threads;
102 }
103
104 void
105 SourceFactory::init ()
106 {
107         for (int n = 0; n < 2; ++n) {
108                 Glib::Threads::Thread::create (sigc::ptr_fun (::peak_thread_work));
109         }
110 }
111
112 int
113 SourceFactory::setup_peakfile (boost::shared_ptr<Source> s, bool async)
114 {
115         boost::shared_ptr<AudioSource> as (boost::dynamic_pointer_cast<AudioSource> (s));
116
117         if (as) {
118
119                 // immediately set 'peakfile-path' for empty and NoPeakFile sources
120                 if (async && !as->empty() && !(as->flags() & Source::NoPeakFile)) {
121
122                         Glib::Threads::Mutex::Lock lm (peak_building_lock);
123                         files_with_peaks.push_back (boost::weak_ptr<AudioSource> (as));
124                         PeaksToBuild.broadcast ();
125
126                 } else {
127
128                         if (as->setup_peakfile ()) {
129                                 error << string_compose("SourceFactory: could not set up peakfile for %1", as->name()) << endmsg;
130                                 return -1;
131                         }
132                 }
133         }
134
135         return 0;
136 }
137
138 boost::shared_ptr<Source>
139 SourceFactory::createSilent (Session& s, const XMLNode& node, samplecnt_t nframes, float sr)
140 {
141         Source* src = new SilentFileSource (s, node, nframes, sr);
142 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
143         // boost_debug_shared_ptr_mark_interesting (src, "Source");
144 #endif
145         boost::shared_ptr<Source> ret (src);
146         // no analysis data - the file is non-existent
147         SourceCreated (ret);
148         return ret;
149 }
150
151 boost::shared_ptr<Source>
152 SourceFactory::create (Session& s, const XMLNode& node, bool defer_peaks)
153 {
154         DataType type = DataType::AUDIO;
155         XMLProperty const * prop = node.property("type");
156
157         if (prop) {
158                 type = DataType (prop->value());
159         }
160
161         if (type == DataType::AUDIO) {
162
163                 /* it could be nested */
164
165                 if (node.property ("playlist") != 0) {
166
167                         try {
168                                 boost::shared_ptr<AudioPlaylistSource> ap (new AudioPlaylistSource (s, node));
169
170                                 if (setup_peakfile (ap, true)) {
171                                         return boost::shared_ptr<Source>();
172                                 }
173
174                                 ap->check_for_analysis_data_on_disk ();
175
176                                 SourceCreated (ap);
177                                 return ap;
178
179                         } catch (failed_constructor&) {
180                                 /* oh well, so much for that then ... */
181                         }
182
183                 } else {
184
185
186                         try {
187                                 Source* src = new SndFileSource (s, node);
188 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
189                                 // boost_debug_shared_ptr_mark_interesting (src, "Source");
190 #endif
191                                 boost::shared_ptr<Source> ret (src);
192                                 if (setup_peakfile (ret, defer_peaks)) {
193                                         return boost::shared_ptr<Source>();
194                                 }
195                                 ret->check_for_analysis_data_on_disk ();
196                                 SourceCreated (ret);
197                                 return ret;
198                         }
199
200                         catch (failed_constructor& err) {
201
202 #ifdef HAVE_COREAUDIO
203
204                                 /* this is allowed to throw */
205
206                                 Source *src = new CoreAudioSource (s, node);
207 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
208                                 // boost_debug_shared_ptr_mark_interesting (src, "Source");
209 #endif
210                                 boost::shared_ptr<Source> ret (src);
211
212                                 if (setup_peakfile (ret, defer_peaks)) {
213                                         return boost::shared_ptr<Source>();
214                                 }
215
216                                 ret->check_for_analysis_data_on_disk ();
217                                 SourceCreated (ret);
218                                 return ret;
219 #else
220                                 throw; // rethrow
221 #endif
222                         }
223                 }
224         } else if (type == DataType::MIDI) {
225                 try {
226                         boost::shared_ptr<SMFSource> src (new SMFSource (s, node));
227                         Source::Lock lock(src->mutex());
228                         src->load_model (lock, true);
229 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
230                         // boost_debug_shared_ptr_mark_interesting (src, "Source");
231 #endif
232                         src->check_for_analysis_data_on_disk ();
233                         SourceCreated (src);
234                         return src;
235                 } catch (...) {
236                 }
237         }
238
239         return boost::shared_ptr<Source>();
240 }
241
242 boost::shared_ptr<Source>
243 SourceFactory::createExternal (DataType type, Session& s, const string& path,
244                                int chn, Source::Flag flags, bool announce, bool defer_peaks)
245 {
246         if (type == DataType::AUDIO) {
247
248                 if (!(flags & Destructive)) {
249
250                         try {
251
252                                 Source* src = new SndFileSource (s, path, chn, flags);
253 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
254                                 // boost_debug_shared_ptr_mark_interesting (src, "Source");
255 #endif
256                                 boost::shared_ptr<Source> ret (src);
257
258                                 if (setup_peakfile (ret, defer_peaks)) {
259                                         return boost::shared_ptr<Source>();
260                                 }
261
262                                 ret->check_for_analysis_data_on_disk ();
263                                 if (announce) {
264                                         SourceCreated (ret);
265                                 }
266                                 return ret;
267                         }
268
269                         catch (failed_constructor& err) {
270 #ifdef HAVE_COREAUDIO
271
272                                 Source* src = new CoreAudioSource (s, path, chn, flags);
273 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
274                                 // boost_debug_shared_ptr_mark_interesting (src, "Source");
275 #endif
276                                 boost::shared_ptr<Source> ret (src);
277                                 if (setup_peakfile (ret, defer_peaks)) {
278                                         return boost::shared_ptr<Source>();
279                                 }
280                                 ret->check_for_analysis_data_on_disk ();
281                                 if (announce) {
282                                         SourceCreated (ret);
283                                 }
284                                 return ret;
285
286 #else
287                                 throw; // rethrow
288 #endif
289                         }
290
291                 } else {
292                         // eh?
293                 }
294
295         } else if (type == DataType::MIDI) {
296
297                 try {
298                         boost::shared_ptr<SMFSource> src (new SMFSource (s, path));
299                         Source::Lock lock(src->mutex());
300                         src->load_model (lock, true);
301 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
302                         // boost_debug_shared_ptr_mark_interesting (src, "Source");
303 #endif
304
305                         if (announce) {
306                                 SourceCreated (src);
307                         }
308
309                         return src;
310                 } catch (...) {
311                 }
312
313         }
314
315         return boost::shared_ptr<Source>();
316 }
317
318 boost::shared_ptr<Source>
319 SourceFactory::createWritable (DataType type, Session& s, const std::string& path,
320                                bool destructive, samplecnt_t rate, bool announce, bool defer_peaks)
321 {
322         /* this might throw failed_constructor(), which is OK */
323
324         if (type == DataType::AUDIO) {
325                 Source* src = new SndFileSource (s, path, string(),
326                                                  s.config.get_native_file_data_format(),
327                                                  s.config.get_native_file_header_format(),
328                                                  rate,
329                                                  (destructive
330                                                   ? Source::Flag (SndFileSource::default_writable_flags | Source::Destructive)
331                                                   : SndFileSource::default_writable_flags));
332 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
333                 // boost_debug_shared_ptr_mark_interesting (src, "Source");
334 #endif
335                 boost::shared_ptr<Source> ret (src);
336
337                 if (setup_peakfile (ret, defer_peaks)) {
338                         return boost::shared_ptr<Source>();
339                 }
340
341                 // no analysis data - this is a new file
342
343                 if (announce) {
344                         SourceCreated (ret);
345                 }
346                 return ret;
347
348         } else if (type == DataType::MIDI) {
349                 // XXX writable flags should belong to MidiSource too
350                 try {
351                         boost::shared_ptr<SMFSource> src (new SMFSource (s, path, SndFileSource::default_writable_flags));
352
353                         assert (src->writable ());
354
355                         Source::Lock lock(src->mutex());
356                         src->load_model (lock, true);
357 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
358                         // boost_debug_shared_ptr_mark_interesting (src, "Source");
359 #endif
360
361                         // no analysis data - this is a new file
362
363                         if (announce) {
364                                 SourceCreated (src);
365                         }
366
367                         return src;
368
369                 } catch (...) {
370                 }
371         }
372
373         return boost::shared_ptr<Source> ();
374 }
375
376 boost::shared_ptr<Source>
377 SourceFactory::createForRecovery (DataType type, Session& s, const std::string& path, int chn)
378 {
379         /* this might throw failed_constructor(), which is OK */
380
381         if (type == DataType::AUDIO) {
382                 Source* src = new SndFileSource (s, path, chn);
383
384 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
385                 // boost_debug_shared_ptr_mark_interesting (src, "Source");
386 #endif
387                 boost::shared_ptr<Source> ret (src);
388
389                 if (setup_peakfile (ret, false)) {
390                         return boost::shared_ptr<Source>();
391                 }
392
393                 // no analysis data - this is still basically a new file (we
394                 // crashed while recording.
395
396                 // always announce these files
397
398                 SourceCreated (ret);
399
400                 return ret;
401
402         } else if (type == DataType::MIDI) {
403                 error << _("Recovery attempted on a MIDI file - not implemented") << endmsg;
404         }
405
406         return boost::shared_ptr<Source> ();
407 }
408
409 boost::shared_ptr<Source>
410 SourceFactory::createFromPlaylist (DataType type, Session& s, boost::shared_ptr<Playlist> p, const PBD::ID& orig, const std::string& name,
411                                    uint32_t chn, sampleoffset_t start, samplecnt_t len, bool copy, bool defer_peaks)
412 {
413         if (type == DataType::AUDIO) {
414                 try {
415
416                         boost::shared_ptr<AudioPlaylist> ap = boost::dynamic_pointer_cast<AudioPlaylist>(p);
417
418                         if (ap) {
419
420                                 if (copy) {
421                                         ap.reset (new AudioPlaylist (ap, start, len, name, true));
422                                         start = 0;
423                                 }
424
425                                 Source* src = new AudioPlaylistSource (s, orig, name, ap, chn, start, len, Source::Flag (0));
426                                 boost::shared_ptr<Source> ret (src);
427
428                                 if (setup_peakfile (ret, defer_peaks)) {
429                                         return boost::shared_ptr<Source>();
430                                 }
431
432                                 ret->check_for_analysis_data_on_disk ();
433                                 SourceCreated (ret);
434                                 return ret;
435                         }
436                 }
437
438                 catch (failed_constructor& err) {
439                         /* relax - return at function scope */
440                 }
441
442         } else if (type == DataType::MIDI) {
443
444                 try {
445
446                         boost::shared_ptr<MidiPlaylist> ap = boost::dynamic_pointer_cast<MidiPlaylist>(p);
447
448                         if (ap) {
449
450                                 if (copy) {
451                                         ap.reset (new MidiPlaylist (ap, start, len, name, true));
452                                         start = 0;
453                                 }
454
455                                 Source* src = new MidiPlaylistSource (s, orig, name, ap, chn, start, len, Source::Flag (0));
456                                 boost::shared_ptr<Source> ret (src);
457
458                                 SourceCreated (ret);
459                                 return ret;
460                         }
461                 }
462
463                 catch (failed_constructor& err) {
464                         /* relax - return at function scope */
465                 }
466
467         }
468
469         return boost::shared_ptr<Source>();
470 }