add fix handling for serializing files during import
[ardour2.git] / gtk2_ardour / sfdb_ui.cc
blob4a4548a73426eb72a2d923727c2f576211f0f99d
1 /*
2 Copyright (C) 2005-2006 Paul Davis
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.
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.
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.
20 #include <map>
21 #include <cerrno>
22 #include <sstream>
24 #include <unistd.h>
25 #include <sys/stat.h>
26 #include <sys/param.h>
28 #include <gtkmm/box.h>
29 #include <gtkmm/stock.h>
30 #include <glibmm/fileutils.h>
32 #include <pbd/convert.h>
33 #include <pbd/tokenizer.h>
34 #include <pbd/enumwriter.h>
35 #include <pbd/pthread_utils.h>
36 #include <pbd/xml++.h>
38 #include <gtkmm2ext/utils.h>
40 #include <ardour/audio_library.h>
41 #include <ardour/auditioner.h>
42 #include <ardour/audioregion.h>
43 #include <ardour/audiofilesource.h>
44 #include <ardour/region_factory.h>
45 #include <ardour/source_factory.h>
46 #include <ardour/profile.h>
48 #include "ardour_ui.h"
49 #include "editing.h"
50 #include "gui_thread.h"
51 #include "prompter.h"
52 #include "sfdb_ui.h"
53 #include "editing.h"
54 #include "utils.h"
55 #include "gain_meter.h"
57 #ifdef FREESOUND
58 #include "sfdb_freesound_mootcher.h"
59 #endif
61 #include "i18n.h"
63 using namespace ARDOUR;
64 using namespace PBD;
65 using namespace std;
66 using namespace Gtk;
67 using namespace Gtkmm2ext;
68 using namespace Editing;
70 using Glib::ustring;
72 ustring SoundFileBrowser::persistent_folder;
74 static ImportMode
75 string2importmode (string str)
77 if (str == _("as new tracks")) {
78 return ImportAsTrack;
79 } else if (str == _("to selected tracks")) {
80 return ImportToTrack;
81 } else if (str == _("to region list")) {
82 return ImportAsRegion;
83 } else if (str == _("as new tape tracks")) {
84 return ImportAsTapeTrack;
87 warning << string_compose (_("programming error: unknown import mode string %1"), str) << endmsg;
89 return ImportAsTrack;
92 static string
93 importmode2string (ImportMode mode)
95 switch (mode) {
96 case ImportAsTrack:
97 return _("as new tracks");
98 case ImportToTrack:
99 return _("to selected tracks");
100 case ImportAsRegion:
101 return _("to region list");
102 case ImportAsTapeTrack:
103 return _("as new tape tracks");
105 /*NOTREACHED*/
106 return _("as new tracks");
109 SoundFileBox::SoundFileBox (bool persistent)
110 : _session(0),
111 table (6, 2),
112 length_clock ("sfboxLengthClock", !persistent, "EditCursorClock", false, true, false),
113 timecode_clock ("sfboxTimecodeClock", !persistent, "EditCursorClock", false, false, false),
114 main_box (false, 6),
115 autoplay_btn (_("Auto-play"))
118 HBox* hbox;
119 VBox* vbox;
121 set_name (X_("SoundFileBox"));
122 set_size_request (300, -1);
124 preview_label.set_markup (_("<b>Soundfile Info</b>"));
126 border_frame.set_label_widget (preview_label);
127 border_frame.add (main_box);
129 pack_start (border_frame, true, true);
130 set_border_width (6);
132 main_box.set_border_width (6);
133 main_box.set_spacing (12);
135 length.set_text (_("Length:"));
136 timecode.set_text (_("Timestamp:"));
137 format.set_text (_("Format:"));
138 channels.set_text (_("Channels:"));
139 samplerate.set_text (_("Sample rate:"));
141 table.set_col_spacings (6);
142 table.set_homogeneous (false);
143 table.set_row_spacings (6);
145 table.attach (channels, 0, 1, 0, 1, FILL|EXPAND, (AttachOptions) 0);
146 table.attach (samplerate, 0, 1, 1, 2, FILL|EXPAND, (AttachOptions) 0);
147 table.attach (format, 0, 1, 2, 4, FILL|EXPAND, (AttachOptions) 0);
148 table.attach (length, 0, 1, 4, 5, FILL|EXPAND, (AttachOptions) 0);
149 table.attach (timecode, 0, 1, 5, 6, FILL|EXPAND, (AttachOptions) 0);
151 table.attach (channels_value, 1, 2, 0, 1, FILL, (AttachOptions) 0);
152 table.attach (samplerate_value, 1, 2, 1, 2, FILL, (AttachOptions) 0);
153 table.attach (format_text, 1, 2, 2, 4, FILL, AttachOptions (0));
154 table.attach (length_clock, 1, 2, 4, 5, FILL, (AttachOptions) 0);
155 table.attach (timecode_clock, 1, 2, 5, 6, FILL, (AttachOptions) 0);
157 length_clock.set_mode (ARDOUR_UI::instance()->secondary_clock.mode());
158 timecode_clock.set_mode (AudioClock::SMPTE);
160 hbox = manage (new HBox);
161 hbox->pack_start (table, false, false);
162 main_box.pack_start (*hbox, false, false);
164 tags_entry.set_editable (true);
165 tags_entry.signal_focus_out_event().connect (mem_fun (*this, &SoundFileBox::tags_entry_left));
166 hbox = manage (new HBox);
167 hbox->pack_start (tags_entry, true, true);
169 vbox = manage (new VBox);
171 Label* label = manage (new Label (_("Tags:")));
172 label->set_alignment (0.0f, 0.5f);
173 vbox->set_spacing (6);
174 vbox->pack_start(*label, false, false);
175 vbox->pack_start(*hbox, true, true);
177 main_box.pack_start(*vbox, true, true);
178 main_box.pack_start(bottom_box, false, false);
180 play_btn.set_image (*(manage (new Image (Stock::MEDIA_PLAY, ICON_SIZE_BUTTON))));
181 play_btn.set_label (_("Play (double click)"));
183 stop_btn.set_image (*(manage (new Image (Stock::MEDIA_STOP, ICON_SIZE_BUTTON))));
184 stop_btn.set_label (_("Stop"));
186 bottom_box.set_homogeneous (false);
187 bottom_box.set_spacing (6);
188 bottom_box.pack_start(play_btn, true, true);
189 bottom_box.pack_start(stop_btn, true, true);
190 bottom_box.pack_start(autoplay_btn, false, false);
192 play_btn.signal_clicked().connect (mem_fun (*this, &SoundFileBox::audition));
193 stop_btn.signal_clicked().connect (mem_fun (*this, &SoundFileBox::stop_audition));
195 length.set_alignment (0.0f, 0.5f);
196 format.set_alignment (0.0f, 0.5f);
197 channels.set_alignment (0.0f, 0.5f);
198 samplerate.set_alignment (0.0f, 0.5f);
199 timecode.set_alignment (0.0f, 0.5f);
201 channels_value.set_alignment (0.0f, 0.5f);
202 samplerate_value.set_alignment (0.0f, 0.5f);
205 void
206 SoundFileBox::set_session(Session* s)
208 _session = s;
210 if (!_session) {
211 play_btn.set_sensitive (false);
212 stop_btn.set_sensitive (false);
216 length_clock.set_session (s);
217 timecode_clock.set_session (s);
220 bool
221 SoundFileBox::setup_labels (const ustring& filename)
223 if (!path.empty()) {
224 // save existing tags
225 tags_changed ();
228 path = filename;
230 string error_msg;
232 if(!AudioFileSource::get_soundfile_info (filename, sf_info, error_msg)) {
234 preview_label.set_markup (_("<b>Soundfile Info</b>"));
235 format_text.set_text (_("n/a"));
236 channels_value.set_text (_("n/a"));
237 samplerate_value.set_text (_("n/a"));
238 tags_entry.get_buffer()->set_text ("");
240 length_clock.set (0);
241 timecode_clock.set (0);
243 tags_entry.set_sensitive (false);
244 play_btn.set_sensitive (false);
246 return false;
249 preview_label.set_markup (string_compose ("<b>%1</b>", Glib::path_get_basename (filename)));
250 format_text.set_text (sf_info.format_name);
251 channels_value.set_text (to_string (sf_info.channels, std::dec));
253 if (_session && sf_info.samplerate != _session->frame_rate()) {
254 samplerate.set_markup (string_compose ("<b>%1</b>", _("Sample rate:")));
255 samplerate_value.set_markup (string_compose (X_("<b>%1 Hz</b>"), sf_info.samplerate));
256 samplerate_value.set_name ("NewSessionSR1Label");
257 samplerate.set_name ("NewSessionSR1Label");
258 } else {
259 samplerate.set_text (_("Sample rate:"));
260 samplerate_value.set_text (string_compose (X_("%1 Hz"), sf_info.samplerate));
261 samplerate_value.set_name ("NewSessionSR2Label");
262 samplerate.set_name ("NewSessionSR2Label");
265 double src_coef = (double) _session->nominal_frame_rate() / sf_info.samplerate;
267 length_clock.set (sf_info.length * src_coef + 0.5, true);
268 timecode_clock.set (sf_info.timecode * src_coef + 0.5, true);
270 // this is a hack that is fixed in trunk, i think (august 26th, 2007)
272 vector<string> tags = Library->get_tags (string ("//") + filename);
274 stringstream tag_string;
275 for (vector<string>::iterator i = tags.begin(); i != tags.end(); ++i) {
276 if (i != tags.begin()) {
277 tag_string << ", ";
279 tag_string << *i;
281 tags_entry.get_buffer()->set_text (tag_string.str());
283 tags_entry.set_sensitive (true);
284 if (_session) {
285 play_btn.set_sensitive (true);
288 return true;
291 bool
292 SoundFileBox::autoplay() const
294 return autoplay_btn.get_active();
297 bool
298 SoundFileBox::audition_oneshot()
300 audition ();
301 return false;
304 void
305 SoundFileBox::audition ()
307 if (!_session) {
308 return;
311 _session->cancel_audition();
313 if (!Glib::file_test (path, Glib::FILE_TEST_EXISTS)) {
314 warning << string_compose(_("Could not read file: %1 (%2)."), path, strerror(errno)) << endmsg;
315 return;
318 boost::shared_ptr<Region> r;
319 SourceList srclist;
320 boost::shared_ptr<AudioFileSource> afs;
321 bool old_sbp = AudioSource::get_build_peakfiles ();
323 /* don't even think of building peakfiles for these files */
325 AudioSource::set_build_peakfiles (false);
327 for (int n = 0; n < sf_info.channels; ++n) {
328 try {
329 afs = boost::dynamic_pointer_cast<AudioFileSource> (SourceFactory::createReadable (*_session, path, n, AudioFileSource::Flag (0), false));
331 srclist.push_back(afs);
333 } catch (failed_constructor& err) {
334 error << _("Could not access soundfile: ") << path << endmsg;
335 AudioSource::set_build_peakfiles (old_sbp);
336 return;
340 AudioSource::set_build_peakfiles (old_sbp);
342 if (srclist.empty()) {
343 return;
346 afs = boost::dynamic_pointer_cast<AudioFileSource> (srclist[0]);
347 string rname = region_name_from_path (afs->path(), false);
348 r = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (srclist, 0, srclist[0]->length(), rname, 0, Region::DefaultFlags, false));
350 _session->audition_region(r);
353 void
354 SoundFileBox::stop_audition ()
356 if (_session) {
357 _session->cancel_audition();
361 bool
362 SoundFileBox::tags_entry_left (GdkEventFocus *ev)
364 tags_changed ();
365 return false;
368 void
369 SoundFileBox::tags_changed ()
371 string tag_string = tags_entry.get_buffer()->get_text ();
373 if (tag_string.empty()) {
374 return;
377 vector<string> tags;
379 if (!PBD::tokenize (tag_string, string(",\n"), std::back_inserter (tags), true)) {
380 warning << _("SoundFileBox: Could not tokenize string: ") << tag_string << endmsg;
381 return;
384 save_tags (tags);
387 void
388 SoundFileBox::save_tags (const vector<string>& tags)
390 Library->set_tags (string ("//") + path, tags);
391 Library->save_changes ();
394 SoundFileBrowser::SoundFileBrowser (Gtk::Window& parent, string title, ARDOUR::Session* s, bool persistent)
395 : ArdourDialog (parent, title, false, false),
396 found_list (ListStore::create(found_list_columns)),
397 freesound_list (ListStore::create(freesound_list_columns)),
398 chooser (FILE_CHOOSER_ACTION_OPEN),
399 preview (persistent),
400 found_search_btn (_("Search")),
401 found_list_view (found_list),
402 freesound_search_btn (_("Start Downloading")),
403 freesound_list_view (freesound_list)
405 resetting_ourselves = false;
406 gm = 0;
408 if (ARDOUR::Profile->get_sae()) {
409 chooser.add_shortcut_folder_uri("file:///Library/GarageBand/Apple Loops");
410 chooser.add_shortcut_folder_uri("file:///Library/Application Support/GarageBand/Instrument Library/Sampler/Sampler Files");
413 #ifdef GTKOSX
414 chooser.add_shortcut_folder_uri("file:///Volumes");
415 #endif
417 //add the file chooser
419 chooser.set_border_width (12);
420 custom_filter.add_custom (FILE_FILTER_FILENAME, mem_fun(*this, &SoundFileBrowser::on_custom));
421 custom_filter.set_name (_("Audio files"));
423 matchall_filter.add_pattern ("*.*");
424 matchall_filter.set_name (_("All files"));
426 chooser.add_filter (custom_filter);
427 chooser.add_filter (matchall_filter);
428 chooser.set_select_multiple (true);
429 chooser.signal_update_preview().connect(mem_fun(*this, &SoundFileBrowser::update_preview));
430 chooser.signal_file_activated().connect (mem_fun (*this, &SoundFileBrowser::chooser_file_activated));
431 #ifdef GTKOSX
432 /* some broken redraw behaviour - this is a bandaid */
433 chooser.signal_selection_changed().connect (mem_fun (chooser, &Widget::queue_draw));
434 #endif
436 if (!persistent_folder.empty()) {
437 chooser.set_current_folder (persistent_folder);
439 notebook.append_page (chooser, _("Browse Files"));
442 hpacker.set_spacing (6);
443 hpacker.pack_start (notebook, true, true);
444 hpacker.pack_start (preview, false, false);
446 get_vbox()->pack_start (hpacker, true, true);
448 //add tag search
450 VBox* vbox;
451 HBox* hbox;
454 hbox = manage(new HBox);
455 hbox->pack_start (found_entry);
456 hbox->pack_start (found_search_btn);
458 Gtk::ScrolledWindow *scroll = manage(new ScrolledWindow);
459 scroll->add(found_list_view);
460 scroll->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
462 vbox = manage(new VBox);
463 vbox->pack_start (*hbox, PACK_SHRINK);
464 vbox->pack_start (*scroll);
466 found_list_view.append_column(_("Paths"), found_list_columns.pathname);
468 found_list_view.get_selection()->signal_changed().connect(mem_fun(*this, &SoundFileBrowser::found_list_view_selected));
470 found_list_view.signal_row_activated().connect (mem_fun (*this, &SoundFileBrowser::found_list_view_activated));
472 found_search_btn.signal_clicked().connect(mem_fun(*this, &SoundFileBrowser::found_search_clicked));
473 found_entry.signal_activate().connect(mem_fun(*this, &SoundFileBrowser::found_search_clicked));
475 notebook.append_page (*vbox, _("Search Tags"));
478 //add freesound search
479 #ifdef FREESOUND
481 VBox* vbox;
482 HBox* passbox;
483 Label* label;
485 passbox = manage(new HBox);
486 passbox->set_border_width (12);
487 passbox->set_spacing (6);
489 label = manage (new Label);
490 label->set_text (_("User:"));
491 passbox->pack_start (*label, false, false);
492 passbox->pack_start (freesound_name_entry);
493 label = manage (new Label);
494 label->set_text (_("Password:"));
495 passbox->pack_start (*label, false, false);
496 passbox->pack_start (freesound_pass_entry);
497 label = manage (new Label);
498 label->set_text (_("Tags:"));
499 passbox->pack_start (*label, false, false);
500 passbox->pack_start (freesound_entry, false, false);
501 passbox->pack_start (freesound_search_btn, false, false);
503 Gtk::ScrolledWindow *scroll = manage(new ScrolledWindow);
504 scroll->add(freesound_list_view);
505 scroll->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
507 vbox = manage(new VBox);
508 vbox->pack_start (*passbox, PACK_SHRINK);
509 vbox->pack_start(*scroll);
511 //vbox->pack_start (freesound_list_view);
513 freesound_list_view.append_column(_("Paths"), freesound_list_columns.pathname);
515 freesound_list_view.get_selection()->signal_changed().connect(mem_fun(*this, &SoundFileBrowser::freesound_list_view_selected));
517 //freesound_list_view.get_selection()->set_mode (SELECTION_MULTIPLE);
518 freesound_list_view.signal_row_activated().connect (mem_fun (*this, &SoundFileBrowser::freesound_list_view_activated));
520 freesound_search_btn.signal_clicked().connect(mem_fun(*this, &SoundFileBrowser::freesound_search_clicked));
521 freesound_entry.signal_activate().connect(mem_fun(*this, &SoundFileBrowser::freesound_search_clicked));
523 notebook.append_page (*vbox, _("Search Freesound"));
525 #endif
528 notebook.set_size_request (500, -1);
530 set_session (s);
532 add_button (Stock::CANCEL, RESPONSE_CANCEL);
533 add_button (Stock::APPLY, RESPONSE_APPLY);
534 add_button (Stock::OK, RESPONSE_OK);
538 SoundFileBrowser::~SoundFileBrowser ()
540 persistent_folder = chooser.get_current_folder();
544 void
545 SoundFileBrowser::on_show ()
547 ArdourDialog::on_show ();
548 start_metering ();
551 void
552 SoundFileBrowser::clear_selection ()
554 chooser.unselect_all ();
555 found_list_view.get_selection()->unselect_all ();
558 void
559 SoundFileBrowser::chooser_file_activated ()
561 preview.audition ();
564 void
565 SoundFileBrowser::found_list_view_activated (const TreeModel::Path& path, TreeViewColumn* col)
567 preview.audition ();
570 void
571 SoundFileBrowser::freesound_list_view_activated (const TreeModel::Path& path, TreeViewColumn* col)
573 preview.audition ();
576 void
577 SoundFileBrowser::set_session (Session* s)
579 ArdourDialog::set_session (s);
580 preview.set_session (s);
581 if (s) {
582 add_gain_meter ();
583 } else {
584 remove_gain_meter ();
588 void
589 SoundFileBrowser::add_gain_meter ()
591 if (gm) {
592 delete gm;
595 gm = new GainMeter (*session);
596 gm->set_io (session->the_auditioner());
598 meter_packer.set_border_width (12);
599 meter_packer.pack_start (*gm, false, true);
600 hpacker.pack_end (meter_packer, false, false);
601 meter_packer.show_all ();
602 start_metering ();
605 void
606 SoundFileBrowser::remove_gain_meter ()
608 if (gm) {
609 meter_packer.remove (*gm);
610 hpacker.remove (meter_packer);
611 delete gm;
612 gm = 0;
616 void
617 SoundFileBrowser::start_metering ()
619 metering_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (mem_fun(*this, &SoundFileBrowser::meter));
622 void
623 SoundFileBrowser::stop_metering ()
625 metering_connection.disconnect();
628 void
629 SoundFileBrowser::meter ()
631 if (is_mapped () && session && gm) {
632 gm->update_meters ();
636 bool
637 SoundFileBrowser::on_custom (const FileFilter::Info& filter_info)
639 return AudioFileSource::safe_file_extension (filter_info.filename);
642 void
643 SoundFileBrowser::update_preview ()
645 if (preview.setup_labels (chooser.get_filename())) {
646 if (preview.autoplay()) {
647 Glib::signal_idle().connect (mem_fun (preview, &SoundFileBox::audition_oneshot));
652 void
653 SoundFileBrowser::found_list_view_selected ()
655 if (!reset_options ()) {
656 set_response_sensitive (RESPONSE_OK, false);
657 } else {
658 ustring file;
660 TreeView::Selection::ListHandle_Path rows = found_list_view.get_selection()->get_selected_rows ();
662 if (!rows.empty()) {
663 TreeIter iter = found_list->get_iter(*rows.begin());
664 file = (*iter)[found_list_columns.pathname];
665 chooser.set_filename (file);
666 set_response_sensitive (RESPONSE_OK, true);
667 } else {
668 set_response_sensitive (RESPONSE_OK, false);
671 preview.setup_labels (file);
675 void
676 SoundFileBrowser::freesound_list_view_selected ()
678 if (!reset_options ()) {
679 set_response_sensitive (RESPONSE_OK, false);
680 } else {
681 ustring file;
683 TreeView::Selection::ListHandle_Path rows = freesound_list_view.get_selection()->get_selected_rows ();
685 if (!rows.empty()) {
686 TreeIter iter = freesound_list->get_iter(*rows.begin());
687 file = (*iter)[freesound_list_columns.pathname];
688 chooser.set_filename (file);
689 set_response_sensitive (RESPONSE_OK, true);
690 } else {
691 set_response_sensitive (RESPONSE_OK, false);
694 preview.setup_labels (file);
698 void
699 SoundFileBrowser::found_search_clicked ()
701 string tag_string = found_entry.get_text ();
703 vector<string> tags;
705 if (!PBD::tokenize (tag_string, string(","), std::back_inserter (tags), true)) {
706 warning << _("SoundFileBrowser: Could not tokenize string: ") << tag_string << endmsg;
707 return;
710 vector<string> results;
711 Library->search_members_and (results, tags);
713 found_list->clear();
714 for (vector<string>::iterator i = results.begin(); i != results.end(); ++i) {
715 TreeModel::iterator new_row = found_list->append();
716 TreeModel::Row row = *new_row;
717 string path = Glib::filename_from_uri (string ("file:") + *i);
718 row[found_list_columns.pathname] = path;
722 void*
723 freesound_search_thread_entry (void* arg)
725 PBD::notify_gui_about_thread_creation (pthread_self(), X_("Freesound Search"));
727 static_cast<SoundFileBrowser*>(arg)->freesound_search_thread ();
729 return 0;
732 bool searching = false;
733 bool canceling = false;
735 void
736 SoundFileBrowser::freesound_search_clicked ()
738 if (canceling) //already canceling, button does nothing
739 return;
741 if ( searching ) {
742 freesound_search_btn.set_label(_("Cancelling.."));
743 canceling = true;
744 } else {
745 searching = true;
746 freesound_search_btn.set_label(_("Cancel"));
747 pthread_t freesound_thr;
748 pthread_create_and_store ("freesound_search", &freesound_thr, 0, freesound_search_thread_entry, this);
752 void
753 SoundFileBrowser::freesound_search_thread()
755 #ifdef FREESOUND
756 freesound_list->clear();
758 string path;
759 path = Glib::get_home_dir();
760 path += "/Freesound/";
761 Mootcher theMootcher(path.c_str());
763 string name_string = freesound_name_entry.get_text ();
764 string pass_string = freesound_pass_entry.get_text ();
765 string search_string = freesound_entry.get_text ();
767 if ( theMootcher.doLogin( name_string, pass_string ) ) {
769 string theString = theMootcher.searchText(search_string);
771 XMLTree doc;
772 doc.read_buffer( theString );
773 XMLNode *root = doc.root();
775 if (root==NULL) return;
777 if ( strcmp(root->name().c_str(), "freesound") == 0) {
779 XMLNode *node = 0;
780 XMLNodeList children = root->children();
781 XMLNodeConstIterator niter;
782 for (niter = children.begin(); niter != children.end() && !canceling; ++niter) {
783 node = *niter;
784 if( strcmp( node->name().c_str(), "sample") == 0 ){
785 XMLProperty *prop=node->property ("id");
786 string filename = theMootcher.getFile( prop->value().c_str() );
787 if ( filename != "" ) {
788 TreeModel::iterator new_row = freesound_list->append();
789 TreeModel::Row row = *new_row;
790 string path = Glib::filename_from_uri (string ("file:") + filename);
791 row[freesound_list_columns.pathname] = path;
798 searching = false;
799 canceling = false;
800 freesound_search_btn.set_label(_("Start Downloading"));
801 #endif
804 vector<ustring>
805 SoundFileBrowser::get_paths ()
807 vector<ustring> results;
809 int n = notebook.get_current_page ();
811 if (n == 0) {
812 vector<ustring> filenames = chooser.get_filenames();
813 vector<ustring>::iterator i;
815 for (i = filenames.begin(); i != filenames.end(); ++i) {
816 struct stat buf;
817 if ((!stat((*i).c_str(), &buf)) && S_ISREG(buf.st_mode)) {
818 results.push_back (*i);
822 } else if (n==1){
824 typedef TreeView::Selection::ListHandle_Path ListPath;
826 ListPath rows = found_list_view.get_selection()->get_selected_rows ();
827 for (ListPath::iterator i = rows.begin() ; i != rows.end(); ++i) {
828 TreeIter iter = found_list->get_iter(*i);
829 ustring str = (*iter)[found_list_columns.pathname];
831 results.push_back (str);
833 } else {
835 typedef TreeView::Selection::ListHandle_Path ListPath;
837 ListPath rows = freesound_list_view.get_selection()->get_selected_rows ();
838 for (ListPath::iterator i = rows.begin() ; i != rows.end(); ++i) {
839 TreeIter iter = freesound_list->get_iter(*i);
840 ustring str = (*iter)[freesound_list_columns.pathname];
842 results.push_back (str);
846 return results;
849 void
850 SoundFileOmega::reset_options_noret ()
852 if (!resetting_ourselves) {
853 (void) reset_options ();
857 bool
858 SoundFileOmega::reset_options ()
860 vector<ustring> paths = get_paths ();
862 if (paths.empty()) {
864 channel_combo.set_sensitive (false);
865 action_combo.set_sensitive (false);
866 where_combo.set_sensitive (false);
867 copy_files_btn.set_sensitive (false);
869 return false;
871 } else {
873 channel_combo.set_sensitive (true);
874 action_combo.set_sensitive (true);
875 where_combo.set_sensitive (true);
877 /* if we get through this function successfully, this may be
878 reset at the end, once we know if we can use hard links
879 to do embedding
882 if (Config->get_only_copy_imported_files()) {
883 copy_files_btn.set_sensitive (false);
884 } else {
885 copy_files_btn.set_sensitive (false);
889 bool same_size;
890 bool src_needed;
891 bool selection_includes_multichannel;
892 bool selection_can_be_embedded_with_links = check_link_status (*session, paths);
893 ImportMode mode;
895 if (check_info (paths, same_size, src_needed, selection_includes_multichannel)) {
896 Glib::signal_idle().connect (mem_fun (*this, &SoundFileOmega::bad_file_message));
897 return false;
900 ustring existing_choice;
901 vector<string> action_strings;
903 if (selected_track_cnt > 0) {
904 if (channel_combo.get_active_text().length()) {
905 ImportDisposition id = get_channel_disposition();
907 switch (id) {
908 case Editing::ImportDistinctFiles:
909 if (selected_track_cnt == paths.size()) {
910 action_strings.push_back (importmode2string (ImportToTrack));
912 break;
914 case Editing::ImportDistinctChannels:
915 /* XXX it would be nice to allow channel-per-selected track
916 but its too hard we don't want to deal with all the
917 different per-file + per-track channel configurations.
919 break;
921 default:
922 action_strings.push_back (importmode2string (ImportToTrack));
923 break;
928 action_strings.push_back (importmode2string (ImportAsTrack));
929 action_strings.push_back (importmode2string (ImportAsRegion));
930 action_strings.push_back (importmode2string (ImportAsTapeTrack));
932 resetting_ourselves = true;
934 existing_choice = action_combo.get_active_text();
936 set_popdown_strings (action_combo, action_strings);
938 /* preserve any existing choice, if possible */
941 if (existing_choice.length()) {
942 vector<string>::iterator x;
943 for (x = action_strings.begin(); x != action_strings.end(); ++x) {
944 if (*x == existing_choice) {
945 action_combo.set_active_text (existing_choice);
946 break;
949 if (x == action_strings.end()) {
950 action_combo.set_active_text (action_strings.front());
952 } else {
953 action_combo.set_active_text (action_strings.front());
956 resetting_ourselves = false;
958 if ((mode = get_mode()) == ImportAsRegion) {
959 where_combo.set_sensitive (false);
960 } else {
961 where_combo.set_sensitive (true);
964 vector<string> channel_strings;
966 if (mode == ImportAsTrack || mode == ImportAsTapeTrack || mode == ImportToTrack) {
967 channel_strings.push_back (_("one track per file"));
969 if (selection_includes_multichannel) {
970 channel_strings.push_back (_("one track per channel"));
973 if (paths.size() > 1) {
974 /* tape tracks are a single region per track, so we cannot
975 sequence multiple files.
977 if (mode != ImportAsTapeTrack) {
978 channel_strings.push_back (_("sequence files"));
980 if (same_size) {
981 channel_strings.push_back (_("all files in one track"));
986 } else {
987 channel_strings.push_back (_("one region per file"));
989 if (selection_includes_multichannel) {
990 channel_strings.push_back (_("one region per channel"));
993 if (paths.size() > 1) {
994 if (same_size) {
995 channel_strings.push_back (_("all files in one region"));
1000 existing_choice = channel_combo.get_active_text();
1002 set_popdown_strings (channel_combo, channel_strings);
1004 /* preserve any existing choice, if possible */
1006 if (existing_choice.length()) {
1007 vector<string>::iterator x;
1008 for (x = channel_strings.begin(); x != channel_strings.end(); ++x) {
1009 if (*x == existing_choice) {
1010 channel_combo.set_active_text (existing_choice);
1011 break;
1014 if (x == channel_strings.end()) {
1015 channel_combo.set_active_text (channel_strings.front());
1017 } else {
1018 channel_combo.set_active_text (channel_strings.front());
1021 if (src_needed) {
1022 src_combo.set_sensitive (true);
1023 } else {
1024 src_combo.set_sensitive (false);
1027 if (Config->get_only_copy_imported_files()) {
1029 if (selection_can_be_embedded_with_links) {
1030 copy_files_btn.set_sensitive (true);
1031 } else {
1032 copy_files_btn.set_sensitive (false);
1035 } else {
1037 copy_files_btn.set_sensitive (true);
1040 return true;
1044 bool
1045 SoundFileOmega::bad_file_message()
1047 MessageDialog msg (*this,
1048 _("One or more of the selected files\ncannot be used by Ardour"),
1049 true,
1050 Gtk::MESSAGE_INFO,
1051 Gtk::BUTTONS_OK);
1052 msg.run ();
1053 resetting_ourselves = true;
1054 chooser.unselect_uri (chooser.get_preview_uri());
1055 resetting_ourselves = false;
1057 return false;
1060 bool
1061 SoundFileOmega::check_info (const vector<ustring>& paths, bool& same_size, bool& src_needed, bool& multichannel)
1063 SoundFileInfo info;
1064 nframes64_t sz = 0;
1065 bool err = false;
1066 string errmsg;
1068 same_size = true;
1069 src_needed = false;
1070 multichannel = false;
1072 for (vector<ustring>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
1074 if (!AudioFileSource::get_soundfile_info (*i, info, errmsg)) {
1075 err = true;
1078 if (info.channels > 1) {
1079 multichannel = true;
1082 if (sz == 0) {
1083 sz = info.length;
1084 } else {
1085 if (sz != info.length) {
1086 same_size = false;
1090 if ((nframes_t) info.samplerate != session->frame_rate()) {
1091 src_needed = true;
1095 return err;
1099 bool
1100 SoundFileOmega::check_link_status (const Session& s, const vector<ustring>& paths)
1102 string tmpdir = s.sound_dir();
1103 bool ret = false;
1105 tmpdir += "/linktest";
1107 if (mkdir (tmpdir.c_str(), 0744)) {
1108 if (errno != EEXIST) {
1109 return false;
1113 for (vector<ustring>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
1115 char tmpc[MAXPATHLEN+1];
1117 snprintf (tmpc, sizeof(tmpc), "%s/%s", tmpdir.c_str(), Glib::path_get_basename (*i).c_str());
1119 /* can we link ? */
1121 if (link ((*i).c_str(), tmpc)) {
1122 goto out;
1125 unlink (tmpc);
1128 ret = true;
1130 out:
1131 rmdir (tmpdir.c_str());
1132 return ret;
1135 SoundFileChooser::SoundFileChooser (Gtk::Window& parent, string title, ARDOUR::Session* s)
1136 : SoundFileBrowser (parent, title, s, false)
1138 chooser.set_select_multiple (false);
1139 found_list_view.get_selection()->set_mode (SELECTION_SINGLE);
1140 freesound_list_view.get_selection()->set_mode (SELECTION_SINGLE);
1143 void
1144 SoundFileChooser::on_hide ()
1146 ArdourDialog::on_hide();
1147 stop_metering ();
1149 if (session) {
1150 session->cancel_audition();
1154 ustring
1155 SoundFileChooser::get_filename ()
1157 vector<ustring> paths;
1159 paths = get_paths ();
1161 if (paths.empty()) {
1162 return ustring ();
1165 if (!Glib::file_test (paths.front(), Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
1166 return ustring();
1169 return paths.front();
1172 SoundFileOmega::SoundFileOmega (Gtk::Window& parent, string title, ARDOUR::Session* s, int selected_tracks, bool persistent,
1173 Editing::ImportMode mode_hint)
1174 : SoundFileBrowser (parent, title, s, persistent),
1175 copy_files_btn ( _("Copy files to session")),
1176 selected_track_cnt (selected_tracks)
1178 VBox* vbox;
1179 HBox* hbox;
1180 vector<string> str;
1182 set_size_request (-1, 450);
1184 block_two.set_border_width (12);
1185 block_three.set_border_width (12);
1186 block_four.set_border_width (12);
1188 options.set_spacing (12);
1190 str.clear ();
1191 str.push_back (_("use file timestamp"));
1192 str.push_back (_("at edit point"));
1193 str.push_back (_("at playhead"));
1194 str.push_back (_("at session start"));
1195 set_popdown_strings (where_combo, str);
1196 where_combo.set_active_text (str.front());
1198 Label* l = manage (new Label);
1199 l->set_text (_("Add files:"));
1201 hbox = manage (new HBox);
1202 hbox->set_border_width (12);
1203 hbox->set_spacing (6);
1204 hbox->pack_start (*l, false, false);
1205 hbox->pack_start (action_combo, false, false);
1206 vbox = manage (new VBox);
1207 vbox->pack_start (*hbox, false, false);
1208 options.pack_start (*vbox, false, false);
1210 /* dummy entry for action combo so that it doesn't look odd if we
1211 come up with no tracks selected.
1214 str.clear ();
1215 str.push_back (importmode2string (mode_hint));
1216 set_popdown_strings (action_combo, str);
1217 action_combo.set_active_text (str.front());
1218 action_combo.set_sensitive (false);
1220 l = manage (new Label);
1221 l->set_text (_("Insert:"));
1223 hbox = manage (new HBox);
1224 hbox->set_border_width (12);
1225 hbox->set_spacing (6);
1226 hbox->pack_start (*l, false, false);
1227 hbox->pack_start (where_combo, false, false);
1228 vbox = manage (new VBox);
1229 vbox->pack_start (*hbox, false, false);
1230 options.pack_start (*vbox, false, false);
1233 l = manage (new Label);
1234 l->set_text (_("Mapping:"));
1236 hbox = manage (new HBox);
1237 hbox->set_border_width (12);
1238 hbox->set_spacing (6);
1239 hbox->pack_start (*l, false, false);
1240 hbox->pack_start (channel_combo, false, false);
1241 vbox = manage (new VBox);
1242 vbox->pack_start (*hbox, false, false);
1243 options.pack_start (*vbox, false, false);
1245 str.clear ();
1246 str.push_back (_("one track per file"));
1247 set_popdown_strings (channel_combo, str);
1248 channel_combo.set_active_text (str.front());
1249 channel_combo.set_sensitive (false);
1251 l = manage (new Label);
1252 l->set_text (_("Conversion Quality:"));
1254 hbox = manage (new HBox);
1255 hbox->set_border_width (12);
1256 hbox->set_spacing (6);
1257 hbox->pack_start (*l, false, false);
1258 hbox->pack_start (src_combo, false, false);
1259 vbox = manage (new VBox);
1260 vbox->pack_start (*hbox, false, false);
1261 options.pack_start (*vbox, false, false);
1263 str.clear ();
1264 str.push_back (_("Best"));
1265 str.push_back (_("Good"));
1266 str.push_back (_("Quick"));
1267 str.push_back (_("Fast"));
1268 str.push_back (_("Fastest"));
1270 set_popdown_strings (src_combo, str);
1271 src_combo.set_active_text (str.front());
1272 src_combo.set_sensitive (false);
1274 reset_options ();
1276 action_combo.signal_changed().connect (mem_fun (*this, &SoundFileOmega::reset_options_noret));
1278 copy_files_btn.set_active (true);
1280 block_four.pack_start (copy_files_btn, false, false);
1282 options.pack_start (block_four, false, false);
1284 get_vbox()->pack_start (options, false, false);
1286 /* setup disposition map */
1288 disposition_map.insert (pair<ustring,ImportDisposition>(_("one track per file"), ImportDistinctFiles));
1289 disposition_map.insert (pair<ustring,ImportDisposition>(_("one track per channel"), ImportDistinctChannels));
1290 disposition_map.insert (pair<ustring,ImportDisposition>(_("merge files"), ImportMergeFiles));
1291 disposition_map.insert (pair<ustring,ImportDisposition>(_("sequence files"), ImportSerializeFiles));
1293 disposition_map.insert (pair<ustring,ImportDisposition>(_("one region per file"), ImportDistinctFiles));
1294 disposition_map.insert (pair<ustring,ImportDisposition>(_("one region per channel"), ImportDistinctChannels));
1295 disposition_map.insert (pair<ustring,ImportDisposition>(_("all files in one region"), ImportMergeFiles));
1296 disposition_map.insert (pair<ustring,ImportDisposition>(_("all files in one track"), ImportSerializeFiles));
1298 chooser.signal_selection_changed().connect (mem_fun (*this, &SoundFileOmega::file_selection_changed));
1300 /* set size requests for a couple of combos to allow them to display the longest text
1301 they will ever be asked to display. This prevents them being resized when the user
1302 selects a file to import, which in turn prevents the size of the dialog from jumping
1303 around. */
1305 vector<string> t;
1306 t.push_back (_("one track per file"));
1307 t.push_back (_("one track per channel"));
1308 t.push_back (_("sequence files"));
1309 t.push_back (_("all files in one region"));
1310 set_size_request_to_display_given_text (channel_combo, t, COMBO_FUDGE + 10, 15);
1312 t.clear ();
1313 t.push_back (importmode2string (ImportAsTrack));
1314 t.push_back (importmode2string (ImportToTrack));
1315 t.push_back (importmode2string (ImportAsRegion));
1316 t.push_back (importmode2string (ImportAsTapeTrack));
1317 set_size_request_to_display_given_text (action_combo, t, COMBO_FUDGE + 10, 15);
1320 void
1321 SoundFileOmega::set_mode (ImportMode mode)
1323 action_combo.set_active_text (importmode2string (mode));
1326 ImportMode
1327 SoundFileOmega::get_mode () const
1329 return string2importmode (action_combo.get_active_text());
1332 void
1333 SoundFileOmega::on_hide ()
1335 ArdourDialog::on_hide();
1336 if (session) {
1337 session->cancel_audition();
1341 ImportPosition
1342 SoundFileOmega::get_position() const
1344 ustring str = where_combo.get_active_text();
1346 if (str == _("use file timestamp")) {
1347 return ImportAtTimestamp;
1348 } else if (str == _("at edit point")) {
1349 return ImportAtEditPoint;
1350 } else if (str == _("at playhead")) {
1351 return ImportAtPlayhead;
1352 } else {
1353 return ImportAtStart;
1357 SrcQuality
1358 SoundFileOmega::get_src_quality() const
1360 ustring str = where_combo.get_active_text();
1362 if (str == _("Best")) {
1363 return SrcBest;
1364 } else if (str == _("Good")) {
1365 return SrcGood;
1366 } else if (str == _("Quick")) {
1367 return SrcQuick;
1368 } else if (str == _("Fast")) {
1369 return SrcFast;
1370 } else {
1371 return SrcFastest;
1375 ImportDisposition
1376 SoundFileOmega::get_channel_disposition () const
1378 /* we use a map here because the channel combo can contain different strings
1379 depending on the state of the other combos. the map contains all possible strings
1380 and the ImportDisposition enum that corresponds to it.
1383 ustring str = channel_combo.get_active_text();
1384 DispositionMap::const_iterator x = disposition_map.find (str);
1386 if (x == disposition_map.end()) {
1387 fatal << string_compose (_("programming error: %1 (%2)"), "unknown string for import disposition", str) << endmsg;
1388 /*NOTREACHED*/
1391 return x->second;
1394 void
1395 SoundFileOmega::reset (int selected_tracks)
1397 selected_track_cnt = selected_tracks;
1398 reset_options ();
1401 void
1402 SoundFileOmega::file_selection_changed ()
1404 if (resetting_ourselves) {
1405 return;
1408 if (!reset_options ()) {
1409 set_response_sensitive (RESPONSE_OK, false);
1410 } else {
1411 if (chooser.get_filenames().size() > 0) {
1412 set_response_sensitive (RESPONSE_OK, true);
1413 } else {
1414 set_response_sensitive (RESPONSE_OK, false);