more upgrades to the import dialog
[ardour.git] / gtk2_ardour / editor_audio_import.cc
1 /*
2     Copyright (C) 2000-2006 Paul Davis 
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <errno.h>
23 #include <unistd.h>
24
25 #include <sndfile.h>
26
27 #include <pbd/pthread_utils.h>
28 #include <pbd/basename.h>
29 #include <pbd/shortpath.h>
30
31 #include <gtkmm2ext/choice.h>
32 #include <gtkmm2ext/window_title.h>
33
34 #include <ardour/session.h>
35 #include <ardour/audioplaylist.h>
36 #include <ardour/audioregion.h>
37 #include <ardour/audio_diskstream.h>
38 #include <ardour/utils.h>
39 #include <ardour/audio_track.h>
40 #include <ardour/audioplaylist.h>
41 #include <ardour/audiofilesource.h>
42 #include <ardour/region_factory.h>
43 #include <ardour/source_factory.h>
44 #include <pbd/memento_command.h>
45
46 #include "ardour_ui.h"
47 #include "editor.h"
48 #include "sfdb_ui.h"
49 #include "editing.h"
50 #include "audio_time_axis.h"
51 #include "utils.h"
52
53 #include "i18n.h"
54
55 using namespace std;
56 using namespace ARDOUR;
57 using namespace PBD;
58 using namespace sigc;
59 using namespace Gtk;
60 using namespace Gtkmm2ext;
61 using namespace Editing;
62 using Glib::ustring;
63
64 /* Functions supporting the incorporation of external (non-captured) audio material into ardour */
65
66 void
67 Editor::add_external_audio_action (ImportMode mode)
68 {
69 }
70
71 void
72 Editor::external_audio_dialog ()
73 {
74         vector<Glib::ustring> paths;
75
76         if (session == 0) {
77                 MessageDialog msg (0, _("You can't import or embed an audiofile until you have a session loaded."));
78                 msg.run ();
79                 return;
80         }
81
82         SoundFileBrowser browser (*this, _("Add existing audio"), session, selection->tracks.size());
83
84         browser.show_all ();
85
86         int response = browser.run ();
87
88                 switch (response) {
89         case RESPONSE_OK:
90                 break;
91         default:
92                 // cancel from the browser - we are done
93                 return;
94         }
95         
96         browser.hide ();
97
98         /* lets do it */
99         
100         AudioTrack* track = 0;
101
102         if (!selection->tracks.empty()) {
103                 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(selection->tracks.front());
104                 if (atv) {
105                         track = atv->audio_track();
106                 }
107         }
108         paths = browser.get_paths ();
109
110         ImportPosition pos = browser.get_position ();
111         ImportMode mode = browser.get_mode ();
112         ImportChannel chns = browser.get_channel_disposition ();
113         nframes64_t where;
114
115         switch (pos) {
116         case ImportAtEditCursor:
117                 where = edit_cursor->current_frame;
118                 break;
119         case ImportAtTimestamp:
120                 where = -1;
121                 break;
122         case ImportAtPlayhead:
123                 where = playhead_cursor->current_frame;
124                 break;
125         case ImportAtStart:
126                 where = session->current_start_frame();
127                 break;
128         }
129
130         if (browser.import.get_active()) {
131                 do_import (paths, chns, mode, track, where);
132         } else {
133                 do_embed (paths, chns, mode, track, where);
134         }
135 }
136
137 void
138 Editor::do_import (vector<ustring> paths, ImportChannel chns, ImportMode mode, AudioTrack* track, nframes64_t& pos)
139 {
140         switch (chns) {
141         case Editing::ImportThingPerFile:
142         case Editing::ImportThingForAll:
143                 import_status.multichan = true;
144                 break;
145
146         case Editing::ImportThingPerChannel:
147                 import_status.multichan = false;
148                 break;
149         }
150
151         if (interthread_progress_window == 0) {
152                 build_interthread_progress_window ();
153         }
154
155         vector<ustring> to_import;
156
157         for (vector<ustring>::iterator a = paths.begin(); a != paths.end(); ++a) {
158
159                 to_import.clear ();
160                 to_import.push_back (*a);
161
162                 import_sndfile (to_import, mode, track, pos);
163         }
164
165         interthread_progress_window->hide_all ();
166 }
167
168 bool
169 Editor::idle_do_embed (vector<ustring> paths, ImportChannel chns, ImportMode mode, AudioTrack* track, nframes64_t& pos)
170 {
171         _do_embed (paths, chns, mode, track, pos);
172         return false;
173 }
174
175 void
176 Editor::do_embed (vector<ustring> paths, ImportChannel chns, ImportMode mode, AudioTrack* track, nframes64_t& pos)
177 {
178 #ifdef GTKOSX
179         Glib::signal_idle().connect (bind (mem_fun (*this, &Editor::idle_do_embed), paths, chns, mode, track, pos));
180 #else
181         _do_embed (paths, chns, mode, track, pos);
182 #endif
183 }
184
185 void
186 Editor::_do_embed (vector<ustring> paths, ImportChannel chns, ImportMode mode, AudioTrack* track, nframes64_t& pos)
187 {
188         bool multiple_files = paths.size() > 1;
189         bool check_sample_rate = true;
190         bool ok = false;
191         vector<ustring> to_embed;
192         
193
194         switch (chns) {
195         case Editing::ImportThingPerFile:
196         case Editing::ImportThingPerChannel:
197                 for (vector<ustring>::iterator a = paths.begin(); a != paths.end(); ++a) {
198
199                         to_embed.clear ();
200                         to_embed.push_back (*a);
201
202                         if (embed_sndfile (to_embed, chns, multiple_files, check_sample_rate, mode, track, pos) < -1) {
203                                 goto out;
204                         }
205                 }
206                 break;
207
208         case Editing::ImportThingForAll:
209                 if (embed_sndfile (paths, chns, multiple_files, check_sample_rate, mode, track, pos) < -1) {
210                         goto out;
211                 }
212                 break;
213         }
214
215         ok = true;
216         
217   out:  
218         if (ok) {
219                 session->save_state ("");
220         }
221 }
222
223 int
224 Editor::import_sndfile (vector<ustring> paths, ImportMode mode, AudioTrack* track, nframes64_t& pos)
225 {
226         WindowTitle title = string_compose (_("importing %1"), paths.front());
227
228         interthread_progress_window->set_title (title.get_string());
229         interthread_progress_window->set_position (Gtk::WIN_POS_MOUSE);
230         interthread_progress_window->show_all ();
231         interthread_progress_bar.set_fraction (0.0f);
232         interthread_cancel_label.set_text (_("Cancel Import"));
233         current_interthread_info = &import_status;
234
235         import_status.paths = paths;
236         import_status.done = false;
237         import_status.cancel = false;
238         import_status.freeze = false;
239         import_status.done = 0.0;
240         
241         interthread_progress_connection = Glib::signal_timeout().connect 
242                 (bind (mem_fun(*this, &Editor::import_progress_timeout), (gpointer) 0), 100);
243         
244         track_canvas.get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH));
245         ARDOUR_UI::instance()->flush_pending ();
246
247         /* start import thread for this spec. this will ultimately call Session::import_audiofile()
248            and if successful will add the file(s) as a region to the session region list.
249         */
250         
251         pthread_create_and_store ("import", &import_status.thread, 0, _import_thread, this);
252         pthread_detach (import_status.thread);
253         
254         while (!(import_status.done || import_status.cancel)) {
255                 gtk_main_iteration ();
256         }
257
258         interthread_progress_window->hide ();
259         
260         import_status.done = true;
261         interthread_progress_connection.disconnect ();
262         
263         /* import thread finished - see if we should build a new track */
264         
265         if (!import_status.new_regions.empty()) {
266                 boost::shared_ptr<AudioRegion> region (import_status.new_regions.front());
267                 finish_bringing_in_audio (region, region->n_channels(), region->n_channels(), track, pos, mode);
268         }
269
270         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
271         return 0;
272 }
273
274 int
275 Editor::embed_sndfile (vector<Glib::ustring> paths, Editing::ImportChannel chns, bool multiple_files, bool& check_sample_rate, ImportMode mode, 
276                        AudioTrack* track, nframes64_t& pos)
277 {
278         boost::shared_ptr<AudioFileSource> source;
279         SourceList sources;
280         boost::shared_ptr<AudioRegion> region;
281         string linked_path;
282         SoundFileInfo finfo;
283         ustring region_name;
284         uint32_t input_chan = 0;
285         uint32_t output_chan = 0;
286         int ret = 0;
287
288         track_canvas.get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH));
289         ARDOUR_UI::instance()->flush_pending ();
290
291         for (vector<Glib::ustring>::iterator p = paths.begin(); p != paths.end(); ++p) {
292
293                 ustring path = *p;
294
295                 /* lets see if we can link it into the session */
296                 
297                 linked_path = session->sound_dir();
298                 linked_path += '/';
299                 linked_path += Glib::path_get_basename (path);
300                 
301                 if (link (path.c_str(), linked_path.c_str()) == 0) {
302
303                         /* there are many reasons why link(2) might have failed.
304                            but if it succeeds, we now have a link in the
305                            session sound dir that will protect against
306                            unlinking of the original path. nice.
307                         */
308                         
309                         path = linked_path;
310
311                 } else {
312
313                         /* one possible reason is that its already linked */
314
315                         if (errno == EEXIST) {
316                                 struct stat sb;
317
318                                 if (stat (linked_path.c_str(), &sb) == 0) {
319                                         if (sb.st_nlink > 1) { // its a hard link, assume its the one we want
320                                                 path = linked_path;
321                                         }
322                                 }
323                         }
324                 }
325                 
326                 /* note that we temporarily truncated _id at the colon */
327                 
328                 string error_msg;
329
330                 if (!AudioFileSource::get_soundfile_info (path, finfo, error_msg)) {
331                         error << string_compose(_("Editor: cannot open file \"%1\", (%2)"), path, error_msg ) << endmsg;
332                         goto out;
333                 }
334                 
335                 if (check_sample_rate  && (finfo.samplerate != (int) session->frame_rate())) {
336                         vector<string> choices;
337                         
338                         if (multiple_files) {
339                                 choices.push_back (_("Cancel entire import"));
340                                 choices.push_back (_("Don't embed it"));
341                                 choices.push_back (_("Embed all without questions"));
342                         
343                                 Gtkmm2ext::Choice rate_choice (
344                                         string_compose (_("%1\nThis audiofile's sample rate doesn't match the session sample rate!"), 
345                                                         short_path (path, 40)),
346                                         choices, false);
347                                 
348                                 int resx = rate_choice.run ();
349                                 
350                                 switch (resx) {
351                                 case 0: /* stop a multi-file import */
352                                         ret = -2;
353                                         goto out;
354                                 case 1: /* don't embed this one */
355                                         ret = -1;
356                                         goto out;
357                                 case 2: /* do it, and the rest without asking */
358                                         check_sample_rate = false;
359                                         break;
360                                 case 3: /* do it */
361                                         break;
362                                 default:
363                                         ret = -2;
364                                         goto out;
365                                 }
366                         } else {
367                                 choices.push_back (_("Cancel"));
368                                 choices.push_back (_("Embed it anyway"));
369                         
370                                 Gtkmm2ext::Choice rate_choice (
371                                         string_compose (_("%1\nThis audiofile's sample rate doesn't match the session sample rate!"), path),
372                                         choices, false);
373                                 
374                                 int resx = rate_choice.run ();
375                                 
376                                 switch (resx) {
377                                 case 0: /* don't import */
378                                         ret = -1;
379                                         goto out;
380                                 case 1: /* do it */
381                                         break;
382                                 default:
383                                         ret = -2;
384                                         goto out;
385                                 }
386                         }
387                 }
388                 
389                 track_canvas.get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH));
390                 // ARDOUR_UI::instance()->flush_pending ();
391         
392                 /* make the proper number of channels in the region */
393                 
394                 input_chan += finfo.channels;
395
396                 for (int n = 0; n < finfo.channels; ++n)
397                 {
398                         try {
399
400                                 /* check if we have this thing embedded already */
401
402                                 boost::shared_ptr<Source> s;
403
404                                 if ((s = session->source_by_path_and_channel (path, n)) == 0) {
405                                         source = boost::dynamic_pointer_cast<AudioFileSource> (SourceFactory::createReadable 
406                                                                                                (*session, path,  n,
407                                                                                                 (mode == ImportAsTapeTrack ? 
408                                                                                                  AudioFileSource::Destructive : 
409                                                                                                  AudioFileSource::Flag (0))));
410                                 } else {
411                                         source = boost::dynamic_pointer_cast<AudioFileSource> (s);
412                                 }
413
414                                 sources.push_back(source);
415                         } 
416                         
417                         catch (failed_constructor& err) {
418                                 error << string_compose(_("could not open %1"), path) << endmsg;
419                                 goto out;
420                         }
421                         
422                         ARDOUR_UI::instance()->flush_pending ();
423                 }
424         }
425
426         if (sources.empty()) {
427                 goto out;
428         }
429
430
431         if (pos == -1) { // "use timestamp"
432                 if (sources[0]->natural_position() != 0) {
433                         pos = sources[0]->natural_position();
434                 } else {
435                         // XXX is this the best alternative ?
436                         pos = edit_cursor->current_frame;
437                 }
438         }
439
440         if (chns == Editing::ImportThingPerFile || chns == Editing::ImportThingForAll) {
441
442                 /* take all the sources we have and package them up as a region */
443
444                 region_name = region_name_from_path (paths.front(), (sources.size() > 1));
445                 
446                 region = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (sources, 0, sources[0]->length(), region_name, 0,
447                                                                                           Region::Flag (Region::DefaultFlags|Region::WholeFile|Region::External)));
448
449                 if (Config->get_output_auto_connect() & AutoConnectMaster) {
450                         output_chan = (session->master_out() ? session->master_out()->n_inputs() : input_chan);
451                 } else {
452                         output_chan = input_chan;
453                 }
454                 
455                 finish_bringing_in_audio (region, input_chan, output_chan, track, pos, mode);
456                 
457         } else {
458
459                 /* take each source and create a region for each one */
460
461                 SourceList just_one;
462                 SourceList::iterator x;
463                 vector<Glib::ustring>::iterator p = paths.begin();
464                 vector<Glib::ustring>::iterator next_path;
465                 
466                 for (x = sources.begin(); x != sources.end(); ++x) {
467
468                         just_one.clear ();
469                         just_one.push_back (*x);
470
471                         region_name = region_name_from_path ((*p), false);
472                         
473                         region = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (just_one, 0, (*x)->length(), region_name, 0,
474                                                                                                   Region::Flag (Region::DefaultFlags|Region::WholeFile|Region::External)));
475
476                         if (Config->get_output_auto_connect() & AutoConnectMaster) {
477                                 output_chan = (session->master_out() ? session->master_out()->n_inputs() : input_chan);
478                         } else {
479                                 output_chan = input_chan;
480                         }
481                 
482                         finish_bringing_in_audio (region, 1, output_chan, track, pos, mode);
483
484                         /* don't run out of paths */
485                         
486                         next_path = p;
487                         next_path++;
488
489                         if (next_path != paths.end()) {
490                                 p = next_path;
491                         }
492                 }
493         }
494
495         
496   out:
497         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
498         return ret;
499 }
500
501 int
502 Editor::finish_bringing_in_audio (boost::shared_ptr<AudioRegion> region, uint32_t in_chans, uint32_t out_chans, AudioTrack* track, nframes64_t& pos, ImportMode mode)
503 {
504         switch (mode) {
505         case ImportAsRegion:
506                 /* relax, its been done */
507                 break;
508                 
509         case ImportToTrack:
510                 if (track) {
511                         boost::shared_ptr<Playlist> playlist = track->diskstream()->playlist();
512                         
513                         boost::shared_ptr<AudioRegion> copy (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (region)));
514                         begin_reversible_command (_("insert sndfile"));
515                         XMLNode &before = playlist->get_state();
516                         playlist->add_region (copy, pos);
517                         session->add_command (new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
518                         commit_reversible_command ();
519
520                         pos += region->length();
521                 }
522                 break;
523                 
524         case ImportAsTrack:
525         { 
526                 list<boost::shared_ptr<AudioTrack> > at (session->new_audio_track (in_chans, out_chans, Normal, 1));
527                 if (!at.empty()) {
528                         boost::shared_ptr<AudioRegion> copy (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (region)));
529                         at.front()->set_name (basename_nosuffix (copy->name()), this);
530                         at.front()->diskstream()->playlist()->add_region (copy, pos);
531                 }
532                 break;
533         }
534
535         case ImportAsTapeTrack:
536         {
537                 list<boost::shared_ptr<AudioTrack> > at (session->new_audio_track (in_chans, out_chans, Destructive));
538                 if (!at.empty()) {
539                         boost::shared_ptr<AudioRegion> copy (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (region)));
540                         at.front()->set_name (basename_nosuffix (copy->name()), this);
541                         at.front()->diskstream()->playlist()->add_region (copy, pos);
542                 }
543                 break;
544         }
545         }
546
547         return 0;
548 }
549
550 void *
551 Editor::_import_thread (void *arg)
552 {
553         PBD::ThreadCreated (pthread_self(), X_("Import"));
554
555         Editor *ed = (Editor *) arg;
556         return ed->import_thread ();
557 }
558
559 void *
560 Editor::import_thread ()
561 {
562         session->import_audiofile (import_status);
563         pthread_exit_pbd (0);
564         /*NOTREACHED*/
565         return 0;
566 }
567
568 gint
569 Editor::import_progress_timeout (void *arg)
570 {
571         interthread_progress_label.set_text (import_status.doing_what);
572
573         if (import_status.freeze) {
574                 interthread_cancel_button.set_sensitive(false);
575         } else {
576                 interthread_cancel_button.set_sensitive(true);
577         }
578
579         if (import_status.doing_what == "building peak files") {
580                 interthread_progress_bar.pulse ();
581                 return FALSE;
582         } else {
583                 interthread_progress_bar.set_fraction (import_status.progress);
584         }
585
586         return !(import_status.done || import_status.cancel);
587 }
588