Remove idiocy.
[ardour2.git] / gtk2_ardour / sfdb_ui.cc
blobd4c294ae29aa013caa2d5c5fb55507aa79bd0f34
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 "evoral/SMF.hpp"
42 #include "ardour/amp.h"
43 #include "ardour/audio_library.h"
44 #include "ardour/auditioner.h"
45 #include "ardour/audioregion.h"
46 #include "ardour/audiofilesource.h"
47 #include "ardour/smf_source.h"
48 #include "ardour/region_factory.h"
49 #include "ardour/source_factory.h"
50 #include "ardour/session.h"
51 #include "ardour/session_directory.h"
52 #include "ardour/profile.h"
54 #include "ardour_ui.h"
55 #include "editing.h"
56 #include "gui_thread.h"
57 #include "prompter.h"
58 #include "sfdb_ui.h"
59 #include "editing.h"
60 #include "utils.h"
61 #include "gain_meter.h"
63 #ifdef FREESOUND
64 #include "sfdb_freesound_mootcher.h"
65 #endif
67 #include "i18n.h"
69 using namespace ARDOUR;
70 using namespace PBD;
71 using namespace std;
72 using namespace Gtk;
73 using namespace Gtkmm2ext;
74 using namespace Editing;
76 using Glib::ustring;
78 ustring SoundFileBrowser::persistent_folder;
80 static ImportMode
81 string2importmode (string str)
83 if (str == _("as new tracks")) {
84 return ImportAsTrack;
85 } else if (str == _("to selected tracks")) {
86 return ImportToTrack;
87 } else if (str == _("to region list")) {
88 return ImportAsRegion;
89 } else if (str == _("as new tape tracks")) {
90 return ImportAsTapeTrack;
93 warning << string_compose (_("programming error: unknown import mode string %1"), str) << endmsg;
95 return ImportAsTrack;
98 static string
99 importmode2string (ImportMode mode)
101 switch (mode) {
102 case ImportAsTrack:
103 return _("as new tracks");
104 case ImportToTrack:
105 return _("to selected tracks");
106 case ImportAsRegion:
107 return _("to region list");
108 case ImportAsTapeTrack:
109 return _("as new tape tracks");
111 /*NOTREACHED*/
112 return _("as new tracks");
115 SoundFileBox::SoundFileBox (bool persistent)
116 : _session(0),
117 table (6, 2),
118 length_clock ("sfboxLengthClock", !persistent, "EditCursorClock", false, true, false),
119 timecode_clock ("sfboxTimecodeClock", !persistent, "EditCursorClock", false, false, false),
120 main_box (false, 6),
121 autoplay_btn (_("Auto-play"))
124 set_name (X_("SoundFileBox"));
125 set_size_request (300, -1);
127 preview_label.set_markup (_("<b>Sound File Information</b>"));
129 border_frame.set_label_widget (preview_label);
130 border_frame.add (main_box);
132 pack_start (border_frame, true, true);
133 set_border_width (6);
135 main_box.set_border_width (6);
137 length.set_text (_("Length:"));
138 length.set_alignment (1, 0.5);
139 timecode.set_text (_("Timestamp:"));
140 timecode.set_alignment (1, 0.5);
141 format.set_text (_("Format:"));
142 format.set_alignment (1, 0.5);
143 channels.set_text (_("Channels:"));
144 channels.set_alignment (1, 0.5);
145 samplerate.set_text (_("Sample rate:"));
146 samplerate.set_alignment (1, 0.5);
148 format_text.set_max_width_chars (8);
149 format_text.set_ellipsize (Pango::ELLIPSIZE_END);
150 format_text.set_alignment (0, 1);
152 table.set_col_spacings (6);
153 table.set_homogeneous (false);
154 table.set_row_spacings (6);
156 table.attach (channels, 0, 1, 0, 1, FILL, FILL);
157 table.attach (samplerate, 0, 1, 1, 2, FILL, FILL);
158 table.attach (format, 0, 1, 2, 4, FILL, FILL);
159 table.attach (length, 0, 1, 4, 5, FILL, FILL);
160 table.attach (timecode, 0, 1, 5, 6, FILL, FILL);
162 table.attach (channels_value, 1, 2, 0, 1, FILL, FILL);
163 table.attach (samplerate_value, 1, 2, 1, 2, FILL, FILL);
164 table.attach (format_text, 1, 2, 2, 4, FILL, FILL);
165 table.attach (length_clock, 1, 2, 4, 5, FILL, FILL);
166 table.attach (timecode_clock, 1, 2, 5, 6, FILL, FILL);
168 length_clock.set_mode (ARDOUR_UI::instance()->secondary_clock.mode());
169 timecode_clock.set_mode (AudioClock::Timecode);
171 main_box.pack_start (table, false, false);
173 tags_entry.set_editable (true);
174 tags_entry.signal_focus_out_event().connect (mem_fun (*this, &SoundFileBox::tags_entry_left));
176 Label* label = manage (new Label (_("Tags:")));
177 label->set_alignment (0.0f, 0.5f);
178 main_box.pack_start (*label, false, false);
179 main_box.pack_start (tags_entry, true, true);
181 main_box.pack_start (bottom_box, false, false);
183 play_btn.set_image (*(manage (new Image (Stock::MEDIA_PLAY, ICON_SIZE_BUTTON))));
184 play_btn.set_label (_("Play"));
186 stop_btn.set_image (*(manage (new Image (Stock::MEDIA_STOP, ICON_SIZE_BUTTON))));
187 stop_btn.set_label (_("Stop"));
189 bottom_box.set_homogeneous (false);
190 bottom_box.set_spacing (6);
191 bottom_box.pack_start(play_btn, true, true);
192 bottom_box.pack_start(stop_btn, true, true);
193 bottom_box.pack_start(autoplay_btn, false, false);
195 play_btn.signal_clicked().connect (mem_fun (*this, &SoundFileBox::audition));
196 stop_btn.signal_clicked().connect (mem_fun (*this, &SoundFileBox::stop_audition));
198 channels_value.set_alignment (0.0f, 0.5f);
199 samplerate_value.set_alignment (0.0f, 0.5f);
202 void
203 SoundFileBox::set_session(Session* s)
205 _session = s;
207 if (!_session) {
208 play_btn.set_sensitive (false);
209 stop_btn.set_sensitive (false);
213 length_clock.set_session (s);
214 timecode_clock.set_session (s);
217 bool
218 SoundFileBox::setup_labels (const ustring& filename)
220 if (!path.empty()) {
221 // save existing tags
222 tags_changed ();
225 path = filename;
227 string error_msg;
229 if(!AudioFileSource::get_soundfile_info (filename, sf_info, error_msg)) {
231 preview_label.set_markup (_("<b>Sound File Information</b>"));
232 format_text.set_text ("");
233 channels_value.set_text ("");
234 samplerate_value.set_text ("");
235 tags_entry.get_buffer()->set_text ("");
237 length_clock.set (0);
238 timecode_clock.set (0);
240 tags_entry.set_sensitive (false);
241 play_btn.set_sensitive (false);
243 return false;
246 preview_label.set_markup (string_compose ("<b>%1</b>", Glib::path_get_basename (filename)));
247 std::string n = sf_info.format_name;
248 if (n.substr (0, 8) == X_("Format: ")) {
249 n = n.substr (8);
251 format_text.set_text (n);
252 channels_value.set_text (to_string (sf_info.channels, std::dec));
254 if (_session && sf_info.samplerate != _session->frame_rate()) {
255 samplerate.set_markup (string_compose ("<b>%1</b>", _("Sample rate:")));
256 samplerate_value.set_markup (string_compose (X_("<b>%1 Hz</b>"), sf_info.samplerate));
257 samplerate_value.set_name ("NewSessionSR1Label");
258 samplerate.set_name ("NewSessionSR1Label");
259 } else {
260 samplerate.set_text (_("Sample rate:"));
261 samplerate_value.set_text (string_compose (X_("%1 Hz"), sf_info.samplerate));
262 samplerate_value.set_name ("NewSessionSR2Label");
263 samplerate.set_name ("NewSessionSR2Label");
266 nframes_t const nfr = _session ? _session->nominal_frame_rate() : 25;
267 double src_coef = (double) nfr / sf_info.samplerate;
269 length_clock.set (sf_info.length * src_coef + 0.5, true);
270 timecode_clock.set (sf_info.timecode * src_coef + 0.5, true);
272 // this is a hack that is fixed in trunk, i think (august 26th, 2007)
274 vector<string> tags = Library->get_tags (string ("//") + filename);
276 stringstream tag_string;
277 for (vector<string>::iterator i = tags.begin(); i != tags.end(); ++i) {
278 if (i != tags.begin()) {
279 tag_string << ", ";
281 tag_string << *i;
283 tags_entry.get_buffer()->set_text (tag_string.str());
285 tags_entry.set_sensitive (true);
286 if (_session) {
287 play_btn.set_sensitive (true);
290 return true;
293 bool
294 SoundFileBox::autoplay() const
296 return autoplay_btn.get_active();
299 bool
300 SoundFileBox::audition_oneshot()
302 audition ();
303 return false;
306 void
307 SoundFileBox::audition ()
309 if (!_session) {
310 return;
313 _session->cancel_audition();
315 if (!Glib::file_test (path, Glib::FILE_TEST_EXISTS)) {
316 warning << string_compose(_("Could not read file: %1 (%2)."), path, strerror(errno)) << endmsg;
317 return;
320 boost::shared_ptr<Region> r;
321 SourceList srclist;
322 boost::shared_ptr<AudioFileSource> afs;
323 bool old_sbp = AudioSource::get_build_peakfiles ();
325 /* don't even think of building peakfiles for these files */
327 AudioSource::set_build_peakfiles (false);
329 for (int n = 0; n < sf_info.channels; ++n) {
330 try {
331 afs = boost::dynamic_pointer_cast<AudioFileSource> (
332 SourceFactory::createReadable (DataType::AUDIO, *_session,
333 path, false, n, Source::Flag (0), false));
335 srclist.push_back(afs);
337 } catch (failed_constructor& err) {
338 error << _("Could not access soundfile: ") << path << endmsg;
339 AudioSource::set_build_peakfiles (old_sbp);
340 return;
344 AudioSource::set_build_peakfiles (old_sbp);
346 if (srclist.empty()) {
347 return;
350 afs = boost::dynamic_pointer_cast<AudioFileSource> (srclist[0]);
351 string rname = region_name_from_path (afs->path(), false);
352 r = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (srclist, 0,
353 srclist[0]->length(srclist[0]->timeline_position()),
354 rname, 0, Region::DefaultFlags, false));
356 _session->audition_region(r);
359 void
360 SoundFileBox::stop_audition ()
362 if (_session) {
363 _session->cancel_audition();
367 bool
368 SoundFileBox::tags_entry_left (GdkEventFocus *)
370 tags_changed ();
371 return false;
374 void
375 SoundFileBox::tags_changed ()
377 string tag_string = tags_entry.get_buffer()->get_text ();
379 if (tag_string.empty()) {
380 return;
383 vector<string> tags;
385 if (!PBD::tokenize (tag_string, string(",\n"), std::back_inserter (tags), true)) {
386 warning << _("SoundFileBox: Could not tokenize string: ") << tag_string << endmsg;
387 return;
390 save_tags (tags);
393 void
394 SoundFileBox::save_tags (const vector<string>& tags)
396 Library->set_tags (string ("//") + path, tags);
397 Library->save_changes ();
400 SoundFileBrowser::SoundFileBrowser (Gtk::Window& parent, string title, ARDOUR::Session* s, bool persistent)
401 : ArdourDialog (parent, title, false, false),
402 found_list (ListStore::create(found_list_columns)),
403 freesound_list (ListStore::create(freesound_list_columns)),
404 chooser (FILE_CHOOSER_ACTION_OPEN),
405 preview (persistent),
406 found_search_btn (_("Search")),
407 found_list_view (found_list),
408 freesound_search_btn (_("Start Downloading")),
409 freesound_list_view (freesound_list)
411 resetting_ourselves = false;
412 gm = 0;
414 resetting_ourselves = false;
415 gm = 0;
417 if (ARDOUR::Profile->get_sae()) {
418 chooser.add_shortcut_folder_uri("file:///Library/GarageBand/Apple Loops");
419 chooser.add_shortcut_folder_uri("file:///Library/Application Support/GarageBand/Instrument Library/Sampler/Sampler Files");
423 //add the file chooser
425 chooser.set_border_width (12);
427 audio_filter.add_custom (FILE_FILTER_FILENAME, mem_fun(*this, &SoundFileBrowser::on_audio_filter));
428 audio_filter.set_name (_("Audio files"));
430 midi_filter.add_custom (FILE_FILTER_FILENAME, mem_fun(*this, &SoundFileBrowser::on_midi_filter));
431 midi_filter.set_name (_("MIDI files"));
433 matchall_filter.add_pattern ("*.*");
434 matchall_filter.set_name (_("All files"));
436 chooser.add_filter (audio_filter);
437 chooser.add_filter (midi_filter);
438 chooser.add_filter (matchall_filter);
439 chooser.set_select_multiple (true);
440 chooser.signal_update_preview().connect(mem_fun(*this, &SoundFileBrowser::update_preview));
441 chooser.signal_file_activated().connect (mem_fun (*this, &SoundFileBrowser::chooser_file_activated));
443 if (!persistent_folder.empty()) {
444 chooser.set_current_folder (persistent_folder);
446 notebook.append_page (chooser, _("Browse Files"));
449 hpacker.set_spacing (6);
450 hpacker.pack_start (notebook, true, true);
451 hpacker.pack_start (preview, false, false);
453 get_vbox()->pack_start (hpacker, true, true);
455 //add tag search
457 VBox* vbox;
458 HBox* hbox;
461 hbox = manage(new HBox);
462 hbox->pack_start (found_entry);
463 hbox->pack_start (found_search_btn);
465 Gtk::ScrolledWindow *scroll = manage(new ScrolledWindow);
466 scroll->add(found_list_view);
467 scroll->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
469 vbox = manage(new VBox);
470 vbox->pack_start (*hbox, PACK_SHRINK);
471 vbox->pack_start (*scroll);
473 found_list_view.append_column(_("Paths"), found_list_columns.pathname);
475 found_list_view.get_selection()->signal_changed().connect(mem_fun(*this, &SoundFileBrowser::found_list_view_selected));
477 found_list_view.signal_row_activated().connect (mem_fun (*this, &SoundFileBrowser::found_list_view_activated));
479 found_search_btn.signal_clicked().connect(mem_fun(*this, &SoundFileBrowser::found_search_clicked));
480 found_entry.signal_activate().connect(mem_fun(*this, &SoundFileBrowser::found_search_clicked));
482 notebook.append_page (*vbox, _("Search Tags"));
485 //add freesound search
486 #ifdef FREESOUND
488 VBox* vbox;
489 HBox* passbox;
490 Label* label;
492 passbox = manage(new HBox);
493 passbox->set_border_width (12);
494 passbox->set_spacing (6);
496 label = manage (new Label);
497 label->set_text (_("User:"));
498 passbox->pack_start (*label, false, false);
499 passbox->pack_start (freesound_name_entry);
500 label = manage (new Label);
501 label->set_text (_("Password:"));
502 passbox->pack_start (*label, false, false);
503 passbox->pack_start (freesound_pass_entry);
504 label = manage (new Label);
505 label->set_text (_("Tags:"));
506 passbox->pack_start (*label, false, false);
507 passbox->pack_start (freesound_entry, false, false);
508 passbox->pack_start (freesound_search_btn, false, false);
510 Gtk::ScrolledWindow *scroll = manage(new ScrolledWindow);
511 scroll->add(freesound_list_view);
512 scroll->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
514 vbox = manage(new VBox);
515 vbox->pack_start (*passbox, PACK_SHRINK);
516 vbox->pack_start(*scroll);
518 //vbox->pack_start (freesound_list_view);
520 freesound_list_view.append_column(_("Paths"), freesound_list_columns.pathname);
521 freesound_list_view.get_selection()->signal_changed().connect(mem_fun(*this, &SoundFileBrowser::freesound_list_view_selected));
523 //freesound_list_view.get_selection()->set_mode (SELECTION_MULTIPLE);
524 freesound_list_view.signal_row_activated().connect (mem_fun (*this, &SoundFileBrowser::freesound_list_view_activated));
525 freesound_search_btn.signal_clicked().connect(mem_fun(*this, &SoundFileBrowser::freesound_search_clicked));
526 freesound_entry.signal_activate().connect(mem_fun(*this, &SoundFileBrowser::freesound_search_clicked));
527 notebook.append_page (*vbox, _("Search Freesound"));
529 #endif
532 notebook.set_size_request (500, -1);
534 set_session (s);
536 add_button (Stock::CANCEL, RESPONSE_CANCEL);
537 add_button (Stock::APPLY, RESPONSE_APPLY);
538 add_button (Stock::OK, RESPONSE_OK);
542 SoundFileBrowser::~SoundFileBrowser ()
544 persistent_folder = chooser.get_current_folder();
548 void
549 SoundFileBrowser::on_show ()
551 ArdourDialog::on_show ();
552 start_metering ();
555 void
556 SoundFileBrowser::clear_selection ()
558 chooser.unselect_all ();
559 found_list_view.get_selection()->unselect_all ();
562 void
563 SoundFileBrowser::chooser_file_activated ()
565 preview.audition ();
568 void
569 SoundFileBrowser::found_list_view_activated (const TreeModel::Path&, TreeViewColumn*)
571 preview.audition ();
574 void
575 SoundFileBrowser::freesound_list_view_activated (const TreeModel::Path&, TreeViewColumn*)
577 preview.audition ();
580 void
581 SoundFileBrowser::set_session (Session* s)
583 ArdourDialog::set_session (s);
584 preview.set_session (s);
585 if (s) {
586 add_gain_meter ();
587 } else {
588 remove_gain_meter ();
592 void
593 SoundFileBrowser::add_gain_meter ()
595 delete gm;
597 gm = new GainMeter (*session);
599 boost::shared_ptr<Route> r = session->the_auditioner ();
601 gm->set_controls (r, r->shared_peak_meter(), r->amp());
603 meter_packer.set_border_width (12);
604 meter_packer.pack_start (*gm, false, true);
605 hpacker.pack_end (meter_packer, false, false);
606 meter_packer.show_all ();
607 start_metering ();
610 void
611 SoundFileBrowser::remove_gain_meter ()
613 if (gm) {
614 meter_packer.remove (*gm);
615 hpacker.remove (meter_packer);
616 delete gm;
617 gm = 0;
621 void
622 SoundFileBrowser::start_metering ()
624 metering_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (mem_fun(*this, &SoundFileBrowser::meter));
627 void
628 SoundFileBrowser::stop_metering ()
630 metering_connection.disconnect();
633 void
634 SoundFileBrowser::meter ()
636 if (is_mapped () && session && gm) {
637 gm->update_meters ();
641 bool
642 SoundFileBrowser::on_audio_filter (const FileFilter::Info& filter_info)
644 return AudioFileSource::safe_audio_file_extension (filter_info.filename);
647 bool
648 SoundFileBrowser::on_midi_filter (const FileFilter::Info& filter_info)
650 return SMFSource::safe_midi_file_extension (filter_info.filename);
653 void
654 SoundFileBrowser::update_preview ()
656 if (preview.setup_labels (chooser.get_filename())) {
657 if (preview.autoplay()) {
658 Glib::signal_idle().connect (mem_fun (preview, &SoundFileBox::audition_oneshot));
663 void
664 SoundFileBrowser::found_list_view_selected ()
666 if (!reset_options ()) {
667 set_response_sensitive (RESPONSE_OK, false);
668 } else {
669 ustring file;
671 TreeView::Selection::ListHandle_Path rows = found_list_view.get_selection()->get_selected_rows ();
673 if (!rows.empty()) {
674 TreeIter iter = found_list->get_iter(*rows.begin());
675 file = (*iter)[found_list_columns.pathname];
676 chooser.set_filename (file);
677 set_response_sensitive (RESPONSE_OK, true);
678 } else {
679 set_response_sensitive (RESPONSE_OK, false);
682 preview.setup_labels (file);
686 void
687 SoundFileBrowser::freesound_list_view_selected ()
689 if (!reset_options ()) {
690 set_response_sensitive (RESPONSE_OK, false);
691 } else {
692 ustring file;
694 TreeView::Selection::ListHandle_Path rows = freesound_list_view.get_selection()->get_selected_rows ();
696 if (!rows.empty()) {
697 TreeIter iter = freesound_list->get_iter(*rows.begin());
698 file = (*iter)[freesound_list_columns.pathname];
699 chooser.set_filename (file);
700 set_response_sensitive (RESPONSE_OK, true);
701 } else {
702 set_response_sensitive (RESPONSE_OK, false);
705 preview.setup_labels (file);
709 void
710 SoundFileBrowser::found_search_clicked ()
712 string tag_string = found_entry.get_text ();
714 vector<string> tags;
716 if (!PBD::tokenize (tag_string, string(","), std::back_inserter (tags), true)) {
717 warning << _("SoundFileBrowser: Could not tokenize string: ") << tag_string << endmsg;
718 return;
721 vector<string> results;
722 Library->search_members_and (results, tags);
724 found_list->clear();
725 for (vector<string>::iterator i = results.begin(); i != results.end(); ++i) {
726 TreeModel::iterator new_row = found_list->append();
727 TreeModel::Row row = *new_row;
728 string path = Glib::filename_from_uri (string ("file:") + *i);
729 row[found_list_columns.pathname] = path;
733 void*
734 freesound_search_thread_entry (void* arg)
736 PBD::notify_gui_about_thread_creation (pthread_self(), X_("Freesound Search"));
738 static_cast<SoundFileBrowser*>(arg)->freesound_search_thread ();
740 return 0;
743 bool searching = false;
744 bool canceling = false;
746 void
747 SoundFileBrowser::freesound_search_clicked ()
749 if (canceling) //already canceling, button does nothing
750 return;
752 if ( searching ) {
753 freesound_search_btn.set_label(_("Cancelling.."));
754 canceling = true;
755 } else {
756 searching = true;
757 freesound_search_btn.set_label(_("Cancel"));
758 pthread_t freesound_thr;
759 pthread_create_and_store ("freesound_search", &freesound_thr, 0, freesound_search_thread_entry, this);
763 void
764 SoundFileBrowser::freesound_search_thread()
766 #ifdef FREESOUND
767 freesound_list->clear();
769 string path;
770 path = Glib::get_home_dir();
771 path += "/Freesound/";
772 Mootcher theMootcher(path.c_str());
774 string name_string = freesound_name_entry.get_text ();
775 string pass_string = freesound_pass_entry.get_text ();
776 string search_string = freesound_entry.get_text ();
778 if ( theMootcher.doLogin( name_string, pass_string ) ) {
780 string theString = theMootcher.searchText(search_string);
782 XMLTree doc;
783 doc.read_buffer( theString );
784 XMLNode *root = doc.root();
786 if (root==NULL) return;
788 if ( strcmp(root->name().c_str(), "freesound") == 0) {
790 XMLNode *node = 0;
791 XMLNodeList children = root->children();
792 XMLNodeConstIterator niter;
793 for (niter = children.begin(); niter != children.end() && !canceling; ++niter) {
794 node = *niter;
795 if( strcmp( node->name().c_str(), "sample") == 0 ){
796 XMLProperty *prop=node->property ("id");
797 string filename = theMootcher.getFile( prop->value().c_str() );
798 if ( filename != "" ) {
799 TreeModel::iterator new_row = freesound_list->append();
800 TreeModel::Row row = *new_row;
801 string path = Glib::filename_from_uri (string ("file:") + filename);
802 row[freesound_list_columns.pathname] = path;
809 searching = false;
810 canceling = false;
811 freesound_search_btn.set_label(_("Start Downloading"));
812 #endif
815 vector<ustring>
816 SoundFileBrowser::get_paths ()
818 vector<ustring> results;
820 int n = notebook.get_current_page ();
822 if (n == 0) {
823 vector<ustring> filenames = chooser.get_filenames();
824 vector<ustring>::iterator i;
826 for (i = filenames.begin(); i != filenames.end(); ++i) {
827 struct stat buf;
828 if ((!stat((*i).c_str(), &buf)) && S_ISREG(buf.st_mode)) {
829 results.push_back (*i);
833 } else if (n==1){
835 typedef TreeView::Selection::ListHandle_Path ListPath;
837 ListPath rows = found_list_view.get_selection()->get_selected_rows ();
838 for (ListPath::iterator i = rows.begin() ; i != rows.end(); ++i) {
839 TreeIter iter = found_list->get_iter(*i);
840 ustring str = (*iter)[found_list_columns.pathname];
842 results.push_back (str);
844 } else {
846 typedef TreeView::Selection::ListHandle_Path ListPath;
848 ListPath rows = freesound_list_view.get_selection()->get_selected_rows ();
849 for (ListPath::iterator i = rows.begin() ; i != rows.end(); ++i) {
850 TreeIter iter = freesound_list->get_iter(*i);
851 ustring str = (*iter)[freesound_list_columns.pathname];
853 results.push_back (str);
857 return results;
860 void
861 SoundFileOmega::reset_options_noret ()
863 if (!resetting_ourselves) {
864 (void) reset_options ();
868 bool
869 SoundFileOmega::reset_options ()
871 vector<ustring> paths = get_paths ();
873 if (paths.empty()) {
875 channel_combo.set_sensitive (false);
876 action_combo.set_sensitive (false);
877 where_combo.set_sensitive (false);
878 copy_files_btn.set_sensitive (false);
880 return false;
882 } else {
884 channel_combo.set_sensitive (true);
885 action_combo.set_sensitive (true);
886 where_combo.set_sensitive (true);
888 /* if we get through this function successfully, this may be
889 reset at the end, once we know if we can use hard links
890 to do embedding
893 if (Config->get_only_copy_imported_files()) {
894 copy_files_btn.set_sensitive (false);
895 } else {
896 copy_files_btn.set_sensitive (false);
900 bool same_size;
901 bool src_needed;
902 bool selection_includes_multichannel;
903 bool selection_can_be_embedded_with_links = check_link_status (*session, paths);
904 ImportMode mode;
906 if (check_info (paths, same_size, src_needed, selection_includes_multichannel)) {
907 Glib::signal_idle().connect (mem_fun (*this, &SoundFileOmega::bad_file_message));
908 return false;
911 ustring existing_choice;
912 vector<string> action_strings;
914 if (selected_track_cnt > 0) {
915 if (channel_combo.get_active_text().length()) {
916 ImportDisposition id = get_channel_disposition();
918 switch (id) {
919 case Editing::ImportDistinctFiles:
920 if (selected_track_cnt == paths.size()) {
921 action_strings.push_back (importmode2string (ImportToTrack));
923 break;
925 case Editing::ImportDistinctChannels:
926 /* XXX it would be nice to allow channel-per-selected track
927 but its too hard we don't want to deal with all the
928 different per-file + per-track channel configurations.
930 break;
932 default:
933 action_strings.push_back (importmode2string (ImportToTrack));
934 break;
939 action_strings.push_back (importmode2string (ImportAsTrack));
940 action_strings.push_back (importmode2string (ImportAsRegion));
941 action_strings.push_back (importmode2string (ImportAsTapeTrack));
943 resetting_ourselves = true;
945 existing_choice = action_combo.get_active_text();
947 set_popdown_strings (action_combo, action_strings);
949 /* preserve any existing choice, if possible */
952 if (existing_choice.length()) {
953 vector<string>::iterator x;
954 for (x = action_strings.begin(); x != action_strings.end(); ++x) {
955 if (*x == existing_choice) {
956 action_combo.set_active_text (existing_choice);
957 break;
960 if (x == action_strings.end()) {
961 action_combo.set_active_text (action_strings.front());
963 } else {
964 action_combo.set_active_text (action_strings.front());
967 resetting_ourselves = false;
969 if ((mode = get_mode()) == ImportAsRegion) {
970 where_combo.set_sensitive (false);
971 } else {
972 where_combo.set_sensitive (true);
975 vector<string> channel_strings;
977 if (mode == ImportAsTrack || mode == ImportAsTapeTrack || mode == ImportToTrack) {
978 channel_strings.push_back (_("one track per file"));
980 if (selection_includes_multichannel) {
981 channel_strings.push_back (_("one track per channel"));
984 if (paths.size() > 1) {
985 /* tape tracks are a single region per track, so we cannot
986 sequence multiple files.
988 if (mode != ImportAsTapeTrack) {
989 channel_strings.push_back (_("sequence files"));
991 if (same_size) {
992 channel_strings.push_back (_("all files in one region"));
997 } else {
998 channel_strings.push_back (_("one region per file"));
1000 if (selection_includes_multichannel) {
1001 channel_strings.push_back (_("one region per channel"));
1004 if (paths.size() > 1) {
1005 if (same_size) {
1006 channel_strings.push_back (_("all files in one region"));
1011 existing_choice = channel_combo.get_active_text();
1013 set_popdown_strings (channel_combo, channel_strings);
1015 /* preserve any existing choice, if possible */
1017 if (existing_choice.length()) {
1018 vector<string>::iterator x;
1019 for (x = channel_strings.begin(); x != channel_strings.end(); ++x) {
1020 if (*x == existing_choice) {
1021 channel_combo.set_active_text (existing_choice);
1022 break;
1025 if (x == channel_strings.end()) {
1026 channel_combo.set_active_text (channel_strings.front());
1028 } else {
1029 channel_combo.set_active_text (channel_strings.front());
1032 if (src_needed) {
1033 src_combo.set_sensitive (true);
1034 } else {
1035 src_combo.set_sensitive (false);
1038 if (Config->get_only_copy_imported_files()) {
1040 if (selection_can_be_embedded_with_links) {
1041 copy_files_btn.set_sensitive (true);
1042 } else {
1043 copy_files_btn.set_sensitive (false);
1046 } else {
1048 copy_files_btn.set_sensitive (true);
1051 return true;
1055 bool
1056 SoundFileOmega::bad_file_message()
1058 MessageDialog msg (*this,
1059 _("One or more of the selected files\ncannot be used by Ardour"),
1060 true,
1061 Gtk::MESSAGE_INFO,
1062 Gtk::BUTTONS_OK);
1063 msg.run ();
1064 resetting_ourselves = true;
1065 chooser.unselect_uri (chooser.get_preview_uri());
1066 resetting_ourselves = false;
1068 return false;
1071 bool
1072 SoundFileOmega::check_info (const vector<ustring>& paths, bool& same_size, bool& src_needed, bool& multichannel)
1074 SoundFileInfo info;
1075 nframes64_t sz = 0;
1076 bool err = false;
1077 string errmsg;
1079 same_size = true;
1080 src_needed = false;
1081 multichannel = false;
1083 for (vector<ustring>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
1085 if (AudioFileSource::get_soundfile_info (*i, info, errmsg)) {
1086 if (info.channels > 1) {
1087 multichannel = true;
1089 if (sz == 0) {
1090 sz = info.length;
1091 } else {
1092 if (sz != info.length) {
1093 same_size = false;
1097 if ((nframes_t) info.samplerate != session->frame_rate()) {
1098 src_needed = true;
1101 } else if (SMFSource::safe_midi_file_extension (*i)) {
1103 Evoral::SMF reader;
1104 reader.open(*i);
1105 if (reader.num_tracks() > 1) {
1106 multichannel = true; // "channel" == track here...
1109 /* XXX we need err = true handling here in case
1110 we can't check the file
1113 } else {
1114 err = true;
1118 return err;
1122 bool
1123 SoundFileOmega::check_link_status (const Session& s, const vector<ustring>& paths)
1125 sys::path path = s.session_directory().sound_path() / "linktest";
1126 string tmpdir = path.to_string();
1127 bool ret = false;
1129 if (mkdir (tmpdir.c_str(), 0744)) {
1130 if (errno != EEXIST) {
1131 return false;
1135 for (vector<ustring>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
1137 char tmpc[MAXPATHLEN+1];
1139 snprintf (tmpc, sizeof(tmpc), "%s/%s", tmpdir.c_str(), Glib::path_get_basename (*i).c_str());
1141 /* can we link ? */
1143 if (link ((*i).c_str(), tmpc)) {
1144 goto out;
1147 unlink (tmpc);
1150 ret = true;
1152 out:
1153 rmdir (tmpdir.c_str());
1154 return ret;
1157 SoundFileChooser::SoundFileChooser (Gtk::Window& parent, string title, ARDOUR::Session* s)
1158 : SoundFileBrowser (parent, title, s, false)
1160 chooser.set_select_multiple (false);
1161 found_list_view.get_selection()->set_mode (SELECTION_SINGLE);
1162 freesound_list_view.get_selection()->set_mode (SELECTION_SINGLE);
1165 void
1166 SoundFileChooser::on_hide ()
1168 ArdourDialog::on_hide();
1169 stop_metering ();
1171 if (session) {
1172 session->cancel_audition();
1176 ustring
1177 SoundFileChooser::get_filename ()
1179 vector<ustring> paths;
1181 paths = get_paths ();
1183 if (paths.empty()) {
1184 return ustring ();
1187 if (!Glib::file_test (paths.front(), Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
1188 return ustring();
1191 return paths.front();
1194 SoundFileOmega::SoundFileOmega (Gtk::Window& parent, string title, ARDOUR::Session* s, int selected_tracks, bool persistent,
1195 Editing::ImportMode mode_hint)
1196 : SoundFileBrowser (parent, title, s, persistent),
1197 copy_files_btn ( _("Copy files to session")),
1198 selected_track_cnt (selected_tracks)
1200 VBox* vbox;
1201 HBox* hbox;
1202 vector<string> str;
1204 set_size_request (-1, 450);
1206 block_two.set_border_width (12);
1207 block_three.set_border_width (12);
1208 block_four.set_border_width (12);
1210 options.set_spacing (12);
1212 str.clear ();
1213 str.push_back (_("file timestamp"));
1214 str.push_back (_("edit point"));
1215 str.push_back (_("playhead"));
1216 str.push_back (_("session start"));
1217 set_popdown_strings (where_combo, str);
1218 where_combo.set_active_text (str.front());
1220 Label* l = manage (new Label);
1221 l->set_text (_("Add files:"));
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 (action_combo, false, false);
1228 vbox = manage (new VBox);
1229 vbox->pack_start (*hbox, false, false);
1230 options.pack_start (*vbox, false, false);
1232 /* dummy entry for action combo so that it doesn't look odd if we
1233 come up with no tracks selected.
1236 str.clear ();
1237 str.push_back (importmode2string (mode_hint));
1238 set_popdown_strings (action_combo, str);
1239 action_combo.set_active_text (str.front());
1240 action_combo.set_sensitive (false);
1242 l = manage (new Label);
1243 l->set_text (_("Insert at:"));
1245 hbox = manage (new HBox);
1246 hbox->set_border_width (12);
1247 hbox->set_spacing (6);
1248 hbox->pack_start (*l, false, false);
1249 hbox->pack_start (where_combo, false, false);
1250 vbox = manage (new VBox);
1251 vbox->pack_start (*hbox, false, false);
1252 options.pack_start (*vbox, false, false);
1255 l = manage (new Label);
1256 l->set_text (_("Mapping:"));
1258 hbox = manage (new HBox);
1259 hbox->set_border_width (12);
1260 hbox->set_spacing (6);
1261 hbox->pack_start (*l, false, false);
1262 hbox->pack_start (channel_combo, false, false);
1263 vbox = manage (new VBox);
1264 vbox->pack_start (*hbox, false, false);
1265 options.pack_start (*vbox, false, false);
1267 str.clear ();
1268 str.push_back (_("one track per file"));
1269 set_popdown_strings (channel_combo, str);
1270 channel_combo.set_active_text (str.front());
1271 channel_combo.set_sensitive (false);
1273 l = manage (new Label);
1274 l->set_text (_("Conversion quality:"));
1276 hbox = manage (new HBox);
1277 hbox->set_border_width (12);
1278 hbox->set_spacing (6);
1279 hbox->pack_start (*l, false, false);
1280 hbox->pack_start (src_combo, false, false);
1281 vbox = manage (new VBox);
1282 vbox->pack_start (*hbox, false, false);
1283 options.pack_start (*vbox, false, false);
1285 str.clear ();
1286 str.push_back (_("Best"));
1287 str.push_back (_("Good"));
1288 str.push_back (_("Quick"));
1289 str.push_back (_("Fast"));
1290 str.push_back (_("Fastest"));
1292 set_popdown_strings (src_combo, str);
1293 src_combo.set_active_text (str.front());
1294 src_combo.set_sensitive (false);
1296 reset_options ();
1298 action_combo.signal_changed().connect (mem_fun (*this, &SoundFileOmega::reset_options_noret));
1300 copy_files_btn.set_active (true);
1302 block_four.pack_start (copy_files_btn, false, false);
1304 options.pack_start (block_four, false, false);
1306 get_vbox()->pack_start (options, false, false);
1308 /* setup disposition map */
1310 disposition_map.insert (pair<ustring,ImportDisposition>(_("one track per file"), ImportDistinctFiles));
1311 disposition_map.insert (pair<ustring,ImportDisposition>(_("one track per channel"), ImportDistinctChannels));
1312 disposition_map.insert (pair<ustring,ImportDisposition>(_("merge files"), ImportMergeFiles));
1313 disposition_map.insert (pair<ustring,ImportDisposition>(_("sequence files"), ImportSerializeFiles));
1315 disposition_map.insert (pair<ustring,ImportDisposition>(_("one region per file"), ImportDistinctFiles));
1316 disposition_map.insert (pair<ustring,ImportDisposition>(_("one region per channel"), ImportDistinctChannels));
1317 disposition_map.insert (pair<ustring,ImportDisposition>(_("all files in one region"), ImportMergeFiles));
1319 chooser.signal_selection_changed().connect (mem_fun (*this, &SoundFileOmega::file_selection_changed));
1321 /* set size requests for a couple of combos to allow them to display the longest text
1322 they will ever be asked to display. This prevents them being resized when the user
1323 selects a file to import, which in turn prevents the size of the dialog from jumping
1324 around. */
1326 vector<string> t;
1327 t.push_back (_("one track per file"));
1328 t.push_back (_("one track per channel"));
1329 t.push_back (_("sequence files"));
1330 t.push_back (_("all files in one region"));
1331 set_size_request_to_display_given_text (channel_combo, t, COMBO_FUDGE + 10, 15);
1333 t.clear ();
1334 t.push_back (importmode2string (ImportAsTrack));
1335 t.push_back (importmode2string (ImportToTrack));
1336 t.push_back (importmode2string (ImportAsRegion));
1337 t.push_back (importmode2string (ImportAsTapeTrack));
1338 set_size_request_to_display_given_text (action_combo, t, COMBO_FUDGE + 10, 15);
1341 void
1342 SoundFileOmega::set_mode (ImportMode mode)
1344 action_combo.set_active_text (importmode2string (mode));
1347 ImportMode
1348 SoundFileOmega::get_mode () const
1350 return string2importmode (action_combo.get_active_text());
1353 void
1354 SoundFileOmega::on_hide ()
1356 ArdourDialog::on_hide();
1357 if (session) {
1358 session->cancel_audition();
1362 ImportPosition
1363 SoundFileOmega::get_position() const
1365 ustring str = where_combo.get_active_text();
1367 if (str == _("file timestamp")) {
1368 return ImportAtTimestamp;
1369 } else if (str == _("edit point")) {
1370 return ImportAtEditPoint;
1371 } else if (str == _("playhead")) {
1372 return ImportAtPlayhead;
1373 } else {
1374 return ImportAtStart;
1378 SrcQuality
1379 SoundFileOmega::get_src_quality() const
1381 ustring str = where_combo.get_active_text();
1383 if (str == _("Best")) {
1384 return SrcBest;
1385 } else if (str == _("Good")) {
1386 return SrcGood;
1387 } else if (str == _("Quick")) {
1388 return SrcQuick;
1389 } else if (str == _("Fast")) {
1390 return SrcFast;
1391 } else {
1392 return SrcFastest;
1396 ImportDisposition
1397 SoundFileOmega::get_channel_disposition () const
1399 /* we use a map here because the channel combo can contain different strings
1400 depending on the state of the other combos. the map contains all possible strings
1401 and the ImportDisposition enum that corresponds to it.
1404 ustring str = channel_combo.get_active_text();
1405 DispositionMap::const_iterator x = disposition_map.find (str);
1407 if (x == disposition_map.end()) {
1408 fatal << string_compose (_("programming error: %1 (%2)"), "unknown string for import disposition", str) << endmsg;
1409 /*NOTREACHED*/
1412 return x->second;
1415 void
1416 SoundFileOmega::reset (int selected_tracks)
1418 selected_track_cnt = selected_tracks;
1419 reset_options ();
1422 void
1423 SoundFileOmega::file_selection_changed ()
1425 if (resetting_ourselves) {
1426 return;
1429 if (!reset_options ()) {
1430 set_response_sensitive (RESPONSE_OK, false);
1431 } else {
1432 if (chooser.get_filenames().size() > 0) {
1433 set_response_sensitive (RESPONSE_OK, true);
1434 } else {
1435 set_response_sensitive (RESPONSE_OK, false);