Zoom session when the mouse pointer is moved up and down during a playhead drag.
[ardour2.git] / gtk2_ardour / sfdb_ui.cc
blobbd19db31f04ba275cc413be8ae3799289b7da9d4
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 #ifdef WAF_BUILD
21 #include "gtk2ardour-config.h"
22 #endif
24 #include <map>
25 #include <cerrno>
26 #include <sstream>
28 #include <unistd.h>
29 #include <sys/stat.h>
30 #include <sys/param.h>
32 #include <gtkmm/box.h>
33 #include <gtkmm/stock.h>
34 #include <glibmm/fileutils.h>
36 #include "pbd/convert.h"
37 #include "pbd/tokenizer.h"
38 #include "pbd/enumwriter.h"
39 #include "pbd/pthread_utils.h"
40 #include "pbd/xml++.h"
42 #include <gtkmm2ext/utils.h>
44 #include "evoral/SMF.hpp"
46 #include "ardour/amp.h"
47 #include "ardour/audio_library.h"
48 #include "ardour/auditioner.h"
49 #include "ardour/audioregion.h"
50 #include "ardour/audiofilesource.h"
51 #include "ardour/smf_source.h"
52 #include "ardour/region_factory.h"
53 #include "ardour/source_factory.h"
54 #include "ardour/session.h"
55 #include "ardour/session_directory.h"
56 #include "ardour/profile.h"
58 #include "ardour_ui.h"
59 #include "editing.h"
60 #include "gui_thread.h"
61 #include "prompter.h"
62 #include "sfdb_ui.h"
63 #include "editing.h"
64 #include "utils.h"
65 #include "gain_meter.h"
67 #ifdef FREESOUND
68 #include "sfdb_freesound_mootcher.h"
69 #endif
71 #include "i18n.h"
73 using namespace ARDOUR;
74 using namespace PBD;
75 using namespace std;
76 using namespace Gtk;
77 using namespace Gtkmm2ext;
78 using namespace Editing;
80 using std::string;
82 string SoundFileBrowser::persistent_folder;
84 static ImportMode
85 string2importmode (string str)
87 if (str == _("as new tracks")) {
88 return ImportAsTrack;
89 } else if (str == _("to selected tracks")) {
90 return ImportToTrack;
91 } else if (str == _("to region list")) {
92 return ImportAsRegion;
93 } else if (str == _("as new tape tracks")) {
94 return ImportAsTapeTrack;
97 warning << string_compose (_("programming error: unknown import mode string %1"), str) << endmsg;
99 return ImportAsTrack;
102 static string
103 importmode2string (ImportMode mode)
105 switch (mode) {
106 case ImportAsTrack:
107 return _("as new tracks");
108 case ImportToTrack:
109 return _("to selected tracks");
110 case ImportAsRegion:
111 return _("to region list");
112 case ImportAsTapeTrack:
113 return _("as new tape tracks");
115 /*NOTREACHED*/
116 return _("as new tracks");
119 SoundFileBox::SoundFileBox (bool persistent)
120 : table (6, 2),
121 length_clock ("sfboxLengthClock", !persistent, "EditCursorClock", false, false, true, false),
122 timecode_clock ("sfboxTimecodeClock", !persistent, "EditCursorClock", false, false, false, false),
123 main_box (false, 6),
124 autoplay_btn (_("Auto-play"))
127 set_name (X_("SoundFileBox"));
128 set_size_request (300, -1);
130 preview_label.set_markup (_("<b>Sound File Information</b>"));
132 border_frame.set_label_widget (preview_label);
133 border_frame.add (main_box);
135 pack_start (border_frame, true, true);
136 set_border_width (6);
138 main_box.set_border_width (6);
140 length.set_text (_("Length:"));
141 length.set_alignment (1, 0.5);
142 timecode.set_text (_("Timestamp:"));
143 timecode.set_alignment (1, 0.5);
144 format.set_text (_("Format:"));
145 format.set_alignment (1, 0.5);
146 channels.set_text (_("Channels:"));
147 channels.set_alignment (1, 0.5);
148 samplerate.set_text (_("Sample rate:"));
149 samplerate.set_alignment (1, 0.5);
151 preview_label.set_max_width_chars (50);
152 preview_label.set_ellipsize (Pango::ELLIPSIZE_END);
154 format_text.set_max_width_chars (20);
155 format_text.set_ellipsize (Pango::ELLIPSIZE_END);
156 format_text.set_alignment (0, 1);
158 table.set_col_spacings (6);
159 table.set_homogeneous (false);
160 table.set_row_spacings (6);
162 table.attach (channels, 0, 1, 0, 1, FILL, FILL);
163 table.attach (samplerate, 0, 1, 1, 2, FILL, FILL);
164 table.attach (format, 0, 1, 2, 4, FILL, FILL);
165 table.attach (length, 0, 1, 4, 5, FILL, FILL);
166 table.attach (timecode, 0, 1, 5, 6, FILL, FILL);
168 table.attach (channels_value, 1, 2, 0, 1, FILL, FILL);
169 table.attach (samplerate_value, 1, 2, 1, 2, FILL, FILL);
170 table.attach (format_text, 1, 2, 2, 4, FILL, FILL);
171 table.attach (length_clock, 1, 2, 4, 5, FILL, FILL);
172 table.attach (timecode_clock, 1, 2, 5, 6, FILL, FILL);
174 length_clock.set_mode (ARDOUR_UI::instance()->secondary_clock.mode());
175 timecode_clock.set_mode (AudioClock::Timecode);
177 main_box.pack_start (table, false, false);
179 tags_entry.set_editable (true);
180 tags_entry.signal_focus_out_event().connect (sigc::mem_fun (*this, &SoundFileBox::tags_entry_left));
182 Label* label = manage (new Label (_("Tags:")));
183 label->set_alignment (0.0f, 0.5f);
184 main_box.pack_start (*label, false, false);
185 main_box.pack_start (tags_entry, true, true);
187 main_box.pack_start (bottom_box, false, false);
189 play_btn.set_image (*(manage (new Image (Stock::MEDIA_PLAY, ICON_SIZE_BUTTON))));
190 play_btn.set_label (_("Play"));
192 stop_btn.set_image (*(manage (new Image (Stock::MEDIA_STOP, ICON_SIZE_BUTTON))));
193 stop_btn.set_label (_("Stop"));
195 bottom_box.set_homogeneous (false);
196 bottom_box.set_spacing (6);
197 bottom_box.pack_start(play_btn, true, true);
198 bottom_box.pack_start(stop_btn, true, true);
199 bottom_box.pack_start(autoplay_btn, false, false);
201 play_btn.signal_clicked().connect (sigc::mem_fun (*this, &SoundFileBox::audition));
202 stop_btn.signal_clicked().connect (sigc::mem_fun (*this, &SoundFileBox::stop_audition));
204 channels_value.set_alignment (0.0f, 0.5f);
205 samplerate_value.set_alignment (0.0f, 0.5f);
208 void
209 SoundFileBox::set_session(Session* s)
211 SessionHandlePtr::set_session (s);
213 length_clock.set_session (s);
214 timecode_clock.set_session (s);
216 if (!_session) {
217 play_btn.set_sensitive (false);
218 stop_btn.set_sensitive (false);
222 bool
223 SoundFileBox::setup_labels (const string& filename)
225 if (!path.empty()) {
226 // save existing tags
227 tags_changed ();
230 path = filename;
232 string error_msg;
234 if(!AudioFileSource::get_soundfile_info (filename, sf_info, error_msg)) {
236 preview_label.set_markup (_("<b>Sound File Information</b>"));
237 format_text.set_text ("");
238 channels_value.set_text ("");
239 samplerate_value.set_text ("");
240 tags_entry.get_buffer()->set_text ("");
242 length_clock.set (0);
243 timecode_clock.set (0);
245 tags_entry.set_sensitive (false);
246 play_btn.set_sensitive (false);
248 return false;
251 preview_label.set_markup (string_compose ("<b>%1</b>", Glib::path_get_basename (filename)));
252 std::string n = sf_info.format_name;
253 if (n.substr (0, 8) == X_("Format: ")) {
254 n = n.substr (8);
256 format_text.set_text (n);
257 channels_value.set_text (to_string (sf_info.channels, std::dec));
259 if (_session && sf_info.samplerate != _session->frame_rate()) {
260 samplerate.set_markup (string_compose ("<b>%1</b>", _("Sample rate:")));
261 samplerate_value.set_markup (string_compose (X_("<b>%1 Hz</b>"), sf_info.samplerate));
262 samplerate_value.set_name ("NewSessionSR1Label");
263 samplerate.set_name ("NewSessionSR1Label");
264 } else {
265 samplerate.set_text (_("Sample rate:"));
266 samplerate_value.set_text (string_compose (X_("%1 Hz"), sf_info.samplerate));
267 samplerate_value.set_name ("NewSessionSR2Label");
268 samplerate.set_name ("NewSessionSR2Label");
271 framecnt_t const nfr = _session ? _session->nominal_frame_rate() : 25;
272 double src_coef = (double) nfr / sf_info.samplerate;
274 length_clock.set (sf_info.length * src_coef + 0.5, true);
275 timecode_clock.set (sf_info.timecode * src_coef + 0.5, true);
277 // this is a hack that is fixed in trunk, i think (august 26th, 2007)
279 vector<string> tags = Library->get_tags (string ("//") + filename);
281 stringstream tag_string;
282 for (vector<string>::iterator i = tags.begin(); i != tags.end(); ++i) {
283 if (i != tags.begin()) {
284 tag_string << ", ";
286 tag_string << *i;
288 tags_entry.get_buffer()->set_text (tag_string.str());
290 tags_entry.set_sensitive (true);
291 if (_session) {
292 play_btn.set_sensitive (true);
295 return true;
298 bool
299 SoundFileBox::autoplay() const
301 return autoplay_btn.get_active();
304 bool
305 SoundFileBox::audition_oneshot()
307 audition ();
308 return false;
311 void
312 SoundFileBox::audition ()
314 if (!_session) {
315 return;
318 if (SMFSource::safe_midi_file_extension (path)) {
319 error << _("Auditioning of MIDI files is not yet supported") << endmsg;
320 return;
323 _session->cancel_audition();
325 if (!Glib::file_test (path, Glib::FILE_TEST_EXISTS)) {
326 warning << string_compose(_("Could not read file: %1 (%2)."), path, strerror(errno)) << endmsg;
327 return;
330 boost::shared_ptr<Region> r;
331 SourceList srclist;
332 boost::shared_ptr<AudioFileSource> afs;
333 bool old_sbp = AudioSource::get_build_peakfiles ();
335 /* don't even think of building peakfiles for these files */
337 AudioSource::set_build_peakfiles (false);
339 for (int n = 0; n < sf_info.channels; ++n) {
340 try {
341 afs = boost::dynamic_pointer_cast<AudioFileSource> (
342 SourceFactory::createReadable (DataType::AUDIO, *_session,
343 path, n, Source::Flag (0), false));
345 srclist.push_back(afs);
347 } catch (failed_constructor& err) {
348 error << _("Could not access soundfile: ") << path << endmsg;
349 AudioSource::set_build_peakfiles (old_sbp);
350 return;
354 AudioSource::set_build_peakfiles (old_sbp);
356 if (srclist.empty()) {
357 return;
360 afs = boost::dynamic_pointer_cast<AudioFileSource> (srclist[0]);
361 string rname = region_name_from_path (afs->path(), false);
363 PropertyList plist;
365 plist.add (ARDOUR::Properties::start, 0);
366 plist.add (ARDOUR::Properties::length, srclist[0]->length(srclist[0]->timeline_position()));
367 plist.add (ARDOUR::Properties::name, rname);
368 plist.add (ARDOUR::Properties::layer, 0);
370 r = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (srclist, plist, false));
372 _session->audition_region(r);
375 void
376 SoundFileBox::stop_audition ()
378 if (_session) {
379 _session->cancel_audition();
383 bool
384 SoundFileBox::tags_entry_left (GdkEventFocus *)
386 tags_changed ();
387 return false;
390 void
391 SoundFileBox::tags_changed ()
393 string tag_string = tags_entry.get_buffer()->get_text ();
395 if (tag_string.empty()) {
396 return;
399 vector<string> tags;
401 if (!PBD::tokenize (tag_string, string(",\n"), std::back_inserter (tags), true)) {
402 warning << _("SoundFileBox: Could not tokenize string: ") << tag_string << endmsg;
403 return;
406 save_tags (tags);
409 void
410 SoundFileBox::save_tags (const vector<string>& tags)
412 Library->set_tags (string ("//") + path, tags);
413 Library->save_changes ();
416 SoundFileBrowser::SoundFileBrowser (Gtk::Window& parent, string title, ARDOUR::Session* s, bool persistent)
417 : ArdourDialog (parent, title, false, false),
418 found_list (ListStore::create(found_list_columns)),
419 freesound_list (ListStore::create(freesound_list_columns)),
420 chooser (FILE_CHOOSER_ACTION_OPEN),
421 preview (persistent),
422 found_search_btn (_("Search")),
423 found_list_view (found_list),
424 freesound_search_btn (_("Start Downloading")),
425 freesound_list_view (freesound_list)
427 resetting_ourselves = false;
428 gm = 0;
430 resetting_ourselves = false;
431 gm = 0;
433 #ifdef GTKOSX
434 chooser.add_shortcut_folder_uri("file:///Library/GarageBand/Apple Loops");
435 chooser.add_shortcut_folder_uri("file:///Library/Audio/Apple Loops");
436 chooser.add_shortcut_folder_uri("file:///Library/Application Support/GarageBand/Instrument Library/Sampler/Sampler Files");
438 chooser.add_shortcut_folder_uri("file:///Volumes");
439 #endif
441 //add the file chooser
443 chooser.set_border_width (12);
445 audio_filter.add_custom (FILE_FILTER_FILENAME, sigc::mem_fun(*this, &SoundFileBrowser::on_audio_filter));
446 audio_filter.set_name (_("Audio files"));
448 midi_filter.add_custom (FILE_FILTER_FILENAME, sigc::mem_fun(*this, &SoundFileBrowser::on_midi_filter));
449 midi_filter.set_name (_("MIDI files"));
451 matchall_filter.add_pattern ("*.*");
452 matchall_filter.set_name (_("All files"));
454 chooser.add_filter (audio_filter);
455 chooser.add_filter (midi_filter);
456 chooser.add_filter (matchall_filter);
457 chooser.set_select_multiple (true);
458 chooser.signal_update_preview().connect(sigc::mem_fun(*this, &SoundFileBrowser::update_preview));
459 chooser.signal_file_activated().connect (sigc::mem_fun (*this, &SoundFileBrowser::chooser_file_activated));
460 #ifdef GTKOSX
461 /* some broken redraw behaviour - this is a bandaid */
462 chooser.signal_selection_changed().connect (mem_fun (chooser, &Widget::queue_draw));
463 #endif
465 if (!persistent_folder.empty()) {
466 chooser.set_current_folder (persistent_folder);
468 notebook.append_page (chooser, _("Browse Files"));
471 hpacker.set_spacing (6);
472 hpacker.pack_start (notebook, true, true);
473 hpacker.pack_start (preview, false, false);
475 get_vbox()->pack_start (hpacker, true, true);
477 //add tag search
479 VBox* vbox;
480 HBox* hbox;
483 hbox = manage(new HBox);
484 hbox->pack_start (found_entry);
485 hbox->pack_start (found_search_btn);
487 Gtk::ScrolledWindow *scroll = manage(new ScrolledWindow);
488 scroll->add(found_list_view);
489 scroll->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
491 vbox = manage(new VBox);
492 vbox->pack_start (*hbox, PACK_SHRINK);
493 vbox->pack_start (*scroll);
495 found_list_view.append_column(_("Paths"), found_list_columns.pathname);
497 found_list_view.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &SoundFileBrowser::found_list_view_selected));
499 found_list_view.signal_row_activated().connect (sigc::mem_fun (*this, &SoundFileBrowser::found_list_view_activated));
501 found_search_btn.signal_clicked().connect(sigc::mem_fun(*this, &SoundFileBrowser::found_search_clicked));
502 found_entry.signal_activate().connect(sigc::mem_fun(*this, &SoundFileBrowser::found_search_clicked));
504 notebook.append_page (*vbox, _("Search Tags"));
507 //add freesound search
508 #ifdef FREESOUND
510 VBox* vbox;
511 HBox* passbox;
512 Label* label;
514 passbox = manage(new HBox);
515 passbox->set_border_width (12);
516 passbox->set_spacing (6);
518 label = manage (new Label);
519 label->set_text (_("User:"));
520 passbox->pack_start (*label, false, false);
521 passbox->pack_start (freesound_name_entry);
522 label = manage (new Label);
523 label->set_text (_("Password:"));
524 passbox->pack_start (*label, false, false);
525 passbox->pack_start (freesound_pass_entry);
526 label = manage (new Label);
527 label->set_text (_("Tags:"));
528 passbox->pack_start (*label, false, false);
529 passbox->pack_start (freesound_entry, false, false);
530 passbox->pack_start (freesound_search_btn, false, false);
532 Gtk::ScrolledWindow *scroll = manage(new ScrolledWindow);
533 scroll->add(freesound_list_view);
534 scroll->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
536 vbox = manage(new VBox);
537 vbox->pack_start (*passbox, PACK_SHRINK);
538 vbox->pack_start(*scroll);
540 //vbox->pack_start (freesound_list_view);
542 freesound_list_view.append_column(_("Paths"), freesound_list_columns.pathname);
543 freesound_list_view.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &SoundFileBrowser::freesound_list_view_selected));
545 //freesound_list_view.get_selection()->set_mode (SELECTION_MULTIPLE);
546 freesound_list_view.signal_row_activated().connect (sigc::mem_fun (*this, &SoundFileBrowser::freesound_list_view_activated));
547 freesound_search_btn.signal_clicked().connect(sigc::mem_fun(*this, &SoundFileBrowser::freesound_search_clicked));
548 freesound_entry.signal_activate().connect(sigc::mem_fun(*this, &SoundFileBrowser::freesound_search_clicked));
549 notebook.append_page (*vbox, _("Search Freesound"));
551 #endif
554 notebook.set_size_request (500, -1);
556 set_session (s);
558 add_button (Stock::CANCEL, RESPONSE_CANCEL);
559 add_button (Stock::APPLY, RESPONSE_APPLY);
560 add_button (Stock::OK, RESPONSE_OK);
564 SoundFileBrowser::~SoundFileBrowser ()
566 persistent_folder = chooser.get_current_folder();
570 void
571 SoundFileBrowser::on_show ()
573 ArdourDialog::on_show ();
574 start_metering ();
577 void
578 SoundFileBrowser::clear_selection ()
580 chooser.unselect_all ();
581 found_list_view.get_selection()->unselect_all ();
584 void
585 SoundFileBrowser::chooser_file_activated ()
587 preview.audition ();
590 void
591 SoundFileBrowser::found_list_view_activated (const TreeModel::Path&, TreeViewColumn*)
593 preview.audition ();
596 void
597 SoundFileBrowser::freesound_list_view_activated (const TreeModel::Path&, TreeViewColumn*)
599 preview.audition ();
602 void
603 SoundFileBrowser::set_session (Session* s)
605 ArdourDialog::set_session (s);
606 preview.set_session (s);
608 if (_session) {
609 add_gain_meter ();
610 } else {
611 remove_gain_meter ();
615 void
616 SoundFileBrowser::add_gain_meter ()
618 delete gm;
620 gm = new GainMeter (_session, 250);
622 boost::shared_ptr<Route> r = _session->the_auditioner ();
624 gm->set_controls (r, r->shared_peak_meter(), r->amp());
626 meter_packer.set_border_width (12);
627 meter_packer.pack_start (*gm, false, true);
628 hpacker.pack_end (meter_packer, false, false);
629 meter_packer.show_all ();
630 start_metering ();
633 void
634 SoundFileBrowser::remove_gain_meter ()
636 if (gm) {
637 meter_packer.remove (*gm);
638 hpacker.remove (meter_packer);
639 delete gm;
640 gm = 0;
644 void
645 SoundFileBrowser::start_metering ()
647 metering_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (sigc::mem_fun(*this, &SoundFileBrowser::meter));
650 void
651 SoundFileBrowser::stop_metering ()
653 metering_connection.disconnect();
656 void
657 SoundFileBrowser::meter ()
659 if (is_mapped () && _session && gm) {
660 gm->update_meters ();
664 bool
665 SoundFileBrowser::on_audio_filter (const FileFilter::Info& filter_info)
667 return AudioFileSource::safe_audio_file_extension (filter_info.filename);
670 bool
671 SoundFileBrowser::on_midi_filter (const FileFilter::Info& filter_info)
673 return SMFSource::safe_midi_file_extension (filter_info.filename);
676 void
677 SoundFileBrowser::update_preview ()
679 if (preview.setup_labels (chooser.get_filename())) {
680 if (preview.autoplay()) {
681 Glib::signal_idle().connect (sigc::mem_fun (preview, &SoundFileBox::audition_oneshot));
686 void
687 SoundFileBrowser::found_list_view_selected ()
689 if (!reset_options ()) {
690 set_response_sensitive (RESPONSE_OK, false);
691 } else {
692 string file;
694 TreeView::Selection::ListHandle_Path rows = found_list_view.get_selection()->get_selected_rows ();
696 if (!rows.empty()) {
697 TreeIter iter = found_list->get_iter(*rows.begin());
698 file = (*iter)[found_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::freesound_list_view_selected ()
712 if (!reset_options ()) {
713 set_response_sensitive (RESPONSE_OK, false);
714 } else {
715 string file;
717 TreeView::Selection::ListHandle_Path rows = freesound_list_view.get_selection()->get_selected_rows ();
719 if (!rows.empty()) {
720 TreeIter iter = freesound_list->get_iter(*rows.begin());
721 file = (*iter)[freesound_list_columns.pathname];
722 chooser.set_filename (file);
723 set_response_sensitive (RESPONSE_OK, true);
724 } else {
725 set_response_sensitive (RESPONSE_OK, false);
728 preview.setup_labels (file);
732 void
733 SoundFileBrowser::found_search_clicked ()
735 string tag_string = found_entry.get_text ();
737 vector<string> tags;
739 if (!PBD::tokenize (tag_string, string(","), std::back_inserter (tags), true)) {
740 warning << _("SoundFileBrowser: Could not tokenize string: ") << tag_string << endmsg;
741 return;
744 vector<string> results;
745 Library->search_members_and (results, tags);
747 found_list->clear();
748 for (vector<string>::iterator i = results.begin(); i != results.end(); ++i) {
749 TreeModel::iterator new_row = found_list->append();
750 TreeModel::Row row = *new_row;
751 string path = Glib::filename_from_uri (string ("file:") + *i);
752 row[found_list_columns.pathname] = path;
756 void*
757 freesound_search_thread_entry (void* arg)
759 SessionEvent::create_per_thread_pool ("freesound events", 64);
761 static_cast<SoundFileBrowser*>(arg)->freesound_search_thread ();
763 return 0;
766 bool searching = false;
767 bool canceling = false;
769 void
770 SoundFileBrowser::freesound_search_clicked ()
772 if (canceling) //already canceling, button does nothing
773 return;
775 if ( searching ) {
776 freesound_search_btn.set_label(_("Cancelling.."));
777 canceling = true;
778 } else {
779 searching = true;
780 freesound_search_btn.set_label(_("Cancel"));
781 pthread_t freesound_thr;
782 pthread_create_and_store ("freesound_search", &freesound_thr, freesound_search_thread_entry, this);
786 void
787 SoundFileBrowser::freesound_search_thread()
789 #if 0
791 THIS IS ALL TOTALLY THREAD-ILLEGAL ... YOU CANNOT DO GTK STUFF IN THIS THREAD
793 #ifdef FREESOUND
794 freesound_list->clear();
796 string path;
797 path = Glib::get_home_dir();
798 path += "/Freesound/";
799 Mootcher theMootcher(path.c_str());
801 string name_string = freesound_name_entry.get_text ();
802 string pass_string = freesound_pass_entry.get_text ();
803 string search_string = freesound_entry.get_text ();
805 if ( theMootcher.doLogin( name_string, pass_string ) ) {
807 string theString = theMootcher.searchText(search_string);
809 XMLTree doc;
810 doc.read_buffer( theString );
811 XMLNode *root = doc.root();
813 if (root==NULL) return;
815 if ( strcmp(root->name().c_str(), "freesound") == 0) {
817 XMLNode *node = 0;
818 XMLNodeList children = root->children();
819 XMLNodeConstIterator niter;
820 for (niter = children.begin(); niter != children.end() && !canceling; ++niter) {
821 node = *niter;
822 if( strcmp( node->name().c_str(), "sample") == 0 ){
823 XMLProperty *prop=node->property ("id");
824 string filename = theMootcher.getFile( prop->value().c_str() );
825 if ( filename != "" ) {
826 TreeModel::iterator new_row = freesound_list->append();
827 TreeModel::Row row = *new_row;
828 string path = Glib::filename_from_uri (string ("file:") + filename);
829 row[freesound_list_columns.pathname] = path;
836 searching = false;
837 canceling = false;
838 freesound_search_btn.set_label(_("Start Downloading"));
839 #endif
840 #endif
844 vector<string>
845 SoundFileBrowser::get_paths ()
847 vector<string> results;
849 int n = notebook.get_current_page ();
851 if (n == 0) {
852 vector<string> filenames = chooser.get_filenames();
853 vector<string>::iterator i;
855 for (i = filenames.begin(); i != filenames.end(); ++i) {
856 struct stat buf;
857 if ((!stat((*i).c_str(), &buf)) && S_ISREG(buf.st_mode)) {
858 results.push_back (*i);
862 } else if (n==1){
864 typedef TreeView::Selection::ListHandle_Path ListPath;
866 ListPath rows = found_list_view.get_selection()->get_selected_rows ();
867 for (ListPath::iterator i = rows.begin() ; i != rows.end(); ++i) {
868 TreeIter iter = found_list->get_iter(*i);
869 string str = (*iter)[found_list_columns.pathname];
871 results.push_back (str);
873 } else {
875 typedef TreeView::Selection::ListHandle_Path ListPath;
877 ListPath rows = freesound_list_view.get_selection()->get_selected_rows ();
878 for (ListPath::iterator i = rows.begin() ; i != rows.end(); ++i) {
879 TreeIter iter = freesound_list->get_iter(*i);
880 string str = (*iter)[freesound_list_columns.pathname];
882 results.push_back (str);
886 return results;
889 void
890 SoundFileOmega::reset_options_noret ()
892 if (!resetting_ourselves) {
893 (void) reset_options ();
897 bool
898 SoundFileOmega::reset_options ()
900 vector<string> paths = get_paths ();
902 if (paths.empty()) {
904 channel_combo.set_sensitive (false);
905 action_combo.set_sensitive (false);
906 where_combo.set_sensitive (false);
907 copy_files_btn.set_sensitive (false);
909 return false;
911 } else {
913 channel_combo.set_sensitive (true);
914 action_combo.set_sensitive (true);
915 where_combo.set_sensitive (true);
917 /* if we get through this function successfully, this may be
918 reset at the end, once we know if we can use hard links
919 to do embedding
922 if (Config->get_only_copy_imported_files()) {
923 copy_files_btn.set_sensitive (false);
924 } else {
925 copy_files_btn.set_sensitive (false);
929 bool same_size;
930 bool src_needed;
931 bool selection_includes_multichannel;
932 bool selection_can_be_embedded_with_links = check_link_status (_session, paths);
933 ImportMode mode;
935 if (check_info (paths, same_size, src_needed, selection_includes_multichannel)) {
936 Glib::signal_idle().connect (sigc::mem_fun (*this, &SoundFileOmega::bad_file_message));
937 return false;
940 string existing_choice;
941 vector<string> action_strings;
943 if (selected_track_cnt > 0) {
944 if (channel_combo.get_active_text().length()) {
945 ImportDisposition id = get_channel_disposition();
947 switch (id) {
948 case Editing::ImportDistinctFiles:
949 if (selected_track_cnt == paths.size()) {
950 action_strings.push_back (importmode2string (ImportToTrack));
952 break;
954 case Editing::ImportDistinctChannels:
955 /* XXX it would be nice to allow channel-per-selected track
956 but its too hard we don't want to deal with all the
957 different per-file + per-track channel configurations.
959 break;
961 default:
962 action_strings.push_back (importmode2string (ImportToTrack));
963 break;
968 action_strings.push_back (importmode2string (ImportAsTrack));
969 action_strings.push_back (importmode2string (ImportAsRegion));
970 action_strings.push_back (importmode2string (ImportAsTapeTrack));
972 resetting_ourselves = true;
974 existing_choice = action_combo.get_active_text();
976 set_popdown_strings (action_combo, action_strings);
978 /* preserve any existing choice, if possible */
981 if (existing_choice.length()) {
982 vector<string>::iterator x;
983 for (x = action_strings.begin(); x != action_strings.end(); ++x) {
984 if (*x == existing_choice) {
985 action_combo.set_active_text (existing_choice);
986 break;
989 if (x == action_strings.end()) {
990 action_combo.set_active_text (action_strings.front());
992 } else {
993 action_combo.set_active_text (action_strings.front());
996 resetting_ourselves = false;
998 if ((mode = get_mode()) == ImportAsRegion) {
999 where_combo.set_sensitive (false);
1000 } else {
1001 where_combo.set_sensitive (true);
1004 vector<string> channel_strings;
1006 if (mode == ImportAsTrack || mode == ImportAsTapeTrack || mode == ImportToTrack) {
1007 channel_strings.push_back (_("one track per file"));
1009 if (selection_includes_multichannel) {
1010 channel_strings.push_back (_("one track per channel"));
1013 if (paths.size() > 1) {
1014 /* tape tracks are a single region per track, so we cannot
1015 sequence multiple files.
1017 if (mode != ImportAsTapeTrack) {
1018 channel_strings.push_back (_("sequence files"));
1020 if (same_size) {
1021 channel_strings.push_back (_("all files in one track"));
1022 channel_strings.push_back (_("merge files"));
1027 } else {
1028 channel_strings.push_back (_("one region per file"));
1030 if (selection_includes_multichannel) {
1031 channel_strings.push_back (_("one region per channel"));
1034 if (paths.size() > 1) {
1035 if (same_size) {
1036 channel_strings.push_back (_("all files in one region"));
1041 resetting_ourselves = true;
1043 existing_choice = channel_combo.get_active_text();
1045 set_popdown_strings (channel_combo, channel_strings);
1047 /* preserve any existing choice, if possible */
1049 if (existing_choice.length()) {
1050 vector<string>::iterator x;
1051 for (x = channel_strings.begin(); x != channel_strings.end(); ++x) {
1052 if (*x == existing_choice) {
1053 channel_combo.set_active_text (existing_choice);
1054 break;
1057 if (x == channel_strings.end()) {
1058 channel_combo.set_active_text (channel_strings.front());
1060 } else {
1061 channel_combo.set_active_text (channel_strings.front());
1064 resetting_ourselves = false;
1066 if (src_needed) {
1067 src_combo.set_sensitive (true);
1068 } else {
1069 src_combo.set_sensitive (false);
1072 if (Config->get_only_copy_imported_files()) {
1074 if (selection_can_be_embedded_with_links) {
1075 copy_files_btn.set_sensitive (true);
1076 } else {
1077 copy_files_btn.set_sensitive (false);
1080 } else {
1082 copy_files_btn.set_sensitive (true);
1085 return true;
1089 bool
1090 SoundFileOmega::bad_file_message()
1092 MessageDialog msg (*this,
1093 string_compose (_("One or more of the selected files\ncannot be used by %1"), PROGRAM_NAME),
1094 true,
1095 Gtk::MESSAGE_INFO,
1096 Gtk::BUTTONS_OK);
1097 msg.run ();
1098 resetting_ourselves = true;
1099 chooser.unselect_uri (chooser.get_preview_uri());
1100 resetting_ourselves = false;
1102 return false;
1105 bool
1106 SoundFileOmega::check_info (const vector<string>& paths, bool& same_size, bool& src_needed, bool& multichannel)
1108 SoundFileInfo info;
1109 framepos_t sz = 0;
1110 bool err = false;
1111 string errmsg;
1113 same_size = true;
1114 src_needed = false;
1115 multichannel = false;
1117 for (vector<string>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
1119 if (AudioFileSource::get_soundfile_info (*i, info, errmsg)) {
1120 if (info.channels > 1) {
1121 multichannel = true;
1123 if (sz == 0) {
1124 sz = info.length;
1125 } else {
1126 if (sz != info.length) {
1127 same_size = false;
1131 if (info.samplerate != _session->frame_rate()) {
1132 src_needed = true;
1135 } else if (SMFSource::safe_midi_file_extension (*i)) {
1137 Evoral::SMF reader;
1138 reader.open(*i);
1139 if (reader.num_tracks() > 1) {
1140 multichannel = true; // "channel" == track here...
1143 /* XXX we need err = true handling here in case
1144 we can't check the file
1147 } else {
1148 err = true;
1152 return err;
1156 bool
1157 SoundFileOmega::check_link_status (const Session* s, const vector<string>& paths)
1159 sys::path path = s->session_directory().sound_path() / "linktest";
1160 string tmpdir = path.to_string();
1161 bool ret = false;
1163 if (mkdir (tmpdir.c_str(), 0744)) {
1164 if (errno != EEXIST) {
1165 return false;
1169 for (vector<string>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
1171 char tmpc[MAXPATHLEN+1];
1173 snprintf (tmpc, sizeof(tmpc), "%s/%s", tmpdir.c_str(), Glib::path_get_basename (*i).c_str());
1175 /* can we link ? */
1177 if (link ((*i).c_str(), tmpc)) {
1178 goto out;
1181 unlink (tmpc);
1184 ret = true;
1186 out:
1187 rmdir (tmpdir.c_str());
1188 return ret;
1191 SoundFileChooser::SoundFileChooser (Gtk::Window& parent, string title, ARDOUR::Session* s)
1192 : SoundFileBrowser (parent, title, s, false)
1194 chooser.set_select_multiple (false);
1195 found_list_view.get_selection()->set_mode (SELECTION_SINGLE);
1196 freesound_list_view.get_selection()->set_mode (SELECTION_SINGLE);
1199 void
1200 SoundFileChooser::on_hide ()
1202 ArdourDialog::on_hide();
1203 stop_metering ();
1205 if (_session) {
1206 _session->cancel_audition();
1210 string
1211 SoundFileChooser::get_filename ()
1213 vector<string> paths;
1215 paths = get_paths ();
1217 if (paths.empty()) {
1218 return string ();
1221 if (!Glib::file_test (paths.front(), Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
1222 return string();
1225 return paths.front();
1228 SoundFileOmega::SoundFileOmega (Gtk::Window& parent, string title, ARDOUR::Session* s, int selected_tracks, bool persistent,
1229 Editing::ImportMode mode_hint)
1230 : SoundFileBrowser (parent, title, s, persistent),
1231 copy_files_btn ( _("Copy files to session")),
1232 selected_track_cnt (selected_tracks)
1234 VBox* vbox;
1235 HBox* hbox;
1236 vector<string> str;
1238 set_size_request (-1, 450);
1240 block_two.set_border_width (12);
1241 block_three.set_border_width (12);
1242 block_four.set_border_width (12);
1244 options.set_spacing (12);
1246 str.clear ();
1247 str.push_back (_("file timestamp"));
1248 str.push_back (_("edit point"));
1249 str.push_back (_("playhead"));
1250 str.push_back (_("session start"));
1251 set_popdown_strings (where_combo, str);
1252 where_combo.set_active_text (str.front());
1254 Label* l = manage (new Label);
1255 l->set_text (_("Add files:"));
1257 hbox = manage (new HBox);
1258 hbox->set_border_width (12);
1259 hbox->set_spacing (6);
1260 hbox->pack_start (*l, false, false);
1261 hbox->pack_start (action_combo, false, false);
1262 vbox = manage (new VBox);
1263 vbox->pack_start (*hbox, false, false);
1264 options.pack_start (*vbox, false, false);
1266 /* dummy entry for action combo so that it doesn't look odd if we
1267 come up with no tracks selected.
1270 str.clear ();
1271 str.push_back (importmode2string (mode_hint));
1272 set_popdown_strings (action_combo, str);
1273 action_combo.set_active_text (str.front());
1274 action_combo.set_sensitive (false);
1276 l = manage (new Label);
1277 l->set_text (_("Insert at:"));
1279 hbox = manage (new HBox);
1280 hbox->set_border_width (12);
1281 hbox->set_spacing (6);
1282 hbox->pack_start (*l, false, false);
1283 hbox->pack_start (where_combo, false, false);
1284 vbox = manage (new VBox);
1285 vbox->pack_start (*hbox, false, false);
1286 options.pack_start (*vbox, false, false);
1289 l = manage (new Label);
1290 l->set_text (_("Mapping:"));
1292 hbox = manage (new HBox);
1293 hbox->set_border_width (12);
1294 hbox->set_spacing (6);
1295 hbox->pack_start (*l, false, false);
1296 hbox->pack_start (channel_combo, false, false);
1297 vbox = manage (new VBox);
1298 vbox->pack_start (*hbox, false, false);
1299 options.pack_start (*vbox, false, false);
1301 str.clear ();
1302 str.push_back (_("one track per file"));
1303 set_popdown_strings (channel_combo, str);
1304 channel_combo.set_active_text (str.front());
1305 channel_combo.set_sensitive (false);
1307 l = manage (new Label);
1308 l->set_text (_("Conversion quality:"));
1310 hbox = manage (new HBox);
1311 hbox->set_border_width (12);
1312 hbox->set_spacing (6);
1313 hbox->pack_start (*l, false, false);
1314 hbox->pack_start (src_combo, false, false);
1315 vbox = manage (new VBox);
1316 vbox->pack_start (*hbox, false, false);
1317 options.pack_start (*vbox, false, false);
1319 str.clear ();
1320 str.push_back (_("Best"));
1321 str.push_back (_("Good"));
1322 str.push_back (_("Quick"));
1323 str.push_back (_("Fast"));
1324 str.push_back (_("Fastest"));
1326 set_popdown_strings (src_combo, str);
1327 src_combo.set_active_text (str.front());
1328 src_combo.set_sensitive (false);
1330 reset_options ();
1332 action_combo.signal_changed().connect (sigc::mem_fun (*this, &SoundFileOmega::reset_options_noret));
1333 channel_combo.signal_changed().connect (sigc::mem_fun (*this, &SoundFileOmega::reset_options_noret));
1335 copy_files_btn.set_active (true);
1337 block_four.pack_start (copy_files_btn, false, false);
1339 options.pack_start (block_four, false, false);
1341 get_vbox()->pack_start (options, false, false);
1343 /* setup disposition map */
1345 disposition_map.insert (pair<string,ImportDisposition>(_("one track per file"), ImportDistinctFiles));
1346 disposition_map.insert (pair<string,ImportDisposition>(_("one track per channel"), ImportDistinctChannels));
1347 disposition_map.insert (pair<string,ImportDisposition>(_("merge files"), ImportMergeFiles));
1348 disposition_map.insert (pair<string,ImportDisposition>(_("sequence files"), ImportSerializeFiles));
1350 disposition_map.insert (pair<string,ImportDisposition>(_("one region per file"), ImportDistinctFiles));
1351 disposition_map.insert (pair<string,ImportDisposition>(_("one region per channel"), ImportDistinctChannels));
1352 disposition_map.insert (pair<string,ImportDisposition>(_("all files in one region"), ImportMergeFiles));
1353 disposition_map.insert (pair<string,ImportDisposition>(_("all files in one track"), ImportMergeFiles));
1355 chooser.signal_selection_changed().connect (sigc::mem_fun (*this, &SoundFileOmega::file_selection_changed));
1357 /* set size requests for a couple of combos to allow them to display the longest text
1358 they will ever be asked to display. This prevents them being resized when the user
1359 selects a file to import, which in turn prevents the size of the dialog from jumping
1360 around. */
1362 vector<string> t;
1363 t.push_back (_("one track per file"));
1364 t.push_back (_("one track per channel"));
1365 t.push_back (_("sequence files"));
1366 t.push_back (_("all files in one region"));
1367 set_size_request_to_display_given_text (channel_combo, t, COMBO_FUDGE + 10, 15);
1369 t.clear ();
1370 t.push_back (importmode2string (ImportAsTrack));
1371 t.push_back (importmode2string (ImportToTrack));
1372 t.push_back (importmode2string (ImportAsRegion));
1373 t.push_back (importmode2string (ImportAsTapeTrack));
1374 set_size_request_to_display_given_text (action_combo, t, COMBO_FUDGE + 10, 15);
1377 void
1378 SoundFileOmega::set_mode (ImportMode mode)
1380 action_combo.set_active_text (importmode2string (mode));
1383 ImportMode
1384 SoundFileOmega::get_mode () const
1386 return string2importmode (action_combo.get_active_text());
1389 void
1390 SoundFileOmega::on_hide ()
1392 ArdourDialog::on_hide();
1393 if (_session) {
1394 _session->cancel_audition();
1398 ImportPosition
1399 SoundFileOmega::get_position() const
1401 string str = where_combo.get_active_text();
1403 if (str == _("file timestamp")) {
1404 return ImportAtTimestamp;
1405 } else if (str == _("edit point")) {
1406 return ImportAtEditPoint;
1407 } else if (str == _("playhead")) {
1408 return ImportAtPlayhead;
1409 } else {
1410 return ImportAtStart;
1414 SrcQuality
1415 SoundFileOmega::get_src_quality() const
1417 string str = where_combo.get_active_text();
1419 if (str == _("Best")) {
1420 return SrcBest;
1421 } else if (str == _("Good")) {
1422 return SrcGood;
1423 } else if (str == _("Quick")) {
1424 return SrcQuick;
1425 } else if (str == _("Fast")) {
1426 return SrcFast;
1427 } else {
1428 return SrcFastest;
1432 ImportDisposition
1433 SoundFileOmega::get_channel_disposition () const
1435 /* we use a map here because the channel combo can contain different strings
1436 depending on the state of the other combos. the map contains all possible strings
1437 and the ImportDisposition enum that corresponds to it.
1440 string str = channel_combo.get_active_text();
1441 DispositionMap::const_iterator x = disposition_map.find (str);
1443 if (x == disposition_map.end()) {
1444 fatal << string_compose (_("programming error: %1 (%2)"), "unknown string for import disposition", str) << endmsg;
1445 /*NOTREACHED*/
1448 return x->second;
1451 void
1452 SoundFileOmega::reset (int selected_tracks)
1454 selected_track_cnt = selected_tracks;
1455 reset_options ();
1458 void
1459 SoundFileOmega::file_selection_changed ()
1461 if (resetting_ourselves) {
1462 return;
1465 if (!reset_options ()) {
1466 set_response_sensitive (RESPONSE_OK, false);
1467 } else {
1468 if (chooser.get_filenames().size() > 0) {
1469 set_response_sensitive (RESPONSE_OK, true);
1470 } else {
1471 set_response_sensitive (RESPONSE_OK, false);