handle multiple imports of the same file better (via better source naming); make...
[ardour2.git] / gtk2_ardour / sfdb_ui.cc
blobd6bd6341cf71a51b8f0c125faea0473a4fdaf140
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 std::string;
78 string 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 : table (6, 2),
117 length_clock ("sfboxLengthClock", !persistent, "EditCursorClock", false, false, true, false),
118 timecode_clock ("sfboxTimecodeClock", !persistent, "EditCursorClock", false, false, false, false),
119 main_box (false, 6),
120 autoplay_btn (_("Auto-play"))
123 set_name (X_("SoundFileBox"));
124 set_size_request (300, -1);
126 preview_label.set_markup (_("<b>Sound File Information</b>"));
128 border_frame.set_label_widget (preview_label);
129 border_frame.add (main_box);
131 pack_start (border_frame, true, true);
132 set_border_width (6);
134 main_box.set_border_width (6);
136 length.set_text (_("Length:"));
137 length.set_alignment (1, 0.5);
138 timecode.set_text (_("Timestamp:"));
139 timecode.set_alignment (1, 0.5);
140 format.set_text (_("Format:"));
141 format.set_alignment (1, 0.5);
142 channels.set_text (_("Channels:"));
143 channels.set_alignment (1, 0.5);
144 samplerate.set_text (_("Sample rate:"));
145 samplerate.set_alignment (1, 0.5);
147 preview_label.set_max_width_chars (50);
148 preview_label.set_ellipsize (Pango::ELLIPSIZE_END);
150 format_text.set_max_width_chars (20);
151 format_text.set_ellipsize (Pango::ELLIPSIZE_END);
152 format_text.set_alignment (0, 1);
154 table.set_col_spacings (6);
155 table.set_homogeneous (false);
156 table.set_row_spacings (6);
158 table.attach (channels, 0, 1, 0, 1, FILL, FILL);
159 table.attach (samplerate, 0, 1, 1, 2, FILL, FILL);
160 table.attach (format, 0, 1, 2, 4, FILL, FILL);
161 table.attach (length, 0, 1, 4, 5, FILL, FILL);
162 table.attach (timecode, 0, 1, 5, 6, FILL, FILL);
164 table.attach (channels_value, 1, 2, 0, 1, FILL, FILL);
165 table.attach (samplerate_value, 1, 2, 1, 2, FILL, FILL);
166 table.attach (format_text, 1, 2, 2, 4, FILL, FILL);
167 table.attach (length_clock, 1, 2, 4, 5, FILL, FILL);
168 table.attach (timecode_clock, 1, 2, 5, 6, FILL, FILL);
170 length_clock.set_mode (ARDOUR_UI::instance()->secondary_clock.mode());
171 timecode_clock.set_mode (AudioClock::Timecode);
173 main_box.pack_start (table, false, false);
175 tags_entry.set_editable (true);
176 tags_entry.signal_focus_out_event().connect (sigc::mem_fun (*this, &SoundFileBox::tags_entry_left));
178 Label* label = manage (new Label (_("Tags:")));
179 label->set_alignment (0.0f, 0.5f);
180 main_box.pack_start (*label, false, false);
181 main_box.pack_start (tags_entry, true, true);
183 main_box.pack_start (bottom_box, false, false);
185 play_btn.set_image (*(manage (new Image (Stock::MEDIA_PLAY, ICON_SIZE_BUTTON))));
186 play_btn.set_label (_("Play"));
188 stop_btn.set_image (*(manage (new Image (Stock::MEDIA_STOP, ICON_SIZE_BUTTON))));
189 stop_btn.set_label (_("Stop"));
191 bottom_box.set_homogeneous (false);
192 bottom_box.set_spacing (6);
193 bottom_box.pack_start(play_btn, true, true);
194 bottom_box.pack_start(stop_btn, true, true);
195 bottom_box.pack_start(autoplay_btn, false, false);
197 play_btn.signal_clicked().connect (sigc::mem_fun (*this, &SoundFileBox::audition));
198 stop_btn.signal_clicked().connect (sigc::mem_fun (*this, &SoundFileBox::stop_audition));
200 channels_value.set_alignment (0.0f, 0.5f);
201 samplerate_value.set_alignment (0.0f, 0.5f);
204 void
205 SoundFileBox::set_session(Session* s)
207 SessionHandlePtr::set_session (s);
209 length_clock.set_session (s);
210 timecode_clock.set_session (s);
212 if (!_session) {
213 play_btn.set_sensitive (false);
214 stop_btn.set_sensitive (false);
218 bool
219 SoundFileBox::setup_labels (const string& filename)
221 if (!path.empty()) {
222 // save existing tags
223 tags_changed ();
226 path = filename;
228 string error_msg;
230 if(!AudioFileSource::get_soundfile_info (filename, sf_info, error_msg)) {
232 preview_label.set_markup (_("<b>Sound File Information</b>"));
233 format_text.set_text ("");
234 channels_value.set_text ("");
235 samplerate_value.set_text ("");
236 tags_entry.get_buffer()->set_text ("");
238 length_clock.set (0);
239 timecode_clock.set (0);
241 tags_entry.set_sensitive (false);
242 play_btn.set_sensitive (false);
244 return false;
247 preview_label.set_markup (string_compose ("<b>%1</b>", Glib::path_get_basename (filename)));
248 std::string n = sf_info.format_name;
249 if (n.substr (0, 8) == X_("Format: ")) {
250 n = n.substr (8);
252 format_text.set_text (n);
253 channels_value.set_text (to_string (sf_info.channels, std::dec));
255 if (_session && sf_info.samplerate != _session->frame_rate()) {
256 samplerate.set_markup (string_compose ("<b>%1</b>", _("Sample rate:")));
257 samplerate_value.set_markup (string_compose (X_("<b>%1 Hz</b>"), sf_info.samplerate));
258 samplerate_value.set_name ("NewSessionSR1Label");
259 samplerate.set_name ("NewSessionSR1Label");
260 } else {
261 samplerate.set_text (_("Sample rate:"));
262 samplerate_value.set_text (string_compose (X_("%1 Hz"), sf_info.samplerate));
263 samplerate_value.set_name ("NewSessionSR2Label");
264 samplerate.set_name ("NewSessionSR2Label");
267 nframes_t const nfr = _session ? _session->nominal_frame_rate() : 25;
268 double src_coef = (double) nfr / sf_info.samplerate;
270 length_clock.set (sf_info.length * src_coef + 0.5, true);
271 timecode_clock.set (sf_info.timecode * src_coef + 0.5, true);
273 // this is a hack that is fixed in trunk, i think (august 26th, 2007)
275 vector<string> tags = Library->get_tags (string ("//") + filename);
277 stringstream tag_string;
278 for (vector<string>::iterator i = tags.begin(); i != tags.end(); ++i) {
279 if (i != tags.begin()) {
280 tag_string << ", ";
282 tag_string << *i;
284 tags_entry.get_buffer()->set_text (tag_string.str());
286 tags_entry.set_sensitive (true);
287 if (_session) {
288 play_btn.set_sensitive (true);
291 return true;
294 bool
295 SoundFileBox::autoplay() const
297 return autoplay_btn.get_active();
300 bool
301 SoundFileBox::audition_oneshot()
303 audition ();
304 return false;
307 void
308 SoundFileBox::audition ()
310 if (!_session) {
311 return;
314 if (SMFSource::safe_midi_file_extension (path)) {
315 error << _("Auditioning of MIDI files is not yet supported") << endmsg;
316 return;
319 _session->cancel_audition();
321 if (!Glib::file_test (path, Glib::FILE_TEST_EXISTS)) {
322 warning << string_compose(_("Could not read file: %1 (%2)."), path, strerror(errno)) << endmsg;
323 return;
326 boost::shared_ptr<Region> r;
327 SourceList srclist;
328 boost::shared_ptr<AudioFileSource> afs;
329 bool old_sbp = AudioSource::get_build_peakfiles ();
331 /* don't even think of building peakfiles for these files */
333 AudioSource::set_build_peakfiles (false);
335 for (int n = 0; n < sf_info.channels; ++n) {
336 try {
337 afs = boost::dynamic_pointer_cast<AudioFileSource> (
338 SourceFactory::createReadable (DataType::AUDIO, *_session,
339 path, n, Source::Flag (0), false));
341 srclist.push_back(afs);
343 } catch (failed_constructor& err) {
344 error << _("Could not access soundfile: ") << path << endmsg;
345 AudioSource::set_build_peakfiles (old_sbp);
346 return;
350 AudioSource::set_build_peakfiles (old_sbp);
352 if (srclist.empty()) {
353 return;
356 afs = boost::dynamic_pointer_cast<AudioFileSource> (srclist[0]);
357 string rname = region_name_from_path (afs->path(), false);
359 PropertyList plist;
361 plist.add (ARDOUR::Properties::start, 0);
362 plist.add (ARDOUR::Properties::length, srclist[0]->length(srclist[0]->timeline_position()));
363 plist.add (ARDOUR::Properties::name, rname);
364 plist.add (ARDOUR::Properties::layer, 0);
366 r = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (srclist, plist, false));
368 _session->audition_region(r);
371 void
372 SoundFileBox::stop_audition ()
374 if (_session) {
375 _session->cancel_audition();
379 bool
380 SoundFileBox::tags_entry_left (GdkEventFocus *)
382 tags_changed ();
383 return false;
386 void
387 SoundFileBox::tags_changed ()
389 string tag_string = tags_entry.get_buffer()->get_text ();
391 if (tag_string.empty()) {
392 return;
395 vector<string> tags;
397 if (!PBD::tokenize (tag_string, string(",\n"), std::back_inserter (tags), true)) {
398 warning << _("SoundFileBox: Could not tokenize string: ") << tag_string << endmsg;
399 return;
402 save_tags (tags);
405 void
406 SoundFileBox::save_tags (const vector<string>& tags)
408 Library->set_tags (string ("//") + path, tags);
409 Library->save_changes ();
412 SoundFileBrowser::SoundFileBrowser (Gtk::Window& parent, string title, ARDOUR::Session* s, bool persistent)
413 : ArdourDialog (parent, title, false, false),
414 found_list (ListStore::create(found_list_columns)),
415 freesound_list (ListStore::create(freesound_list_columns)),
416 chooser (FILE_CHOOSER_ACTION_OPEN),
417 preview (persistent),
418 found_search_btn (_("Search")),
419 found_list_view (found_list),
420 freesound_search_btn (_("Start Downloading")),
421 freesound_list_view (freesound_list)
423 resetting_ourselves = false;
424 gm = 0;
426 resetting_ourselves = false;
427 gm = 0;
429 #ifdef GTKOSX
430 chooser.add_shortcut_folder_uri("file:///Library/GarageBand/Apple Loops");
431 chooser.add_shortcut_folder_uri("file:///Library/Audio/Apple Loops");
432 chooser.add_shortcut_folder_uri("file:///Library/Application Support/GarageBand/Instrument Library/Sampler/Sampler Files");
434 chooser.add_shortcut_folder_uri("file:///Volumes");
435 #endif
437 //add the file chooser
439 chooser.set_border_width (12);
441 audio_filter.add_custom (FILE_FILTER_FILENAME, sigc::mem_fun(*this, &SoundFileBrowser::on_audio_filter));
442 audio_filter.set_name (_("Audio files"));
444 midi_filter.add_custom (FILE_FILTER_FILENAME, sigc::mem_fun(*this, &SoundFileBrowser::on_midi_filter));
445 midi_filter.set_name (_("MIDI files"));
447 matchall_filter.add_pattern ("*.*");
448 matchall_filter.set_name (_("All files"));
450 chooser.add_filter (audio_filter);
451 chooser.add_filter (midi_filter);
452 chooser.add_filter (matchall_filter);
453 chooser.set_select_multiple (true);
454 chooser.signal_update_preview().connect(sigc::mem_fun(*this, &SoundFileBrowser::update_preview));
455 chooser.signal_file_activated().connect (sigc::mem_fun (*this, &SoundFileBrowser::chooser_file_activated));
456 #ifdef GTKOSX
457 /* some broken redraw behaviour - this is a bandaid */
458 chooser.signal_selection_changed().connect (mem_fun (chooser, &Widget::queue_draw));
459 #endif
461 if (!persistent_folder.empty()) {
462 chooser.set_current_folder (persistent_folder);
464 notebook.append_page (chooser, _("Browse Files"));
467 hpacker.set_spacing (6);
468 hpacker.pack_start (notebook, true, true);
469 hpacker.pack_start (preview, false, false);
471 get_vbox()->pack_start (hpacker, true, true);
473 //add tag search
475 VBox* vbox;
476 HBox* hbox;
479 hbox = manage(new HBox);
480 hbox->pack_start (found_entry);
481 hbox->pack_start (found_search_btn);
483 Gtk::ScrolledWindow *scroll = manage(new ScrolledWindow);
484 scroll->add(found_list_view);
485 scroll->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
487 vbox = manage(new VBox);
488 vbox->pack_start (*hbox, PACK_SHRINK);
489 vbox->pack_start (*scroll);
491 found_list_view.append_column(_("Paths"), found_list_columns.pathname);
493 found_list_view.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &SoundFileBrowser::found_list_view_selected));
495 found_list_view.signal_row_activated().connect (sigc::mem_fun (*this, &SoundFileBrowser::found_list_view_activated));
497 found_search_btn.signal_clicked().connect(sigc::mem_fun(*this, &SoundFileBrowser::found_search_clicked));
498 found_entry.signal_activate().connect(sigc::mem_fun(*this, &SoundFileBrowser::found_search_clicked));
500 notebook.append_page (*vbox, _("Search Tags"));
503 //add freesound search
504 #ifdef FREESOUND
506 VBox* vbox;
507 HBox* passbox;
508 Label* label;
510 passbox = manage(new HBox);
511 passbox->set_border_width (12);
512 passbox->set_spacing (6);
514 label = manage (new Label);
515 label->set_text (_("User:"));
516 passbox->pack_start (*label, false, false);
517 passbox->pack_start (freesound_name_entry);
518 label = manage (new Label);
519 label->set_text (_("Password:"));
520 passbox->pack_start (*label, false, false);
521 passbox->pack_start (freesound_pass_entry);
522 label = manage (new Label);
523 label->set_text (_("Tags:"));
524 passbox->pack_start (*label, false, false);
525 passbox->pack_start (freesound_entry, false, false);
526 passbox->pack_start (freesound_search_btn, false, false);
528 Gtk::ScrolledWindow *scroll = manage(new ScrolledWindow);
529 scroll->add(freesound_list_view);
530 scroll->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
532 vbox = manage(new VBox);
533 vbox->pack_start (*passbox, PACK_SHRINK);
534 vbox->pack_start(*scroll);
536 //vbox->pack_start (freesound_list_view);
538 freesound_list_view.append_column(_("Paths"), freesound_list_columns.pathname);
539 freesound_list_view.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &SoundFileBrowser::freesound_list_view_selected));
541 //freesound_list_view.get_selection()->set_mode (SELECTION_MULTIPLE);
542 freesound_list_view.signal_row_activated().connect (sigc::mem_fun (*this, &SoundFileBrowser::freesound_list_view_activated));
543 freesound_search_btn.signal_clicked().connect(sigc::mem_fun(*this, &SoundFileBrowser::freesound_search_clicked));
544 freesound_entry.signal_activate().connect(sigc::mem_fun(*this, &SoundFileBrowser::freesound_search_clicked));
545 notebook.append_page (*vbox, _("Search Freesound"));
547 #endif
550 notebook.set_size_request (500, -1);
552 set_session (s);
554 add_button (Stock::CANCEL, RESPONSE_CANCEL);
555 add_button (Stock::APPLY, RESPONSE_APPLY);
556 add_button (Stock::OK, RESPONSE_OK);
560 SoundFileBrowser::~SoundFileBrowser ()
562 persistent_folder = chooser.get_current_folder();
566 void
567 SoundFileBrowser::on_show ()
569 ArdourDialog::on_show ();
570 start_metering ();
573 void
574 SoundFileBrowser::clear_selection ()
576 chooser.unselect_all ();
577 found_list_view.get_selection()->unselect_all ();
580 void
581 SoundFileBrowser::chooser_file_activated ()
583 preview.audition ();
586 void
587 SoundFileBrowser::found_list_view_activated (const TreeModel::Path&, TreeViewColumn*)
589 preview.audition ();
592 void
593 SoundFileBrowser::freesound_list_view_activated (const TreeModel::Path&, TreeViewColumn*)
595 preview.audition ();
598 void
599 SoundFileBrowser::set_session (Session* s)
601 ArdourDialog::set_session (s);
602 preview.set_session (s);
604 if (_session) {
605 add_gain_meter ();
606 } else {
607 remove_gain_meter ();
611 void
612 SoundFileBrowser::add_gain_meter ()
614 delete gm;
616 gm = new GainMeter (_session, 250);
618 boost::shared_ptr<Route> r = _session->the_auditioner ();
620 gm->set_controls (r, r->shared_peak_meter(), r->amp());
622 meter_packer.set_border_width (12);
623 meter_packer.pack_start (*gm, false, true);
624 hpacker.pack_end (meter_packer, false, false);
625 meter_packer.show_all ();
626 start_metering ();
629 void
630 SoundFileBrowser::remove_gain_meter ()
632 if (gm) {
633 meter_packer.remove (*gm);
634 hpacker.remove (meter_packer);
635 delete gm;
636 gm = 0;
640 void
641 SoundFileBrowser::start_metering ()
643 metering_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (sigc::mem_fun(*this, &SoundFileBrowser::meter));
646 void
647 SoundFileBrowser::stop_metering ()
649 metering_connection.disconnect();
652 void
653 SoundFileBrowser::meter ()
655 if (is_mapped () && _session && gm) {
656 gm->update_meters ();
660 bool
661 SoundFileBrowser::on_audio_filter (const FileFilter::Info& filter_info)
663 return AudioFileSource::safe_audio_file_extension (filter_info.filename);
666 bool
667 SoundFileBrowser::on_midi_filter (const FileFilter::Info& filter_info)
669 return SMFSource::safe_midi_file_extension (filter_info.filename);
672 void
673 SoundFileBrowser::update_preview ()
675 if (preview.setup_labels (chooser.get_filename())) {
676 if (preview.autoplay()) {
677 Glib::signal_idle().connect (sigc::mem_fun (preview, &SoundFileBox::audition_oneshot));
682 void
683 SoundFileBrowser::found_list_view_selected ()
685 if (!reset_options ()) {
686 set_response_sensitive (RESPONSE_OK, false);
687 } else {
688 string file;
690 TreeView::Selection::ListHandle_Path rows = found_list_view.get_selection()->get_selected_rows ();
692 if (!rows.empty()) {
693 TreeIter iter = found_list->get_iter(*rows.begin());
694 file = (*iter)[found_list_columns.pathname];
695 chooser.set_filename (file);
696 set_response_sensitive (RESPONSE_OK, true);
697 } else {
698 set_response_sensitive (RESPONSE_OK, false);
701 preview.setup_labels (file);
705 void
706 SoundFileBrowser::freesound_list_view_selected ()
708 if (!reset_options ()) {
709 set_response_sensitive (RESPONSE_OK, false);
710 } else {
711 string file;
713 TreeView::Selection::ListHandle_Path rows = freesound_list_view.get_selection()->get_selected_rows ();
715 if (!rows.empty()) {
716 TreeIter iter = freesound_list->get_iter(*rows.begin());
717 file = (*iter)[freesound_list_columns.pathname];
718 chooser.set_filename (file);
719 set_response_sensitive (RESPONSE_OK, true);
720 } else {
721 set_response_sensitive (RESPONSE_OK, false);
724 preview.setup_labels (file);
728 void
729 SoundFileBrowser::found_search_clicked ()
731 string tag_string = found_entry.get_text ();
733 vector<string> tags;
735 if (!PBD::tokenize (tag_string, string(","), std::back_inserter (tags), true)) {
736 warning << _("SoundFileBrowser: Could not tokenize string: ") << tag_string << endmsg;
737 return;
740 vector<string> results;
741 Library->search_members_and (results, tags);
743 found_list->clear();
744 for (vector<string>::iterator i = results.begin(); i != results.end(); ++i) {
745 TreeModel::iterator new_row = found_list->append();
746 TreeModel::Row row = *new_row;
747 string path = Glib::filename_from_uri (string ("file:") + *i);
748 row[found_list_columns.pathname] = path;
752 void*
753 freesound_search_thread_entry (void* arg)
755 SessionEvent::create_per_thread_pool ("freesound events", 64);
757 static_cast<SoundFileBrowser*>(arg)->freesound_search_thread ();
759 return 0;
762 bool searching = false;
763 bool canceling = false;
765 void
766 SoundFileBrowser::freesound_search_clicked ()
768 if (canceling) //already canceling, button does nothing
769 return;
771 if ( searching ) {
772 freesound_search_btn.set_label(_("Cancelling.."));
773 canceling = true;
774 } else {
775 searching = true;
776 freesound_search_btn.set_label(_("Cancel"));
777 pthread_t freesound_thr;
778 pthread_create_and_store ("freesound_search", &freesound_thr, freesound_search_thread_entry, this);
782 void
783 SoundFileBrowser::freesound_search_thread()
785 #if 0
787 THIS IS ALL TOTALLY THREAD-ILLEGAL ... YOU CANNOT DO GTK STUFF IN THIS THREAD
789 #ifdef FREESOUND
790 freesound_list->clear();
792 string path;
793 path = Glib::get_home_dir();
794 path += "/Freesound/";
795 Mootcher theMootcher(path.c_str());
797 string name_string = freesound_name_entry.get_text ();
798 string pass_string = freesound_pass_entry.get_text ();
799 string search_string = freesound_entry.get_text ();
801 if ( theMootcher.doLogin( name_string, pass_string ) ) {
803 string theString = theMootcher.searchText(search_string);
805 XMLTree doc;
806 doc.read_buffer( theString );
807 XMLNode *root = doc.root();
809 if (root==NULL) return;
811 if ( strcmp(root->name().c_str(), "freesound") == 0) {
813 XMLNode *node = 0;
814 XMLNodeList children = root->children();
815 XMLNodeConstIterator niter;
816 for (niter = children.begin(); niter != children.end() && !canceling; ++niter) {
817 node = *niter;
818 if( strcmp( node->name().c_str(), "sample") == 0 ){
819 XMLProperty *prop=node->property ("id");
820 string filename = theMootcher.getFile( prop->value().c_str() );
821 if ( filename != "" ) {
822 TreeModel::iterator new_row = freesound_list->append();
823 TreeModel::Row row = *new_row;
824 string path = Glib::filename_from_uri (string ("file:") + filename);
825 row[freesound_list_columns.pathname] = path;
832 searching = false;
833 canceling = false;
834 freesound_search_btn.set_label(_("Start Downloading"));
835 #endif
836 #endif
840 vector<string>
841 SoundFileBrowser::get_paths ()
843 vector<string> results;
845 int n = notebook.get_current_page ();
847 if (n == 0) {
848 vector<string> filenames = chooser.get_filenames();
849 vector<string>::iterator i;
851 for (i = filenames.begin(); i != filenames.end(); ++i) {
852 struct stat buf;
853 if ((!stat((*i).c_str(), &buf)) && S_ISREG(buf.st_mode)) {
854 results.push_back (*i);
858 } else if (n==1){
860 typedef TreeView::Selection::ListHandle_Path ListPath;
862 ListPath rows = found_list_view.get_selection()->get_selected_rows ();
863 for (ListPath::iterator i = rows.begin() ; i != rows.end(); ++i) {
864 TreeIter iter = found_list->get_iter(*i);
865 string str = (*iter)[found_list_columns.pathname];
867 results.push_back (str);
869 } else {
871 typedef TreeView::Selection::ListHandle_Path ListPath;
873 ListPath rows = freesound_list_view.get_selection()->get_selected_rows ();
874 for (ListPath::iterator i = rows.begin() ; i != rows.end(); ++i) {
875 TreeIter iter = freesound_list->get_iter(*i);
876 string str = (*iter)[freesound_list_columns.pathname];
878 results.push_back (str);
882 return results;
885 void
886 SoundFileOmega::reset_options_noret ()
888 if (!resetting_ourselves) {
889 (void) reset_options ();
893 bool
894 SoundFileOmega::reset_options ()
896 vector<string> paths = get_paths ();
898 if (paths.empty()) {
900 channel_combo.set_sensitive (false);
901 action_combo.set_sensitive (false);
902 where_combo.set_sensitive (false);
903 copy_files_btn.set_sensitive (false);
905 return false;
907 } else {
909 channel_combo.set_sensitive (true);
910 action_combo.set_sensitive (true);
911 where_combo.set_sensitive (true);
913 /* if we get through this function successfully, this may be
914 reset at the end, once we know if we can use hard links
915 to do embedding
918 if (Config->get_only_copy_imported_files()) {
919 copy_files_btn.set_sensitive (false);
920 } else {
921 copy_files_btn.set_sensitive (false);
925 bool same_size;
926 bool src_needed;
927 bool selection_includes_multichannel;
928 bool selection_can_be_embedded_with_links = check_link_status (_session, paths);
929 ImportMode mode;
931 if (check_info (paths, same_size, src_needed, selection_includes_multichannel)) {
932 Glib::signal_idle().connect (sigc::mem_fun (*this, &SoundFileOmega::bad_file_message));
933 return false;
936 string existing_choice;
937 vector<string> action_strings;
939 if (selected_track_cnt > 0) {
940 if (channel_combo.get_active_text().length()) {
941 ImportDisposition id = get_channel_disposition();
943 switch (id) {
944 case Editing::ImportDistinctFiles:
945 if (selected_track_cnt == paths.size()) {
946 action_strings.push_back (importmode2string (ImportToTrack));
948 break;
950 case Editing::ImportDistinctChannels:
951 /* XXX it would be nice to allow channel-per-selected track
952 but its too hard we don't want to deal with all the
953 different per-file + per-track channel configurations.
955 break;
957 default:
958 action_strings.push_back (importmode2string (ImportToTrack));
959 break;
964 action_strings.push_back (importmode2string (ImportAsTrack));
965 action_strings.push_back (importmode2string (ImportAsRegion));
966 action_strings.push_back (importmode2string (ImportAsTapeTrack));
968 resetting_ourselves = true;
970 existing_choice = action_combo.get_active_text();
972 set_popdown_strings (action_combo, action_strings);
974 /* preserve any existing choice, if possible */
977 if (existing_choice.length()) {
978 vector<string>::iterator x;
979 for (x = action_strings.begin(); x != action_strings.end(); ++x) {
980 if (*x == existing_choice) {
981 action_combo.set_active_text (existing_choice);
982 break;
985 if (x == action_strings.end()) {
986 action_combo.set_active_text (action_strings.front());
988 } else {
989 action_combo.set_active_text (action_strings.front());
992 resetting_ourselves = false;
994 if ((mode = get_mode()) == ImportAsRegion) {
995 where_combo.set_sensitive (false);
996 } else {
997 where_combo.set_sensitive (true);
1000 vector<string> channel_strings;
1002 if (mode == ImportAsTrack || mode == ImportAsTapeTrack || mode == ImportToTrack) {
1003 channel_strings.push_back (_("one track per file"));
1005 if (selection_includes_multichannel) {
1006 channel_strings.push_back (_("one track per channel"));
1009 if (paths.size() > 1) {
1010 /* tape tracks are a single region per track, so we cannot
1011 sequence multiple files.
1013 if (mode != ImportAsTapeTrack) {
1014 channel_strings.push_back (_("sequence files"));
1016 if (same_size) {
1017 channel_strings.push_back (_("all files in one track"));
1018 channel_strings.push_back (_("merge files"));
1023 } else {
1024 channel_strings.push_back (_("one region per file"));
1026 if (selection_includes_multichannel) {
1027 channel_strings.push_back (_("one region per channel"));
1030 if (paths.size() > 1) {
1031 if (same_size) {
1032 channel_strings.push_back (_("all files in one region"));
1037 resetting_ourselves = true;
1039 existing_choice = channel_combo.get_active_text();
1041 set_popdown_strings (channel_combo, channel_strings);
1043 /* preserve any existing choice, if possible */
1045 if (existing_choice.length()) {
1046 vector<string>::iterator x;
1047 for (x = channel_strings.begin(); x != channel_strings.end(); ++x) {
1048 if (*x == existing_choice) {
1049 channel_combo.set_active_text (existing_choice);
1050 break;
1053 if (x == channel_strings.end()) {
1054 channel_combo.set_active_text (channel_strings.front());
1056 } else {
1057 channel_combo.set_active_text (channel_strings.front());
1060 resetting_ourselves = false;
1062 if (src_needed) {
1063 src_combo.set_sensitive (true);
1064 } else {
1065 src_combo.set_sensitive (false);
1068 if (Config->get_only_copy_imported_files()) {
1070 if (selection_can_be_embedded_with_links) {
1071 copy_files_btn.set_sensitive (true);
1072 } else {
1073 copy_files_btn.set_sensitive (false);
1076 } else {
1078 copy_files_btn.set_sensitive (true);
1081 return true;
1085 bool
1086 SoundFileOmega::bad_file_message()
1088 MessageDialog msg (*this,
1089 string_compose (_("One or more of the selected files\ncannot be used by %1"), PROGRAM_NAME),
1090 true,
1091 Gtk::MESSAGE_INFO,
1092 Gtk::BUTTONS_OK);
1093 msg.run ();
1094 resetting_ourselves = true;
1095 chooser.unselect_uri (chooser.get_preview_uri());
1096 resetting_ourselves = false;
1098 return false;
1101 bool
1102 SoundFileOmega::check_info (const vector<string>& paths, bool& same_size, bool& src_needed, bool& multichannel)
1104 SoundFileInfo info;
1105 framepos_t sz = 0;
1106 bool err = false;
1107 string errmsg;
1109 same_size = true;
1110 src_needed = false;
1111 multichannel = false;
1113 for (vector<string>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
1115 if (AudioFileSource::get_soundfile_info (*i, info, errmsg)) {
1116 if (info.channels > 1) {
1117 multichannel = true;
1119 if (sz == 0) {
1120 sz = info.length;
1121 } else {
1122 if (sz != info.length) {
1123 same_size = false;
1127 if ((nframes_t) info.samplerate != _session->frame_rate()) {
1128 src_needed = true;
1131 } else if (SMFSource::safe_midi_file_extension (*i)) {
1133 Evoral::SMF reader;
1134 reader.open(*i);
1135 if (reader.num_tracks() > 1) {
1136 multichannel = true; // "channel" == track here...
1139 /* XXX we need err = true handling here in case
1140 we can't check the file
1143 } else {
1144 err = true;
1148 return err;
1152 bool
1153 SoundFileOmega::check_link_status (const Session* s, const vector<string>& paths)
1155 sys::path path = s->session_directory().sound_path() / "linktest";
1156 string tmpdir = path.to_string();
1157 bool ret = false;
1159 if (mkdir (tmpdir.c_str(), 0744)) {
1160 if (errno != EEXIST) {
1161 return false;
1165 for (vector<string>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
1167 char tmpc[MAXPATHLEN+1];
1169 snprintf (tmpc, sizeof(tmpc), "%s/%s", tmpdir.c_str(), Glib::path_get_basename (*i).c_str());
1171 /* can we link ? */
1173 if (link ((*i).c_str(), tmpc)) {
1174 goto out;
1177 unlink (tmpc);
1180 ret = true;
1182 out:
1183 rmdir (tmpdir.c_str());
1184 return ret;
1187 SoundFileChooser::SoundFileChooser (Gtk::Window& parent, string title, ARDOUR::Session* s)
1188 : SoundFileBrowser (parent, title, s, false)
1190 chooser.set_select_multiple (false);
1191 found_list_view.get_selection()->set_mode (SELECTION_SINGLE);
1192 freesound_list_view.get_selection()->set_mode (SELECTION_SINGLE);
1195 void
1196 SoundFileChooser::on_hide ()
1198 ArdourDialog::on_hide();
1199 stop_metering ();
1201 if (_session) {
1202 _session->cancel_audition();
1206 string
1207 SoundFileChooser::get_filename ()
1209 vector<string> paths;
1211 paths = get_paths ();
1213 if (paths.empty()) {
1214 return string ();
1217 if (!Glib::file_test (paths.front(), Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
1218 return string();
1221 return paths.front();
1224 SoundFileOmega::SoundFileOmega (Gtk::Window& parent, string title, ARDOUR::Session* s, int selected_tracks, bool persistent,
1225 Editing::ImportMode mode_hint)
1226 : SoundFileBrowser (parent, title, s, persistent),
1227 copy_files_btn ( _("Copy files to session")),
1228 selected_track_cnt (selected_tracks)
1230 VBox* vbox;
1231 HBox* hbox;
1232 vector<string> str;
1234 set_size_request (-1, 450);
1236 block_two.set_border_width (12);
1237 block_three.set_border_width (12);
1238 block_four.set_border_width (12);
1240 options.set_spacing (12);
1242 str.clear ();
1243 str.push_back (_("file timestamp"));
1244 str.push_back (_("edit point"));
1245 str.push_back (_("playhead"));
1246 str.push_back (_("session start"));
1247 set_popdown_strings (where_combo, str);
1248 where_combo.set_active_text (str.front());
1250 Label* l = manage (new Label);
1251 l->set_text (_("Add files:"));
1253 hbox = manage (new HBox);
1254 hbox->set_border_width (12);
1255 hbox->set_spacing (6);
1256 hbox->pack_start (*l, false, false);
1257 hbox->pack_start (action_combo, false, false);
1258 vbox = manage (new VBox);
1259 vbox->pack_start (*hbox, false, false);
1260 options.pack_start (*vbox, false, false);
1262 /* dummy entry for action combo so that it doesn't look odd if we
1263 come up with no tracks selected.
1266 str.clear ();
1267 str.push_back (importmode2string (mode_hint));
1268 set_popdown_strings (action_combo, str);
1269 action_combo.set_active_text (str.front());
1270 action_combo.set_sensitive (false);
1272 l = manage (new Label);
1273 l->set_text (_("Insert at:"));
1275 hbox = manage (new HBox);
1276 hbox->set_border_width (12);
1277 hbox->set_spacing (6);
1278 hbox->pack_start (*l, false, false);
1279 hbox->pack_start (where_combo, false, false);
1280 vbox = manage (new VBox);
1281 vbox->pack_start (*hbox, false, false);
1282 options.pack_start (*vbox, false, false);
1285 l = manage (new Label);
1286 l->set_text (_("Mapping:"));
1288 hbox = manage (new HBox);
1289 hbox->set_border_width (12);
1290 hbox->set_spacing (6);
1291 hbox->pack_start (*l, false, false);
1292 hbox->pack_start (channel_combo, false, false);
1293 vbox = manage (new VBox);
1294 vbox->pack_start (*hbox, false, false);
1295 options.pack_start (*vbox, false, false);
1297 str.clear ();
1298 str.push_back (_("one track per file"));
1299 set_popdown_strings (channel_combo, str);
1300 channel_combo.set_active_text (str.front());
1301 channel_combo.set_sensitive (false);
1303 l = manage (new Label);
1304 l->set_text (_("Conversion quality:"));
1306 hbox = manage (new HBox);
1307 hbox->set_border_width (12);
1308 hbox->set_spacing (6);
1309 hbox->pack_start (*l, false, false);
1310 hbox->pack_start (src_combo, false, false);
1311 vbox = manage (new VBox);
1312 vbox->pack_start (*hbox, false, false);
1313 options.pack_start (*vbox, false, false);
1315 str.clear ();
1316 str.push_back (_("Best"));
1317 str.push_back (_("Good"));
1318 str.push_back (_("Quick"));
1319 str.push_back (_("Fast"));
1320 str.push_back (_("Fastest"));
1322 set_popdown_strings (src_combo, str);
1323 src_combo.set_active_text (str.front());
1324 src_combo.set_sensitive (false);
1326 reset_options ();
1328 action_combo.signal_changed().connect (sigc::mem_fun (*this, &SoundFileOmega::reset_options_noret));
1329 channel_combo.signal_changed().connect (sigc::mem_fun (*this, &SoundFileOmega::reset_options_noret));
1331 copy_files_btn.set_active (true);
1333 block_four.pack_start (copy_files_btn, false, false);
1335 options.pack_start (block_four, false, false);
1337 get_vbox()->pack_start (options, false, false);
1339 /* setup disposition map */
1341 disposition_map.insert (pair<string,ImportDisposition>(_("one track per file"), ImportDistinctFiles));
1342 disposition_map.insert (pair<string,ImportDisposition>(_("one track per channel"), ImportDistinctChannels));
1343 disposition_map.insert (pair<string,ImportDisposition>(_("merge files"), ImportMergeFiles));
1344 disposition_map.insert (pair<string,ImportDisposition>(_("sequence files"), ImportSerializeFiles));
1346 disposition_map.insert (pair<string,ImportDisposition>(_("one region per file"), ImportDistinctFiles));
1347 disposition_map.insert (pair<string,ImportDisposition>(_("one region per channel"), ImportDistinctChannels));
1348 disposition_map.insert (pair<string,ImportDisposition>(_("all files in one region"), ImportMergeFiles));
1349 disposition_map.insert (pair<string,ImportDisposition>(_("all files in one track"), ImportMergeFiles));
1351 chooser.signal_selection_changed().connect (sigc::mem_fun (*this, &SoundFileOmega::file_selection_changed));
1353 /* set size requests for a couple of combos to allow them to display the longest text
1354 they will ever be asked to display. This prevents them being resized when the user
1355 selects a file to import, which in turn prevents the size of the dialog from jumping
1356 around. */
1358 vector<string> t;
1359 t.push_back (_("one track per file"));
1360 t.push_back (_("one track per channel"));
1361 t.push_back (_("sequence files"));
1362 t.push_back (_("all files in one region"));
1363 set_size_request_to_display_given_text (channel_combo, t, COMBO_FUDGE + 10, 15);
1365 t.clear ();
1366 t.push_back (importmode2string (ImportAsTrack));
1367 t.push_back (importmode2string (ImportToTrack));
1368 t.push_back (importmode2string (ImportAsRegion));
1369 t.push_back (importmode2string (ImportAsTapeTrack));
1370 set_size_request_to_display_given_text (action_combo, t, COMBO_FUDGE + 10, 15);
1373 void
1374 SoundFileOmega::set_mode (ImportMode mode)
1376 action_combo.set_active_text (importmode2string (mode));
1379 ImportMode
1380 SoundFileOmega::get_mode () const
1382 return string2importmode (action_combo.get_active_text());
1385 void
1386 SoundFileOmega::on_hide ()
1388 ArdourDialog::on_hide();
1389 if (_session) {
1390 _session->cancel_audition();
1394 ImportPosition
1395 SoundFileOmega::get_position() const
1397 string str = where_combo.get_active_text();
1399 if (str == _("file timestamp")) {
1400 return ImportAtTimestamp;
1401 } else if (str == _("edit point")) {
1402 return ImportAtEditPoint;
1403 } else if (str == _("playhead")) {
1404 return ImportAtPlayhead;
1405 } else {
1406 return ImportAtStart;
1410 SrcQuality
1411 SoundFileOmega::get_src_quality() const
1413 string str = where_combo.get_active_text();
1415 if (str == _("Best")) {
1416 return SrcBest;
1417 } else if (str == _("Good")) {
1418 return SrcGood;
1419 } else if (str == _("Quick")) {
1420 return SrcQuick;
1421 } else if (str == _("Fast")) {
1422 return SrcFast;
1423 } else {
1424 return SrcFastest;
1428 ImportDisposition
1429 SoundFileOmega::get_channel_disposition () const
1431 /* we use a map here because the channel combo can contain different strings
1432 depending on the state of the other combos. the map contains all possible strings
1433 and the ImportDisposition enum that corresponds to it.
1436 string str = channel_combo.get_active_text();
1437 DispositionMap::const_iterator x = disposition_map.find (str);
1439 if (x == disposition_map.end()) {
1440 fatal << string_compose (_("programming error: %1 (%2)"), "unknown string for import disposition", str) << endmsg;
1441 /*NOTREACHED*/
1444 return x->second;
1447 void
1448 SoundFileOmega::reset (int selected_tracks)
1450 selected_track_cnt = selected_tracks;
1451 reset_options ();
1454 void
1455 SoundFileOmega::file_selection_changed ()
1457 if (resetting_ourselves) {
1458 return;
1461 if (!reset_options ()) {
1462 set_response_sensitive (RESPONSE_OK, false);
1463 } else {
1464 if (chooser.get_filenames().size() > 0) {
1465 set_response_sensitive (RESPONSE_OK, true);
1466 } else {
1467 set_response_sensitive (RESPONSE_OK, false);