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.
21 #include "gtk2ardour-config.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/audio_library.h"
47 #include "ardour/auditioner.h"
48 #include "ardour/audioregion.h"
49 #include "ardour/audiofilesource.h"
50 #include "ardour/smf_source.h"
51 #include "ardour/region_factory.h"
52 #include "ardour/source_factory.h"
53 #include "ardour/session.h"
54 #include "ardour/session_directory.h"
56 #include "ardour_ui.h"
58 #include "gui_thread.h"
63 #include "gain_meter.h"
64 #include "main_clock.h"
67 #include "sfdb_freesound_mootcher.h"
72 using namespace ARDOUR;
76 using namespace Gtkmm2ext;
77 using namespace Editing;
81 string SoundFileBrowser::persistent_folder;
84 string2importmode (string str)
86 if (str == _("as new tracks")) {
88 } else if (str == _("to selected tracks")) {
90 } else if (str == _("to region list")) {
91 return ImportAsRegion;
92 } else if (str == _("as new tape tracks")) {
93 return ImportAsTapeTrack;
96 warning << string_compose (_("programming error: unknown import mode string %1"), str) << endmsg;
102 importmode2string (ImportMode mode)
106 return _("as new tracks");
108 return _("to selected tracks");
110 return _("to region list");
111 case ImportAsTapeTrack:
112 return _("as new tape tracks");
115 return _("as new tracks");
118 SoundFileBox::SoundFileBox (bool persistent)
120 length_clock ("sfboxLengthClock", !persistent, "", false, false, true, false),
121 timecode_clock ("sfboxTimecodeClock", !persistent, "", false, false, false, false),
123 autoplay_btn (_("Auto-play"))
126 set_name (X_("SoundFileBox"));
127 set_size_request (300, -1);
129 preview_label.set_markup (_("<b>Sound File Information</b>"));
131 border_frame.set_label_widget (preview_label);
132 border_frame.add (main_box);
134 pack_start (border_frame, true, true);
135 set_border_width (6);
137 main_box.set_border_width (6);
139 length.set_text (_("Length:"));
140 length.set_alignment (1, 0.5);
141 timecode.set_text (_("Timestamp:"));
142 timecode.set_alignment (1, 0.5);
143 format.set_text (_("Format:"));
144 format.set_alignment (1, 0.5);
145 channels.set_text (_("Channels:"));
146 channels.set_alignment (1, 0.5);
147 samplerate.set_text (_("Sample rate:"));
148 samplerate.set_alignment (1, 0.5);
150 preview_label.set_max_width_chars (50);
151 preview_label.set_ellipsize (Pango::ELLIPSIZE_END);
153 format_text.set_max_width_chars (20);
154 format_text.set_ellipsize (Pango::ELLIPSIZE_END);
155 format_text.set_alignment (0, 1);
157 table.set_col_spacings (6);
158 table.set_homogeneous (false);
159 table.set_row_spacings (6);
161 table.attach (channels, 0, 1, 0, 1, FILL, FILL);
162 table.attach (samplerate, 0, 1, 1, 2, FILL, FILL);
163 table.attach (format, 0, 1, 2, 4, FILL, FILL);
164 table.attach (length, 0, 1, 4, 5, FILL, FILL);
165 table.attach (timecode, 0, 1, 5, 6, FILL, FILL);
167 table.attach (channels_value, 1, 2, 0, 1, FILL, FILL);
168 table.attach (samplerate_value, 1, 2, 1, 2, FILL, FILL);
169 table.attach (format_text, 1, 2, 2, 4, FILL, FILL);
170 table.attach (length_clock, 1, 2, 4, 5, FILL, FILL);
171 table.attach (timecode_clock, 1, 2, 5, 6, FILL, FILL);
173 length_clock.set_mode (ARDOUR_UI::instance()->secondary_clock->mode());
174 timecode_clock.set_mode (AudioClock::Timecode);
176 main_box.pack_start (table, false, false);
178 tags_entry.set_editable (true);
179 tags_entry.set_wrap_mode(Gtk::WRAP_WORD);
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);
209 SoundFileBox::set_session(Session* s)
211 SessionHandlePtr::set_session (s);
213 length_clock.set_session (s);
214 timecode_clock.set_session (s);
217 play_btn.set_sensitive (false);
218 stop_btn.set_sensitive (false);
223 SoundFileBox::setup_labels (const string& filename)
226 // save existing tags
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);
251 preview_label.set_markup (string_compose ("<b>%1</b>", Glib::Markup::escape_text (Glib::path_get_basename (filename))));
252 std::string n = sf_info.format_name;
253 if (n.substr (0, 8) == X_("Format: ")) {
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");
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()) {
288 tags_entry.get_buffer()->set_text (tag_string.str());
290 tags_entry.set_sensitive (true);
292 play_btn.set_sensitive (true);
299 SoundFileBox::autoplay() const
301 return autoplay_btn.get_active();
305 SoundFileBox::audition_oneshot()
312 SoundFileBox::audition ()
318 if (SMFSource::safe_midi_file_extension (path)) {
319 error << _("Auditioning of MIDI files is not yet supported") << endmsg;
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;
330 boost::shared_ptr<Region> r;
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) {
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);
354 AudioSource::set_build_peakfiles (old_sbp);
356 if (srclist.empty()) {
360 afs = boost::dynamic_pointer_cast<AudioFileSource> (srclist[0]);
361 string rname = region_name_from_path (afs->path(), false);
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);
376 SoundFileBox::stop_audition ()
379 _session->cancel_audition();
384 SoundFileBox::tags_entry_left (GdkEventFocus *)
391 SoundFileBox::tags_changed ()
393 string tag_string = tags_entry.get_buffer()->get_text ();
395 if (tag_string.empty()) {
401 if (!PBD::tokenize (tag_string, string(",\n"), std::back_inserter (tags), true)) {
402 warning << _("SoundFileBox: Could not tokenize string: ") << tag_string << endmsg;
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 (_("Search")),
425 freesound_list_view (freesound_list)
427 resetting_ourselves = false;
430 resetting_ourselves = false;
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");
442 mootcher = new Mootcher();
445 //add the file chooser
447 chooser.set_border_width (12);
449 audio_and_midi_filter.add_custom (FILE_FILTER_FILENAME, sigc::mem_fun (*this, &SoundFileBrowser::on_audio_and_midi_filter));
450 audio_and_midi_filter.set_name (_("Audio and MIDI files"));
452 audio_filter.add_custom (FILE_FILTER_FILENAME, sigc::mem_fun(*this, &SoundFileBrowser::on_audio_filter));
453 audio_filter.set_name (_("Audio files"));
455 midi_filter.add_custom (FILE_FILTER_FILENAME, sigc::mem_fun(*this, &SoundFileBrowser::on_midi_filter));
456 midi_filter.set_name (_("MIDI files"));
458 matchall_filter.add_pattern ("*.*");
459 matchall_filter.set_name (_("All files"));
461 chooser.add_filter (audio_and_midi_filter);
462 chooser.add_filter (audio_filter);
463 chooser.add_filter (midi_filter);
464 chooser.add_filter (matchall_filter);
465 chooser.set_select_multiple (true);
466 chooser.signal_update_preview().connect(sigc::mem_fun(*this, &SoundFileBrowser::update_preview));
467 chooser.signal_file_activated().connect (sigc::mem_fun (*this, &SoundFileBrowser::chooser_file_activated));
469 /* some broken redraw behaviour - this is a bandaid */
470 chooser.signal_selection_changed().connect (mem_fun (chooser, &Widget::queue_draw));
473 if (!persistent_folder.empty()) {
474 chooser.set_current_folder (persistent_folder);
476 notebook.append_page (chooser, _("Browse Files"));
479 hpacker.set_spacing (6);
480 hpacker.pack_start (notebook, true, true);
481 hpacker.pack_start (preview, false, false);
483 get_vbox()->pack_start (hpacker, true, true);
491 hbox = manage(new HBox);
492 hbox->pack_start (found_entry);
493 hbox->pack_start (found_search_btn);
495 Gtk::ScrolledWindow *scroll = manage(new ScrolledWindow);
496 scroll->add(found_list_view);
497 scroll->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
499 vbox = manage(new VBox);
500 vbox->pack_start (*hbox, PACK_SHRINK);
501 vbox->pack_start (*scroll);
503 found_list_view.append_column(_("Paths"), found_list_columns.pathname);
505 found_list_view.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &SoundFileBrowser::found_list_view_selected));
507 found_list_view.signal_row_activated().connect (sigc::mem_fun (*this, &SoundFileBrowser::found_list_view_activated));
509 found_search_btn.signal_clicked().connect(sigc::mem_fun(*this, &SoundFileBrowser::found_search_clicked));
510 found_entry.signal_activate().connect(sigc::mem_fun(*this, &SoundFileBrowser::found_search_clicked));
512 freesound_stop_btn.signal_clicked().connect(sigc::mem_fun(*this, &SoundFileBrowser::freesound_stop_clicked));
514 notebook.append_page (*vbox, _("Search Tags"));
517 //add freesound search
524 passbox = manage(new HBox);
525 passbox->set_spacing (6);
527 label = manage (new Label);
528 label->set_text (_("Tags:"));
529 passbox->pack_start (*label, false, false);
530 passbox->pack_start (freesound_entry, true, true);
532 label = manage (new Label);
533 label->set_text (_("Sort:"));
534 passbox->pack_start (*label, false, false);
535 passbox->pack_start (freesound_sort, false, false);
536 freesound_sort.clear_items();
538 // Order of the following must correspond with enum sortMethod
539 // in sfdb_freesound_mootcher.h
540 freesound_sort.append_text(_("None"));
541 freesound_sort.append_text(_("Longest"));
542 freesound_sort.append_text(_("Shortest"));
543 freesound_sort.append_text(_("Newest"));
544 freesound_sort.append_text(_("Oldest"));
545 freesound_sort.append_text(_("Most downloaded"));
546 freesound_sort.append_text(_("Least downloaded"));
547 freesound_sort.append_text(_("Highest rated"));
548 freesound_sort.append_text(_("Lowest rated"));
549 freesound_sort.set_active(0);
551 passbox->pack_start (freesound_search_btn, false, false);
552 passbox->pack_end (freesound_stop_btn, false, false);
553 freesound_stop_btn.set_label(_("Stop"));
555 Gtk::ScrolledWindow *scroll = manage(new ScrolledWindow);
556 scroll->add(freesound_list_view);
557 scroll->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
559 vbox = manage(new VBox);
560 vbox->set_spacing (3);
561 vbox->pack_start (*passbox, PACK_SHRINK);
562 vbox->pack_start (freesound_progress_bar, PACK_SHRINK);
563 vbox->pack_start (*scroll);
565 freesound_list_view.append_column(_("ID") , freesound_list_columns.id);
566 freesound_list_view.append_column(_("Filename"), freesound_list_columns.filename);
567 // freesound_list_view.append_column(_("URI") , freesound_list_columns.uri);
568 freesound_list_view.append_column(_("Duration"), freesound_list_columns.duration);
569 freesound_list_view.append_column(_("Size"), freesound_list_columns.filesize);
570 freesound_list_view.append_column(_("Samplerate"), freesound_list_columns.smplrate);
571 freesound_list_view.append_column(_("License"), freesound_list_columns.license);
572 freesound_list_view.get_column(0)->set_alignment(0.5);
573 freesound_list_view.get_column(1)->set_expand(true);
574 freesound_list_view.get_column(2)->set_alignment(0.5);
575 freesound_list_view.get_column(3)->set_alignment(0.5);
576 freesound_list_view.get_column(4)->set_alignment(0.5);
577 freesound_list_view.get_column(5)->set_alignment(0.5);
579 freesound_list_view.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &SoundFileBrowser::freesound_list_view_selected));
581 freesound_list_view.get_selection()->set_mode (SELECTION_MULTIPLE);
582 freesound_list_view.signal_row_activated().connect (sigc::mem_fun (*this, &SoundFileBrowser::freesound_list_view_activated));
583 freesound_search_btn.signal_clicked().connect(sigc::mem_fun(*this, &SoundFileBrowser::freesound_search_clicked));
584 freesound_entry.signal_activate().connect(sigc::mem_fun(*this, &SoundFileBrowser::freesound_search_clicked));
585 freesound_stop_btn.signal_clicked().connect(sigc::mem_fun(*this, &SoundFileBrowser::freesound_stop_clicked));
586 notebook.append_page (*vbox, _("Search Freesound"));
592 notebook.set_size_request (500, -1);
593 notebook.signal_switch_page().connect (
594 sigc::hide_return (sigc::hide (sigc::hide (sigc::mem_fun (*this, &SoundFileBrowser::reset_options))))
599 add_button (Stock::CANCEL, RESPONSE_CANCEL);
600 add_button (Stock::APPLY, RESPONSE_APPLY);
601 add_button (Stock::OK, RESPONSE_OK);
605 SoundFileBrowser::~SoundFileBrowser ()
607 persistent_folder = chooser.get_current_folder();
612 SoundFileBrowser::on_show ()
614 ArdourDialog::on_show ();
619 SoundFileBrowser::clear_selection ()
621 chooser.unselect_all ();
622 found_list_view.get_selection()->unselect_all ();
626 SoundFileBrowser::chooser_file_activated ()
632 SoundFileBrowser::found_list_view_activated (const TreeModel::Path&, TreeViewColumn*)
638 SoundFileBrowser::freesound_list_view_activated (const TreeModel::Path&, TreeViewColumn*)
644 SoundFileBrowser::set_session (Session* s)
646 ArdourDialog::set_session (s);
647 preview.set_session (s);
652 remove_gain_meter ();
657 SoundFileBrowser::add_gain_meter ()
661 gm = new GainMeter (_session, 250);
663 boost::shared_ptr<Route> r = _session->the_auditioner ();
665 gm->set_controls (r, r->shared_peak_meter(), r->amp());
667 meter_packer.set_border_width (12);
668 meter_packer.pack_start (*gm, false, true);
669 hpacker.pack_end (meter_packer, false, false);
670 meter_packer.show_all ();
675 SoundFileBrowser::remove_gain_meter ()
678 meter_packer.remove (*gm);
679 hpacker.remove (meter_packer);
686 SoundFileBrowser::start_metering ()
688 metering_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (sigc::mem_fun(*this, &SoundFileBrowser::meter));
692 SoundFileBrowser::stop_metering ()
694 metering_connection.disconnect();
698 SoundFileBrowser::meter ()
700 if (is_mapped () && _session && gm) {
701 gm->update_meters ();
706 SoundFileBrowser::on_audio_filter (const FileFilter::Info& filter_info)
708 return AudioFileSource::safe_audio_file_extension (filter_info.filename);
712 SoundFileBrowser::on_midi_filter (const FileFilter::Info& filter_info)
714 return SMFSource::safe_midi_file_extension (filter_info.filename);
718 SoundFileBrowser::on_audio_and_midi_filter (const FileFilter::Info& filter_info)
720 return on_audio_filter (filter_info) || on_midi_filter (filter_info);
724 SoundFileBrowser::update_preview ()
726 if (preview.setup_labels (chooser.get_preview_filename())) {
727 if (preview.autoplay()) {
728 Glib::signal_idle().connect (sigc::mem_fun (preview, &SoundFileBox::audition_oneshot));
734 SoundFileBrowser::found_list_view_selected ()
736 if (!reset_options ()) {
737 set_response_sensitive (RESPONSE_OK, false);
741 TreeView::Selection::ListHandle_Path rows = found_list_view.get_selection()->get_selected_rows ();
744 TreeIter iter = found_list->get_iter(*rows.begin());
745 file = (*iter)[found_list_columns.pathname];
746 chooser.set_filename (file);
747 set_response_sensitive (RESPONSE_OK, true);
749 set_response_sensitive (RESPONSE_OK, false);
752 preview.setup_labels (file);
757 SoundFileBrowser::freesound_list_view_selected ()
759 freesound_download_cancel = false;
762 if (!reset_options ()) {
763 set_response_sensitive (RESPONSE_OK, false);
768 TreeView::Selection::ListHandle_Path rows = freesound_list_view.get_selection()->get_selected_rows ();
771 TreeIter iter = freesound_list->get_iter(*rows.begin());
773 string id = (*iter)[freesound_list_columns.id];
774 string uri = (*iter)[freesound_list_columns.uri];
775 string ofn = (*iter)[freesound_list_columns.filename];
777 // download the sound file
778 GdkCursor *prev_cursor;
779 prev_cursor = gdk_window_get_cursor (get_window()->gobj());
780 gdk_window_set_cursor (get_window()->gobj(), gdk_cursor_new(GDK_WATCH));
783 file = mootcher->getAudioFile(ofn, id, uri, this);
785 gdk_window_set_cursor (get_window()->gobj(), prev_cursor);
788 chooser.set_filename (file);
789 set_response_sensitive (RESPONSE_OK, true);
792 set_response_sensitive (RESPONSE_OK, false);
795 freesound_progress_bar.set_text(
796 string_compose(_("found %1 matche(s)"), matches));
798 preview.setup_labels (file);
804 SoundFileBrowser::found_search_clicked ()
806 string tag_string = found_entry.get_text ();
810 if (!PBD::tokenize (tag_string, string(","), std::back_inserter (tags), true)) {
811 warning << _("SoundFileBrowser: Could not tokenize string: ") << tag_string << endmsg;
815 vector<string> results;
816 Library->search_members_and (results, tags);
819 for (vector<string>::iterator i = results.begin(); i != results.end(); ++i) {
820 TreeModel::iterator new_row = found_list->append();
821 TreeModel::Row row = *new_row;
822 string path = Glib::filename_from_uri (string ("file:") + *i);
823 row[found_list_columns.pathname] = path;
828 SoundFileBrowser::freesound_search_clicked ()
830 freesound_search_cancel = false;
835 SoundFileBrowser::freesound_stop_clicked ()
837 freesound_download_cancel = true;
838 freesound_search_cancel = true;
843 SoundFileBrowser::freesound_search()
846 freesound_list->clear();
847 freesound_list_view.get_column(1)->set_sizing(TREE_VIEW_COLUMN_GROW_ONLY);
850 string search_string = freesound_entry.get_text ();
851 enum sortMethod sort_method = (enum sortMethod) freesound_sort.get_active_row_number();
853 GdkCursor *prev_cursor;
854 prev_cursor = gdk_window_get_cursor (get_window()->gobj());
855 gdk_window_set_cursor (get_window()->gobj(), gdk_cursor_new(GDK_WATCH));
856 freesound_progress_bar.set_fraction(0.0);
859 int freesound_n_pages = 1;
860 for (int page = 1; page <= 99 && page <= freesound_n_pages; page++ ) {
863 if (freesound_n_pages > 1) {
864 freesound_progress_bar.set_fraction(page/(float)freesound_n_pages);
865 prog = string_compose (_("Searching Page %1 of %2, click Stop to cancel"), page, freesound_n_pages);
867 prog = _("Searching, click Stop to cancel");
869 freesound_progress_bar.set_text(prog);
870 while (Glib::MainContext::get_default()->iteration (false)) {
874 std::string theString = mootcher->searchText(
878 "", // OSX eats anything incl mp3
880 "type:wav OR type:aiff OR type:flac OR type:aif OR type:ogg OR type:oga",
886 doc.read_buffer( theString );
887 XMLNode *root = doc.root();
890 error << "no root XML node!" << endmsg;
894 if ( strcmp(root->name().c_str(), "response") != 0) {
895 error << string_compose ("root node name == %1 != \"response\"", root->name()) << endmsg;
899 //find out how many pages are available to search
900 XMLNode *res = root->child("num_pages");
902 string result = res->child("text")->content();
903 freesound_n_pages = atoi(result.c_str());
906 XMLNode *sounds_root = root->child("sounds");
909 error << "no child node \"sounds\" found!" << endmsg;
913 XMLNodeList sounds = sounds_root->children();
914 if (sounds.size() == 0) {
919 XMLNodeConstIterator niter;
921 for (niter = sounds.begin(); niter != sounds.end(); ++niter) {
923 if( strcmp( node->name().c_str(), "resource") != 0 ){
924 error << string_compose ("node->name()=%1 != \"resource\"", node->name()) << endmsg;
925 freesound_search_cancel = true;
929 // node->dump(cerr, "node:");
931 XMLNode *id_node = node->child ("id");
932 XMLNode *uri_node = node->child ("serve");
933 XMLNode *ofn_node = node->child ("original_filename");
934 XMLNode *dur_node = node->child ("duration");
935 XMLNode *siz_node = node->child ("filesize");
936 XMLNode *srt_node = node->child ("samplerate");
937 XMLNode *lic_node = node->child ("license");
939 if (id_node && uri_node && ofn_node && dur_node && siz_node && srt_node) {
941 std::string id = id_node->child("text")->content();
942 std::string uri = uri_node->child("text")->content();
943 std::string ofn = ofn_node->child("text")->content();
944 std::string dur = dur_node->child("text")->content();
945 std::string siz = siz_node->child("text")->content();
946 std::string srt = srt_node->child("text")->content();
947 std::string lic = lic_node->child("text")->content();
950 // cerr << "id=" << id << ",uri=" << uri << ",ofn=" << ofn << ",dur=" << dur << endl;
952 double duration_seconds = atof(dur.c_str());
954 char duration_hhmmss[16];
955 if (duration_seconds >= 99 * 60 * 60) {
956 strcpy(duration_hhmmss, ">99h");
958 s = modf(duration_seconds/60, &m) * 60;
959 m = modf(m/60, &h) * 60;
960 sprintf(duration_hhmmss, "%02.fh:%02.fm:%04.1fs",
965 double size_bytes = atof(siz.c_str());
967 if (size_bytes < 1000) {
968 sprintf(bsize, "%.0f %s", size_bytes, _("B"));
969 } else if (size_bytes < 1000000 ) {
970 sprintf(bsize, "%.1f %s", size_bytes / 1000.0, _("kB"));
971 } else if (size_bytes < 10000000) {
972 sprintf(bsize, "%.1f %s", size_bytes / 1000000.0, _("MB"));
973 } else if (size_bytes < 1000000000) {
974 sprintf(bsize, "%.2f %s", size_bytes / 1000000.0, _("MB"));
976 sprintf(bsize, "%.2f %s", size_bytes / 1000000000.0, _("GB"));
979 /* see http://www.freesound.org/help/faq/#licenses */
980 char shortlicense[64];
981 if(!lic.compare(0, 42, "http://creativecommons.org/licenses/by-nc/")){
982 sprintf(shortlicense, "CC-BY-NC");
983 } else if(!lic.compare(0, 39, "http://creativecommons.org/licenses/by/")) {
984 sprintf(shortlicense, "CC-BY");
985 } else if(!lic.compare("http://creativecommons.org/licenses/sampling+/1.0/")) {
986 sprintf(shortlicense, "sampling+");
987 } else if(!lic.compare(0, 40, "http://creativecommons.org/publicdomain/")) {
988 sprintf(shortlicense, "PD");
990 snprintf(shortlicense, 64, "%s", lic.c_str());
991 shortlicense[63]= '\0';
994 TreeModel::iterator new_row = freesound_list->append();
995 TreeModel::Row row = *new_row;
997 row[freesound_list_columns.id ] = id;
998 row[freesound_list_columns.uri ] = uri;
999 row[freesound_list_columns.filename] = ofn;
1000 row[freesound_list_columns.duration] = duration_hhmmss;
1001 row[freesound_list_columns.filesize] = bsize;
1002 row[freesound_list_columns.smplrate] = srt;
1003 row[freesound_list_columns.license ] = shortlicense;
1009 if (freesound_search_cancel)
1014 gdk_window_set_cursor (get_window()->gobj(), prev_cursor);
1016 freesound_progress_bar.set_fraction(0.0);
1019 freesound_progress_bar.set_text(_("Search returned no results."));
1022 freesound_progress_bar.set_text(_("Found one match."));
1025 freesound_progress_bar.set_text(string_compose(_("Found %1 matche(s)"), matches));
1027 freesound_list_view.get_column(1)->set_sizing(TREE_VIEW_COLUMN_AUTOSIZE);
1032 SoundFileBrowser::get_paths ()
1034 vector<string> results;
1036 int n = notebook.get_current_page ();
1039 vector<string> filenames = chooser.get_filenames();
1040 vector<string>::iterator i;
1042 for (i = filenames.begin(); i != filenames.end(); ++i) {
1044 if ((!stat((*i).c_str(), &buf)) && S_ISREG(buf.st_mode)) {
1045 results.push_back (*i);
1051 typedef TreeView::Selection::ListHandle_Path ListPath;
1053 ListPath rows = found_list_view.get_selection()->get_selected_rows ();
1054 for (ListPath::iterator i = rows.begin() ; i != rows.end(); ++i) {
1055 TreeIter iter = found_list->get_iter(*i);
1056 string str = (*iter)[found_list_columns.pathname];
1058 results.push_back (str);
1062 typedef TreeView::Selection::ListHandle_Path ListPath;
1064 ListPath rows = freesound_list_view.get_selection()->get_selected_rows ();
1065 for (ListPath::iterator i = rows.begin() ; i != rows.end(); ++i) {
1066 TreeIter iter = freesound_list->get_iter(*i);
1067 string id = (*iter)[freesound_list_columns.id];
1068 string uri = (*iter)[freesound_list_columns.uri];
1069 string ofn = (*iter)[freesound_list_columns.filename];
1071 GdkCursor *prev_cursor;
1072 prev_cursor = gdk_window_get_cursor (get_window()->gobj());
1073 gdk_window_set_cursor (get_window()->gobj(), gdk_cursor_new(GDK_WATCH));
1076 string str = mootcher->getAudioFile(ofn, id, uri, this);
1078 results.push_back (str);
1081 gdk_window_set_cursor (get_window()->gobj(), prev_cursor);
1091 SoundFileOmega::reset_options_noret ()
1093 if (!resetting_ourselves) {
1094 (void) reset_options ();
1099 SoundFileOmega::reset_options ()
1101 vector<string> paths = get_paths ();
1103 if (paths.empty()) {
1105 channel_combo.set_sensitive (false);
1106 action_combo.set_sensitive (false);
1107 where_combo.set_sensitive (false);
1108 copy_files_btn.set_active (true);
1109 copy_files_btn.set_sensitive (false);
1115 channel_combo.set_sensitive (true);
1116 action_combo.set_sensitive (true);
1117 where_combo.set_sensitive (true);
1119 /* if we get through this function successfully, this may be
1120 reset at the end, once we know if we can use hard links
1121 to do embedding (or if we are importing a MIDI file).
1124 if (Config->get_only_copy_imported_files()) {
1125 copy_files_btn.set_sensitive (false);
1127 copy_files_btn.set_sensitive (false);
1133 bool selection_includes_multichannel;
1134 bool selection_can_be_embedded_with_links = check_link_status (_session, paths);
1137 /* See if we are thinking about importing any MIDI files */
1138 vector<string>::iterator i = paths.begin ();
1139 while (i != paths.end() && SMFSource::safe_midi_file_extension (*i) == false) {
1142 bool const have_a_midi_file = (i != paths.end ());
1144 if (check_info (paths, same_size, src_needed, selection_includes_multichannel)) {
1145 Glib::signal_idle().connect (sigc::mem_fun (*this, &SoundFileOmega::bad_file_message));
1149 string existing_choice;
1150 vector<string> action_strings;
1152 resetting_ourselves = true;
1154 if (chooser.get_filter() == &audio_filter) {
1158 if (selected_audio_track_cnt > 0) {
1159 if (channel_combo.get_active_text().length()) {
1160 ImportDisposition id = get_channel_disposition();
1163 case Editing::ImportDistinctFiles:
1164 if (selected_audio_track_cnt == paths.size()) {
1165 action_strings.push_back (importmode2string (ImportToTrack));
1169 case Editing::ImportDistinctChannels:
1170 /* XXX it would be nice to allow channel-per-selected track
1171 but its too hard we don't want to deal with all the
1172 different per-file + per-track channel configurations.
1177 action_strings.push_back (importmode2string (ImportToTrack));
1187 if (selected_midi_track_cnt > 0) {
1188 action_strings.push_back (importmode2string (ImportToTrack));
1192 action_strings.push_back (importmode2string (ImportAsTrack));
1193 action_strings.push_back (importmode2string (ImportAsRegion));
1194 action_strings.push_back (importmode2string (ImportAsTapeTrack));
1196 existing_choice = action_combo.get_active_text();
1198 set_popdown_strings (action_combo, action_strings);
1200 /* preserve any existing choice, if possible */
1203 if (existing_choice.length()) {
1204 vector<string>::iterator x;
1205 for (x = action_strings.begin(); x != action_strings.end(); ++x) {
1206 if (*x == existing_choice) {
1207 action_combo.set_active_text (existing_choice);
1211 if (x == action_strings.end()) {
1212 action_combo.set_active_text (action_strings.front());
1215 action_combo.set_active_text (action_strings.front());
1218 resetting_ourselves = false;
1220 if ((mode = get_mode()) == ImportAsRegion) {
1221 where_combo.set_sensitive (false);
1223 where_combo.set_sensitive (true);
1226 vector<string> channel_strings;
1228 if (mode == ImportAsTrack || mode == ImportAsTapeTrack || mode == ImportToTrack) {
1229 channel_strings.push_back (_("one track per file"));
1231 if (selection_includes_multichannel) {
1232 channel_strings.push_back (_("one track per channel"));
1235 if (paths.size() > 1) {
1236 /* tape tracks are a single region per track, so we cannot
1237 sequence multiple files.
1239 if (mode != ImportAsTapeTrack) {
1240 channel_strings.push_back (_("sequence files"));
1243 channel_strings.push_back (_("all files in one track"));
1244 channel_strings.push_back (_("merge files"));
1250 channel_strings.push_back (_("one region per file"));
1252 if (selection_includes_multichannel) {
1253 channel_strings.push_back (_("one region per channel"));
1256 if (paths.size() > 1) {
1258 channel_strings.push_back (_("all files in one region"));
1263 resetting_ourselves = true;
1265 existing_choice = channel_combo.get_active_text();
1267 set_popdown_strings (channel_combo, channel_strings);
1269 /* preserve any existing choice, if possible */
1271 if (existing_choice.length()) {
1272 vector<string>::iterator x;
1273 for (x = channel_strings.begin(); x != channel_strings.end(); ++x) {
1274 if (*x == existing_choice) {
1275 channel_combo.set_active_text (existing_choice);
1279 if (x == channel_strings.end()) {
1280 channel_combo.set_active_text (channel_strings.front());
1283 channel_combo.set_active_text (channel_strings.front());
1286 resetting_ourselves = false;
1289 src_combo.set_sensitive (true);
1291 src_combo.set_sensitive (false);
1294 /* We must copy MIDI files or those from Freesound */
1295 bool const must_copy = have_a_midi_file || notebook.get_current_page() == 2;
1297 if (Config->get_only_copy_imported_files()) {
1299 if (selection_can_be_embedded_with_links && !must_copy) {
1300 copy_files_btn.set_sensitive (true);
1303 copy_files_btn.set_active (true);
1305 copy_files_btn.set_sensitive (false);
1311 copy_files_btn.set_active (true);
1313 copy_files_btn.set_sensitive (!must_copy);
1321 SoundFileOmega::bad_file_message()
1323 MessageDialog msg (*this,
1324 string_compose (_("One or more of the selected files\ncannot be used by %1"), PROGRAM_NAME),
1329 resetting_ourselves = true;
1330 chooser.unselect_uri (chooser.get_preview_uri());
1331 resetting_ourselves = false;
1337 SoundFileOmega::check_info (const vector<string>& paths, bool& same_size, bool& src_needed, bool& multichannel)
1346 multichannel = false;
1348 for (vector<string>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
1350 if (AudioFileSource::get_soundfile_info (*i, info, errmsg)) {
1351 if (info.channels > 1) {
1352 multichannel = true;
1357 if (sz != info.length) {
1362 if (info.samplerate != _session->frame_rate()) {
1366 } else if (SMFSource::safe_midi_file_extension (*i)) {
1370 if (reader.num_tracks() > 1) {
1371 multichannel = true; // "channel" == track here...
1374 /* XXX we need err = true handling here in case
1375 we can't check the file
1388 SoundFileOmega::check_link_status (const Session* s, const vector<string>& paths)
1390 std::string tmpdir(Glib::build_filename (s->session_directory().sound_path(), "linktest"));
1393 if (mkdir (tmpdir.c_str(), 0744)) {
1394 if (errno != EEXIST) {
1399 for (vector<string>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
1401 char tmpc[MAXPATHLEN+1];
1403 snprintf (tmpc, sizeof(tmpc), "%s/%s", tmpdir.c_str(), Glib::path_get_basename (*i).c_str());
1407 if (link ((*i).c_str(), tmpc)) {
1417 rmdir (tmpdir.c_str());
1421 SoundFileChooser::SoundFileChooser (Gtk::Window& parent, string title, ARDOUR::Session* s)
1422 : SoundFileBrowser (parent, title, s, false)
1424 chooser.set_select_multiple (false);
1425 found_list_view.get_selection()->set_mode (SELECTION_SINGLE);
1426 freesound_list_view.get_selection()->set_mode (SELECTION_SINGLE);
1430 SoundFileChooser::on_hide ()
1432 ArdourDialog::on_hide();
1436 _session->cancel_audition();
1441 SoundFileChooser::get_filename ()
1443 vector<string> paths;
1445 paths = get_paths ();
1447 if (paths.empty()) {
1451 if (!Glib::file_test (paths.front(), Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
1455 return paths.front();
1458 SoundFileOmega::SoundFileOmega (Gtk::Window& parent, string title, ARDOUR::Session* s,
1459 uint32_t selected_audio_tracks,
1460 uint32_t selected_midi_tracks,
1462 Editing::ImportMode mode_hint)
1463 : SoundFileBrowser (parent, title, s, persistent)
1464 , copy_files_btn ( _("Copy files to session"))
1465 , selected_audio_track_cnt (selected_audio_tracks)
1466 , selected_midi_track_cnt (selected_midi_tracks)
1472 set_size_request (-1, 450);
1474 block_two.set_border_width (12);
1475 block_three.set_border_width (12);
1476 block_four.set_border_width (12);
1478 options.set_spacing (12);
1481 str.push_back (_("file timestamp"));
1482 str.push_back (_("edit point"));
1483 str.push_back (_("playhead"));
1484 str.push_back (_("session start"));
1485 set_popdown_strings (where_combo, str);
1486 where_combo.set_active_text (str.front());
1488 Label* l = manage (new Label);
1489 l->set_markup (_("<b>Add files as ...</b>"));
1491 vbox = manage (new VBox);
1492 vbox->set_border_width (12);
1493 vbox->set_spacing (6);
1494 vbox->pack_start (*l, false, false);
1495 vbox->pack_start (action_combo, false, false);
1496 hbox = manage (new HBox);
1497 hbox->pack_start (*vbox, false, false);
1498 options.pack_start (*hbox, false, false);
1500 /* dummy entry for action combo so that it doesn't look odd if we
1501 come up with no tracks selected.
1505 str.push_back (importmode2string (mode_hint));
1506 set_popdown_strings (action_combo, str);
1507 action_combo.set_active_text (str.front());
1508 action_combo.set_sensitive (false);
1510 l = manage (new Label);
1511 l->set_markup (_("<b>Insert at</b>"));
1513 vbox = manage (new VBox);
1514 vbox->set_border_width (12);
1515 vbox->set_spacing (6);
1516 vbox->pack_start (*l, false, false);
1517 vbox->pack_start (where_combo, false, false);
1518 hbox = manage (new HBox);
1519 hbox->pack_start (*vbox, false, false);
1520 options.pack_start (*hbox, false, false);
1523 l = manage (new Label);
1524 l->set_markup (_("<b>Mapping</b>"));
1526 vbox = manage (new VBox);
1527 vbox->set_border_width (12);
1528 vbox->set_spacing (6);
1529 vbox->pack_start (*l, false, false);
1530 vbox->pack_start (channel_combo, false, false);
1531 hbox = manage (new HBox);
1532 hbox->pack_start (*vbox, false, false);
1533 options.pack_start (*hbox, false, false);
1536 str.push_back (_("one track per file"));
1537 set_popdown_strings (channel_combo, str);
1538 channel_combo.set_active_text (str.front());
1539 channel_combo.set_sensitive (false);
1541 l = manage (new Label);
1542 l->set_markup (_("<b>Conversion quality</b>"));
1544 vbox = manage (new VBox);
1545 vbox->set_border_width (12);
1546 vbox->set_spacing (6);
1547 vbox->pack_start (*l, false, false);
1548 vbox->pack_start (src_combo, false, false);
1549 hbox = manage (new HBox);
1550 hbox->pack_start (*vbox, false, false);
1551 options.pack_start (*hbox, false, false);
1554 str.push_back (_("Best"));
1555 str.push_back (_("Good"));
1556 str.push_back (_("Quick"));
1557 str.push_back (_("Fast"));
1558 str.push_back (_("Fastest"));
1560 set_popdown_strings (src_combo, str);
1561 src_combo.set_active_text (str.front());
1562 src_combo.set_sensitive (false);
1566 action_combo.signal_changed().connect (sigc::mem_fun (*this, &SoundFileOmega::reset_options_noret));
1567 channel_combo.signal_changed().connect (sigc::mem_fun (*this, &SoundFileOmega::reset_options_noret));
1569 copy_files_btn.set_active (true);
1571 Gtk::Label* copy_label = dynamic_cast<Gtk::Label*>(copy_files_btn.get_child());
1574 copy_label->set_size_request (175, -1);
1575 copy_label->set_line_wrap (true);
1578 block_four.pack_start (copy_files_btn, false, false);
1580 options.pack_start (block_four, false, false);
1582 get_vbox()->pack_start (options, false, false);
1584 /* setup disposition map */
1586 disposition_map.insert (pair<string,ImportDisposition>(_("one track per file"), ImportDistinctFiles));
1587 disposition_map.insert (pair<string,ImportDisposition>(_("one track per channel"), ImportDistinctChannels));
1588 disposition_map.insert (pair<string,ImportDisposition>(_("merge files"), ImportMergeFiles));
1589 disposition_map.insert (pair<string,ImportDisposition>(_("sequence files"), ImportSerializeFiles));
1591 disposition_map.insert (pair<string,ImportDisposition>(_("one region per file"), ImportDistinctFiles));
1592 disposition_map.insert (pair<string,ImportDisposition>(_("one region per channel"), ImportDistinctChannels));
1593 disposition_map.insert (pair<string,ImportDisposition>(_("all files in one region"), ImportMergeFiles));
1594 disposition_map.insert (pair<string,ImportDisposition>(_("all files in one track"), ImportMergeFiles));
1596 chooser.signal_selection_changed().connect (sigc::mem_fun (*this, &SoundFileOmega::file_selection_changed));
1598 /* set size requests for a couple of combos to allow them to display the longest text
1599 they will ever be asked to display. This prevents them being resized when the user
1600 selects a file to import, which in turn prevents the size of the dialog from jumping
1604 t.push_back (_("one track per file"));
1605 t.push_back (_("one track per channel"));
1606 t.push_back (_("sequence files"));
1607 t.push_back (_("all files in one region"));
1608 set_popdown_strings (channel_combo, t);
1611 t.push_back (importmode2string (ImportAsTrack));
1612 t.push_back (importmode2string (ImportToTrack));
1613 t.push_back (importmode2string (ImportAsRegion));
1614 t.push_back (importmode2string (ImportAsTapeTrack));
1615 set_popdown_strings (action_combo, t);
1619 SoundFileOmega::set_mode (ImportMode mode)
1621 action_combo.set_active_text (importmode2string (mode));
1625 SoundFileOmega::get_mode () const
1627 return string2importmode (action_combo.get_active_text());
1631 SoundFileOmega::on_hide ()
1633 ArdourDialog::on_hide();
1635 _session->cancel_audition();
1640 SoundFileOmega::get_position() const
1642 string str = where_combo.get_active_text();
1644 if (str == _("file timestamp")) {
1645 return ImportAtTimestamp;
1646 } else if (str == _("edit point")) {
1647 return ImportAtEditPoint;
1648 } else if (str == _("playhead")) {
1649 return ImportAtPlayhead;
1651 return ImportAtStart;
1656 SoundFileOmega::get_src_quality() const
1658 string str = where_combo.get_active_text();
1660 if (str == _("Best")) {
1662 } else if (str == _("Good")) {
1664 } else if (str == _("Quick")) {
1666 } else if (str == _("Fast")) {
1674 SoundFileOmega::get_channel_disposition () const
1676 /* we use a map here because the channel combo can contain different strings
1677 depending on the state of the other combos. the map contains all possible strings
1678 and the ImportDisposition enum that corresponds to it.
1681 string str = channel_combo.get_active_text();
1682 DispositionMap::const_iterator x = disposition_map.find (str);
1684 if (x == disposition_map.end()) {
1685 fatal << string_compose (_("programming error: %1 (%2)"), "unknown string for import disposition", str) << endmsg;
1693 SoundFileOmega::reset (uint32_t selected_audio_tracks, uint32_t selected_midi_tracks)
1695 selected_audio_track_cnt = selected_audio_tracks;
1696 selected_midi_track_cnt = selected_midi_tracks;
1698 if (selected_audio_track_cnt == 0 && selected_midi_track_cnt > 0) {
1699 chooser.set_filter (midi_filter);
1700 } else if (selected_midi_track_cnt == 0 && selected_audio_track_cnt > 0) {
1701 chooser.set_filter (audio_filter);
1703 chooser.set_filter (audio_and_midi_filter);
1710 SoundFileOmega::file_selection_changed ()
1712 if (resetting_ourselves) {
1716 if (!reset_options ()) {
1717 set_response_sensitive (RESPONSE_OK, false);
1719 if (chooser.get_filenames().size() > 0) {
1720 set_response_sensitive (RESPONSE_OK, true);
1722 set_response_sensitive (RESPONSE_OK, false);