Fix a tiny memory-leak when calling vfork
[ardour.git] / libs / ardour / import_pt.cc
1 /*
2     Copyright (C) 2018 Paul Davis
3     Author: Damien Zammit
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19 */
20
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <sys/time.h>
24 #include <errno.h>
25 #include <unistd.h>
26 #include <algorithm>
27 #include <glibmm.h>
28
29 #include "pbd/pthread_utils.h"
30 #include "pbd/basename.h"
31 #include "pbd/shortpath.h"
32 #include "pbd/stateful_diff_command.h"
33
34 #include "ardour/audioengine.h"
35 #include "ardour/audio_track.h"
36 #include "ardour/audiofilesource.h"
37 #include "ardour/audioregion.h"
38 #include "ardour/import_status.h"
39 #include "ardour/midi_region.h"
40 #include "ardour/midi_track.h"
41 #include "ardour/midi_model.h"
42 #include "ardour/operations.h"
43 #include "ardour/region_factory.h"
44 #include "ardour/smf_source.h"
45 #include "ardour/source_factory.h"
46 #include "ardour/utils.h"
47 #include "ardour/playlist.h"
48 #include "ardour/session.h"
49 #include "pbd/memento_command.h"
50
51 #include "ptformat/ptfformat.h"
52
53 #include "pbd/i18n.h"
54
55 using namespace std;
56 using namespace ARDOUR;
57 using namespace PBD;
58 using namespace Glib;
59 using std::string;
60
61 /* Functions supporting the incorporation of PT sessions into ardour */
62
63 struct midipair {
64         midipair (uint16_t idx, string n)
65                 : ptfindex (idx)
66                   , trname (n)
67         {}
68         uint16_t ptfindex;
69         string trname;
70 };
71
72 typedef struct ptflookup {
73         uint16_t index1;
74         uint16_t index2;
75         PBD::ID  id;
76
77         bool operator ==(const struct ptflookup& other) {
78                 return (this->index1 == other.index1);
79         }
80 } ptflookup_t;
81
82
83 bool
84 Session::import_sndfile_as_region (string path, SrcQuality quality, samplepos_t& pos, SourceList& sources, ImportStatus& status)
85 {
86         /* Import the source */
87         status.paths.clear();
88         status.paths.push_back(path);
89         status.current = 1;
90         status.total = 1;
91         status.freeze = false;
92         status.quality = quality;
93         status.replace_existing_source = false;
94         status.split_midi_channels = false;
95         status.done = false;
96         status.cancel = false;
97
98         import_files(status);
99         sources.clear();
100
101         /* FIXME: There is no way to tell if cancel button was pressed
102          * or if the file failed to import, just that one of these occurred.
103          * We want status.cancel to reflect the user's choice only
104          */
105         if (status.cancel && status.current > 1) {
106                 /* Succeeded to import file, assume user hit cancel */
107                 return false;
108         } else if (status.cancel && status.current == 1) {
109                 /* Failed to import file, assume user did not hit cancel */
110                 status.cancel = false;
111                 return false;
112         }
113
114         sources.push_back(status.sources.front());
115
116         /* Put the source on a region */
117         vector<boost::shared_ptr<Region> > regions;
118         string region_name;
119         bool use_timestamp;
120
121         use_timestamp = (pos == -1);
122
123         /* take all the sources we have and package them up as a region */
124
125         region_name = region_name_from_path (status.paths.front(), (sources.size() > 1), false);
126
127         /* we checked in import_sndfiles() that there were not too many */
128
129         while (RegionFactory::region_by_name (region_name)) {
130                 region_name = bump_name_once (region_name, '.');
131         }
132
133         PropertyList plist;
134
135         plist.add (ARDOUR::Properties::start, 0);
136         plist.add (ARDOUR::Properties::length, sources[0]->length (pos));
137         plist.add (ARDOUR::Properties::name, region_name);
138         plist.add (ARDOUR::Properties::layer, 0);
139         plist.add (ARDOUR::Properties::whole_file, true);
140         plist.add (ARDOUR::Properties::external, true);
141
142         boost::shared_ptr<Region> r = RegionFactory::create (sources, plist);
143
144         if (use_timestamp && boost::dynamic_pointer_cast<AudioRegion>(r)) {
145                 boost::dynamic_pointer_cast<AudioRegion>(r)->special_set_position(sources[0]->natural_position());
146         }
147
148         regions.push_back (r);
149
150         /* if we're creating a new track, name it after the cleaned-up
151          * and "merged" region name.
152          */
153
154         int n = 0;
155
156         for (vector<boost::shared_ptr<Region> >::iterator r = regions.begin(); r != regions.end(); ++r, ++n) {
157                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (*r);
158
159                 if (use_timestamp) {
160                         if (ar) {
161
162                                 /* get timestamp for this region */
163
164                                 const boost::shared_ptr<Source> s (ar->sources().front());
165                                 const boost::shared_ptr<AudioSource> as = boost::dynamic_pointer_cast<AudioSource> (s);
166
167                                 assert (as);
168
169                                 if (as->natural_position() != 0) {
170                                         pos = as->natural_position();
171                                 } else {
172                                         pos = 0;
173                                 }
174                         } else {
175                                 /* should really get first position in MIDI file, but for now, use 0 */
176                                 pos = 0;
177                         }
178                 }
179         }
180
181         for (SourceList::iterator x = sources.begin(); x != sources.end(); ++x) {
182                 SourceFactory::setup_peakfile (*x, true);
183         }
184
185         return true;
186 }
187
188
189 void
190 Session::import_pt (PTFFormat& ptf, ImportStatus& status)
191 {
192         vector<boost::shared_ptr<Region> > regions;
193         boost::shared_ptr<ARDOUR::Track> track;
194         ARDOUR::PluginInfoPtr instrument;
195         vector<string> to_import;
196         string fullpath;
197         bool ok = false;
198         bool onefailed = false;
199         samplepos_t pos = -1;
200         uint32_t srate = sample_rate ();
201
202         vector<ptflookup_t> ptfwavpair;
203         vector<ptflookup_t> ptfregpair;
204         vector<PTFFormat::wav_t>::iterator w;
205
206         SourceList just_one_src;
207         SourceList imported;
208
209         boost::shared_ptr<AudioTrack> existing_track;
210         uint16_t nth = 0;
211         vector<ptflookup_t> usedtracks;
212         ptflookup_t utr;
213
214         Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock (), Glib::Threads::NOT_LOCK);
215
216         for (w = ptf.audiofiles.begin (); w != ptf.audiofiles.end () && !status.cancel; ++w) {
217                 ptflookup_t p;
218                 ok = false;
219                 /* Try audio file */
220                 fullpath = Glib::build_filename (Glib::path_get_dirname (ptf.path), "Audio Files");
221                 fullpath = Glib::build_filename (fullpath, w->filename);
222                 if (Glib::file_test (fullpath, Glib::FILE_TEST_EXISTS)) {
223                         just_one_src.clear();
224                         ok = import_sndfile_as_region (fullpath, SrcBest, pos, just_one_src, status);
225                 } else {
226                         /* Try fade file */
227                         fullpath = Glib::build_filename (Glib::path_get_dirname (ptf.path), "Fade Files");
228                         fullpath = Glib::build_filename (fullpath, w->filename);
229                         if (Glib::file_test (fullpath, Glib::FILE_TEST_EXISTS)) {
230                                 just_one_src.clear();
231                                 ok = import_sndfile_as_region (fullpath, SrcBest, pos, just_one_src, status);
232                         } else {
233                                 onefailed = true;
234
235                                 /* ptformat knows length of sources *in PT sample rate*
236                                  * BUT if ardour user later resolves missing file,
237                                  * it won't be resampled, so we can only do this
238                                  * when sample rates are matching
239                                  */
240                                 if (sample_rate () == ptf.sessionrate) {
241                                         /* Insert reference to missing source */
242                                         samplecnt_t sourcelen = w->length;
243                                         XMLNode srcxml (X_("Source"));
244                                         srcxml.set_property ("name", w->filename);
245                                         srcxml.set_property ("type", "audio");
246                                         srcxml.set_property ("id", PBD::ID ().to_s ());
247                                         boost::shared_ptr<Source> source = SourceFactory::createSilent (*this, srcxml, sourcelen, sample_rate ());
248                                         p.index1 = w->index;
249                                         p.id = source->id ();
250                                         ptfwavpair.push_back (p);
251                                         imported.push_back (source);
252                                         warning << string_compose (_("PT Import : MISSING `%1`, inserting ref to missing source"), fullpath) << endmsg;
253                                 } else {
254                                         warning << string_compose (_("PT Import : MISSING `%1`, please check Audio Files"), fullpath) << endmsg;
255                                 }
256                         }
257                 }
258                 if (ok) {
259                         p.index1 = w->index;
260                         p.id = just_one_src.back ()->id ();
261
262                         ptfwavpair.push_back (p);
263                         imported.push_back (just_one_src.back ());
264                 }
265         }
266
267         if (imported.empty ()) {
268                 error << _("Failed to find any audio for PT import") << endmsg;
269                 goto trymidi;
270         } else if (onefailed) {
271                 warning << _("Failed to load one or more of the audio files for PT import, see above list") << endmsg;
272         } else {
273                 info << _("All audio files found for PT import!") << endmsg;
274         }
275
276         lx.acquire();
277         save_state("");
278
279         for (vector<PTFFormat::region_t>::iterator a = ptf.regions.begin ();
280                         a != ptf.regions.end (); ++a) {
281                 for (vector<ptflookup_t>::iterator p = ptfwavpair.begin ();
282                                 p != ptfwavpair.end (); ++p) {
283                         if ((p->index1 == a->wave.index) && (strcmp (a->wave.filename.c_str (), "") != 0)) {
284                                 for (SourceList::iterator x = imported.begin (); x != imported.end (); ++x) {
285                                         if ((*x)->id () == p->id) {
286                                                 /* Matched an uncreated ptf region to ardour region */
287                                                 ptflookup_t rp;
288                                                 PropertyList plist;
289
290                                                 plist.add (ARDOUR::Properties::start, a->sampleoffset);
291                                                 plist.add (ARDOUR::Properties::position, 0);
292                                                 plist.add (ARDOUR::Properties::length, a->length);
293                                                 plist.add (ARDOUR::Properties::name, a->name);
294                                                 plist.add (ARDOUR::Properties::layer, 0);
295                                                 plist.add (ARDOUR::Properties::whole_file, false);
296                                                 plist.add (ARDOUR::Properties::external, true);
297
298                                                 just_one_src.clear ();
299                                                 just_one_src.push_back (*x);
300
301                                                 boost::shared_ptr<Region> r = RegionFactory::create (just_one_src, plist);
302                                                 regions.push_back (r);
303
304                                                 rp.id = regions.back ()->id ();
305                                                 rp.index1 = a->index;
306                                                 ptfregpair.push_back (rp);
307                                         }
308                                 }
309                         }
310                 }
311         }
312
313         for (vector<PTFFormat::track_t>::iterator a = ptf.tracks.begin (); a != ptf.tracks.end (); ++a) {
314                 for (vector<ptflookup_t>::iterator p = ptfregpair.begin ();
315                                 p != ptfregpair.end (); ++p) {
316
317                         if (p->index1 == a->reg.index)  {
318
319                                 /* Matched a ptf active region to an ardour region */
320                                 utr.index1 = a->index;
321                                 utr.index2 = nth;
322                                 utr.id = p->id;
323                                 boost::shared_ptr<Region> r = RegionFactory::region_by_id (p->id);
324                                 vector<ptflookup_t>::iterator lookuptr = usedtracks.begin ();
325                                 vector<ptflookup_t>::iterator found;
326                                 if ((found = std::find (lookuptr, usedtracks.end (), utr)) != usedtracks.end ()) {
327                                         DEBUG_TRACE (DEBUG::FileUtils, string_compose ("\twav(%1) reg(%2) ptf_tr(%3) ard_tr(%4)\n", a->reg.wave.filename.c_str (), a->reg.index, found->index1, found->index2));
328
329                                         /* Use existing track if possible */
330                                         existing_track = get_nth_audio_track (found->index2 + 1);
331                                         if (!existing_track) {
332                                                 lx.release();
333                                                 list<boost::shared_ptr<AudioTrack> > at (new_audio_track (1, 2, 0, 1, "", PresentationInfo::max_order, Normal));
334                                                 if (at.empty ()) {
335                                                         return;
336                                                 }
337                                                 lx.acquire();
338                                                 existing_track = at.back ();
339                                         }
340                                         /* Put on existing track */
341                                         boost::shared_ptr<Playlist> playlist = existing_track->playlist ();
342                                         boost::shared_ptr<Region> copy (RegionFactory::create (r, true));
343                                         playlist->clear_changes ();
344                                         playlist->add_region (copy, a->reg.startpos);
345                                         //add_command (new StatefulDiffCommand (playlist));
346                                 } else {
347                                         lx.release();
348                                         /* Put on a new track */
349                                         DEBUG_TRACE (DEBUG::FileUtils, string_compose ("\twav(%1) reg(%2) new_tr(%3)\n", a->reg.wave.filename.c_str (), a->reg.index, nth));
350                                         list<boost::shared_ptr<AudioTrack> > at (new_audio_track (1, 2, 0, 1, "", PresentationInfo::max_order, Normal));
351                                         if (at.empty ()) {
352                                                 return;
353                                         }
354                                         lx.acquire();
355                                         existing_track = at.back ();
356                                         std::string trackname;
357                                         try {
358                                                 trackname = Glib::convert_with_fallback (a->name, "UTF-8", "UTF-8", "_");
359                                         } catch (Glib::ConvertError& err) {
360                                                 trackname = string_compose ("Invalid %1", a->index);
361                                         }
362                                         /* generate a unique name by adding a number if needed */
363                                         uint32_t id = 0;
364                                         if (!find_route_name (trackname.c_str (), id, trackname, false)) {
365                                                 fatal << _("PTImport: UINT_MAX routes? impossible!") << endmsg;
366                                                 abort(); /*NOTREACHED*/
367                                         }
368                                         existing_track->set_name (trackname);
369                                         boost::shared_ptr<Playlist> playlist = existing_track->playlist();
370                                         boost::shared_ptr<Region> copy (RegionFactory::create (r, true));
371                                         playlist->clear_changes ();
372                                         playlist->add_region (copy, a->reg.startpos);
373                                         //add_command (new StatefulDiffCommand (playlist));
374                                         nth++;
375                                 }
376                                 usedtracks.push_back (utr);
377
378                                 save_state("");
379                         }
380                 }
381         }
382
383         lx.release();
384
385 trymidi:
386         status.paths.clear();
387         status.paths.push_back(ptf.path);
388         status.current = 1;
389         status.total = 1;
390         status.freeze = false;
391         status.done = false;
392         status.cancel = false;
393         status.progress = 0;
394
395         /* MIDI - Find list of unique midi tracks first */
396
397         vector<midipair> uniquetr;
398
399         for (vector<PTFFormat::track_t>::iterator a = ptf.miditracks.begin (); a != ptf.miditracks.end (); ++a) {
400                 bool found = false;
401                 for (vector<midipair>::iterator b = uniquetr.begin (); b != uniquetr.end (); ++b) {
402                         if (b->trname == a->name) {
403                                 found = true;
404                                 break;
405                         }
406                 }
407                 if (!found) {
408                         uniquetr.push_back (midipair (a->index, a->name));
409                         //printf(" : %d : %s\n", a->index, a->name.c_str());
410                 }
411         }
412
413         std::map <int, boost::shared_ptr<MidiTrack> > midi_tracks;
414         /* MIDI - Create unique midi tracks and a lookup table for used tracks */
415         for (vector<midipair>::iterator a = uniquetr.begin (); a != uniquetr.end (); ++a) {
416                 ptflookup_t miditr;
417                 list<boost::shared_ptr<MidiTrack> > mt (new_midi_track (
418                                 ChanCount (DataType::MIDI, 1),
419                                 ChanCount (DataType::MIDI, 1),
420                                 true,
421                                 instrument, (Plugin::PresetRecord*) 0,
422                                 (RouteGroup*) 0,
423                                 1,
424                                 a->trname,
425                                 PresentationInfo::max_order,
426                                 Normal));
427                 assert (mt.size () == 1);
428                 midi_tracks[a->ptfindex] = mt.front ();
429         }
430
431         /* MIDI - Add midi regions one-by-one to corresponding midi tracks */
432         for (vector<PTFFormat::track_t>::iterator a = ptf.miditracks.begin (); a != ptf.miditracks.end (); ++a) {
433
434                 boost::shared_ptr<MidiTrack> midi_track = midi_tracks[a->index];
435                 assert (midi_track);
436                 boost::shared_ptr<Playlist> playlist = midi_track->playlist ();
437                 samplepos_t f = (samplepos_t)a->reg.startpos * srate / 1920000.;
438                 samplecnt_t length = (samplecnt_t)a->reg.length * srate / 1920000.;
439                 MusicSample pos (f, 0);
440                 boost::shared_ptr<Source> src = create_midi_source_by_stealing_name (midi_track);
441                 PropertyList plist;
442                 plist.add (ARDOUR::Properties::start, 0);
443                 plist.add (ARDOUR::Properties::length, length);
444                 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix (src->name ()));
445                 //printf(" : %d - trackname: (%s)\n", a->index, src->name ().c_str ());
446                 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
447                 /* sets beat position */
448                 region->set_position (pos.sample, pos.division);
449                 midi_track->playlist ()->add_region (region, pos.sample, 1.0, false, pos.division);
450
451                 boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(region);
452                 boost::shared_ptr<MidiModel> mm = mr->midi_source (0)->model ();
453                 MidiModel::NoteDiffCommand *midicmd;
454                 midicmd = mm->new_note_diff_command ("Import ProTools MIDI");
455
456                 for (vector<PTFFormat::midi_ev_t>::iterator j = a->reg.midi.begin (); j != a->reg.midi.end (); ++j) {
457                         //printf(" : MIDI : pos=%f len=%f\n", (float)j->pos / 960000., (float)j->length / 960000.);
458                         Temporal::Beats start = (Temporal::Beats)(j->pos / 960000.);
459                         Temporal::Beats len = (Temporal::Beats)(j->length / 960000.);
460                         /* PT C-2 = 0, Ardour C-1 = 0, subtract twelve to convert ? */
461                         midicmd->add (boost::shared_ptr<Evoral::Note<Temporal::Beats> > (new Evoral::Note<Temporal::Beats> ((uint8_t)1, start, len, j->note, j->velocity)));
462                 }
463                 mm->apply_command (this, midicmd);
464                 boost::shared_ptr<Region> copy (RegionFactory::create (mr, true));
465                 playlist->clear_changes ();
466                 playlist->add_region (copy, f);
467         }
468
469         status.progress = 1.0;
470         status.done = true;
471         save_state("");
472         status.sources.clear ();
473         status.all_done = true;
474 }