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_border_width (12);
526 passbox->set_spacing (6);
528 label = manage (new Label);
529 label->set_text (_("Tags:"));
530 passbox->pack_start (*label, false, false);
531 passbox->pack_start (freesound_entry, false, false);
533 label = manage (new Label);
534 label->set_text (_("Sort:"));
535 passbox->pack_start (*label, false, false);
536 passbox->pack_start (freesound_sort, false, false);
537 freesound_sort.clear_items();
539 // Order of the following must correspond with enum sortMethod
540 // in sfdb_freesound_mootcher.h
541 freesound_sort.append_text(_("None"));
542 freesound_sort.append_text(_("Longest"));
543 freesound_sort.append_text(_("Shortest"));
544 freesound_sort.append_text(_("Newest"));
545 freesound_sort.append_text(_("Oldest"));
546 freesound_sort.append_text(_("Most downloaded"));
547 freesound_sort.append_text(_("Least downloaded"));
548 freesound_sort.append_text(_("Highest rated"));
549 freesound_sort.append_text(_("Lowest rated"));
550 freesound_sort.set_active(0);
552 passbox->pack_start (freesound_search_btn, false, false);
553 passbox->pack_start (freesound_progress_bar);
554 passbox->pack_end (freesound_stop_btn, false, false);
555 freesound_stop_btn.set_label(_("Stop"));
557 Gtk::ScrolledWindow *scroll = manage(new ScrolledWindow);
558 scroll->add(freesound_list_view);
559 scroll->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
561 vbox = manage(new VBox);
562 vbox->pack_start (*passbox, 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.get_column(1)->set_expand(true);
571 freesound_list_view.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &SoundFileBrowser::freesound_list_view_selected));
573 freesound_list_view.get_selection()->set_mode (SELECTION_MULTIPLE);
574 freesound_list_view.signal_row_activated().connect (sigc::mem_fun (*this, &SoundFileBrowser::freesound_list_view_activated));
575 freesound_search_btn.signal_clicked().connect(sigc::mem_fun(*this, &SoundFileBrowser::freesound_search_clicked));
576 freesound_entry.signal_activate().connect(sigc::mem_fun(*this, &SoundFileBrowser::freesound_search_clicked));
577 freesound_stop_btn.signal_clicked().connect(sigc::mem_fun(*this, &SoundFileBrowser::freesound_stop_clicked));
578 notebook.append_page (*vbox, _("Search Freesound"));
584 notebook.set_size_request (500, -1);
585 notebook.signal_switch_page().connect (
586 sigc::hide_return (sigc::hide (sigc::hide (sigc::mem_fun (*this, &SoundFileBrowser::reset_options))))
591 add_button (Stock::CANCEL, RESPONSE_CANCEL);
592 add_button (Stock::APPLY, RESPONSE_APPLY);
593 add_button (Stock::OK, RESPONSE_OK);
597 SoundFileBrowser::~SoundFileBrowser ()
599 persistent_folder = chooser.get_current_folder();
604 SoundFileBrowser::on_show ()
606 ArdourDialog::on_show ();
611 SoundFileBrowser::clear_selection ()
613 chooser.unselect_all ();
614 found_list_view.get_selection()->unselect_all ();
618 SoundFileBrowser::chooser_file_activated ()
624 SoundFileBrowser::found_list_view_activated (const TreeModel::Path&, TreeViewColumn*)
630 SoundFileBrowser::freesound_list_view_activated (const TreeModel::Path&, TreeViewColumn*)
636 SoundFileBrowser::set_session (Session* s)
638 ArdourDialog::set_session (s);
639 preview.set_session (s);
644 remove_gain_meter ();
649 SoundFileBrowser::add_gain_meter ()
653 gm = new GainMeter (_session, 250);
655 boost::shared_ptr<Route> r = _session->the_auditioner ();
657 gm->set_controls (r, r->shared_peak_meter(), r->amp());
659 meter_packer.set_border_width (12);
660 meter_packer.pack_start (*gm, false, true);
661 hpacker.pack_end (meter_packer, false, false);
662 meter_packer.show_all ();
667 SoundFileBrowser::remove_gain_meter ()
670 meter_packer.remove (*gm);
671 hpacker.remove (meter_packer);
678 SoundFileBrowser::start_metering ()
680 metering_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (sigc::mem_fun(*this, &SoundFileBrowser::meter));
684 SoundFileBrowser::stop_metering ()
686 metering_connection.disconnect();
690 SoundFileBrowser::meter ()
692 if (is_mapped () && _session && gm) {
693 gm->update_meters ();
698 SoundFileBrowser::on_audio_filter (const FileFilter::Info& filter_info)
700 return AudioFileSource::safe_audio_file_extension (filter_info.filename);
704 SoundFileBrowser::on_midi_filter (const FileFilter::Info& filter_info)
706 return SMFSource::safe_midi_file_extension (filter_info.filename);
710 SoundFileBrowser::on_audio_and_midi_filter (const FileFilter::Info& filter_info)
712 return on_audio_filter (filter_info) || on_midi_filter (filter_info);
716 SoundFileBrowser::update_preview ()
718 if (preview.setup_labels (chooser.get_preview_filename())) {
719 if (preview.autoplay()) {
720 Glib::signal_idle().connect (sigc::mem_fun (preview, &SoundFileBox::audition_oneshot));
726 SoundFileBrowser::found_list_view_selected ()
728 if (!reset_options ()) {
729 set_response_sensitive (RESPONSE_OK, false);
733 TreeView::Selection::ListHandle_Path rows = found_list_view.get_selection()->get_selected_rows ();
736 TreeIter iter = found_list->get_iter(*rows.begin());
737 file = (*iter)[found_list_columns.pathname];
738 chooser.set_filename (file);
739 set_response_sensitive (RESPONSE_OK, true);
741 set_response_sensitive (RESPONSE_OK, false);
744 preview.setup_labels (file);
749 SoundFileBrowser::freesound_list_view_selected ()
751 freesound_download_cancel = false;
754 if (!reset_options ()) {
755 set_response_sensitive (RESPONSE_OK, false);
760 TreeView::Selection::ListHandle_Path rows = freesound_list_view.get_selection()->get_selected_rows ();
763 TreeIter iter = freesound_list->get_iter(*rows.begin());
765 string id = (*iter)[freesound_list_columns.id];
766 string uri = (*iter)[freesound_list_columns.uri];
767 string ofn = (*iter)[freesound_list_columns.filename];
769 // download the sound file
770 GdkCursor *prev_cursor;
771 prev_cursor = gdk_window_get_cursor (get_window()->gobj());
772 gdk_window_set_cursor (get_window()->gobj(), gdk_cursor_new(GDK_WATCH));
775 file = mootcher->getAudioFile(ofn, id, uri, this);
777 gdk_window_set_cursor (get_window()->gobj(), prev_cursor);
780 chooser.set_filename (file);
781 set_response_sensitive (RESPONSE_OK, true);
784 set_response_sensitive (RESPONSE_OK, false);
787 preview.setup_labels (file);
793 SoundFileBrowser::found_search_clicked ()
795 string tag_string = found_entry.get_text ();
799 if (!PBD::tokenize (tag_string, string(","), std::back_inserter (tags), true)) {
800 warning << _("SoundFileBrowser: Could not tokenize string: ") << tag_string << endmsg;
804 vector<string> results;
805 Library->search_members_and (results, tags);
808 for (vector<string>::iterator i = results.begin(); i != results.end(); ++i) {
809 TreeModel::iterator new_row = found_list->append();
810 TreeModel::Row row = *new_row;
811 string path = Glib::filename_from_uri (string ("file:") + *i);
812 row[found_list_columns.pathname] = path;
817 SoundFileBrowser::freesound_search_clicked ()
819 freesound_search_cancel = false;
824 SoundFileBrowser::freesound_stop_clicked ()
826 freesound_download_cancel = true;
827 freesound_search_cancel = true;
832 SoundFileBrowser::freesound_search()
835 freesound_list->clear();
837 string search_string = freesound_entry.get_text ();
838 enum sortMethod sort_method = (enum sortMethod) freesound_sort.get_active_row_number();
840 GdkCursor *prev_cursor;
841 prev_cursor = gdk_window_get_cursor (get_window()->gobj());
842 gdk_window_set_cursor (get_window()->gobj(), gdk_cursor_new(GDK_WATCH));
844 for (int page = 1; page <= 99; page++ ) {
847 prog = string_compose (_("Page %1, [Stop]->"), page);
848 freesound_progress_bar.set_text(prog);
849 while (Glib::MainContext::get_default()->iteration (false)) {
853 std::string theString = mootcher->searchText(
856 "", // filter, could do, e.g. "type:wav"
861 doc.read_buffer( theString );
862 XMLNode *root = doc.root();
865 error << "no root XML node!" << endmsg;
869 if ( strcmp(root->name().c_str(), "response") != 0) {
870 error << string_compose ("root node name == %1 != \"response\"", root->name()) << endmsg;
874 XMLNode *sounds_root = root->child("sounds");
877 error << "no child node \"sounds\" found!" << endmsg;
881 XMLNodeList sounds = sounds_root->children();
882 XMLNodeConstIterator niter;
884 for (niter = sounds.begin(); niter != sounds.end(); ++niter) {
886 if( strcmp( node->name().c_str(), "resource") != 0 ){
887 error << string_compose ("node->name()=%1 != \"resource\"", node->name()) << endmsg;
888 freesound_search_cancel = true;
892 // node->dump(cerr, "node:");
894 XMLNode *id_node = node->child ("id");
895 XMLNode *uri_node = node->child ("serve");
896 XMLNode *ofn_node = node->child ("original_filename");
897 XMLNode *dur_node = node->child ("duration");
899 if (id_node && uri_node && ofn_node && dur_node) {
901 std::string id = id_node->child("text")->content();
902 std::string uri = uri_node->child("text")->content();
903 std::string ofn = ofn_node->child("text")->content();
904 std::string dur = dur_node->child("text")->content();
907 // cerr << "id=" << id << ",uri=" << uri << ",ofn=" << ofn << ",dur=" << dur << endl;
909 double duration_seconds = atof(dur.c_str());
911 char duration_hhmmss[16];
912 if (duration_seconds >= 99 * 60 * 60) {
913 strcpy(duration_hhmmss, ">99h");
915 s = modf(duration_seconds/60, &m) * 60;
916 m = modf(m/60, &h) * 60;
917 sprintf(duration_hhmmss, "%02.fh:%02.fm:%04.1fs",
922 TreeModel::iterator new_row = freesound_list->append();
923 TreeModel::Row row = *new_row;
925 row[freesound_list_columns.id ] = id;
926 row[freesound_list_columns.uri ] = uri;
927 row[freesound_list_columns.filename] = ofn;
928 row[freesound_list_columns.duration] = duration_hhmmss;
933 if (freesound_search_cancel)
938 gdk_window_set_cursor (get_window()->gobj(), prev_cursor);
940 freesound_progress_bar.set_text("");
946 SoundFileBrowser::get_paths ()
948 vector<string> results;
950 int n = notebook.get_current_page ();
953 vector<string> filenames = chooser.get_filenames();
954 vector<string>::iterator i;
956 for (i = filenames.begin(); i != filenames.end(); ++i) {
958 if ((!stat((*i).c_str(), &buf)) && S_ISREG(buf.st_mode)) {
959 results.push_back (*i);
965 typedef TreeView::Selection::ListHandle_Path ListPath;
967 ListPath rows = found_list_view.get_selection()->get_selected_rows ();
968 for (ListPath::iterator i = rows.begin() ; i != rows.end(); ++i) {
969 TreeIter iter = found_list->get_iter(*i);
970 string str = (*iter)[found_list_columns.pathname];
972 results.push_back (str);
976 typedef TreeView::Selection::ListHandle_Path ListPath;
978 ListPath rows = freesound_list_view.get_selection()->get_selected_rows ();
979 for (ListPath::iterator i = rows.begin() ; i != rows.end(); ++i) {
980 TreeIter iter = freesound_list->get_iter(*i);
981 string id = (*iter)[freesound_list_columns.id];
982 string uri = (*iter)[freesound_list_columns.uri];
983 string ofn = (*iter)[freesound_list_columns.filename];
985 GdkCursor *prev_cursor;
986 prev_cursor = gdk_window_get_cursor (get_window()->gobj());
987 gdk_window_set_cursor (get_window()->gobj(), gdk_cursor_new(GDK_WATCH));
990 string str = mootcher->getAudioFile(ofn, id, uri, this);
992 results.push_back (str);
995 gdk_window_set_cursor (get_window()->gobj(), prev_cursor);
1005 SoundFileOmega::reset_options_noret ()
1007 if (!resetting_ourselves) {
1008 (void) reset_options ();
1013 SoundFileOmega::reset_options ()
1015 vector<string> paths = get_paths ();
1017 if (paths.empty()) {
1019 channel_combo.set_sensitive (false);
1020 action_combo.set_sensitive (false);
1021 where_combo.set_sensitive (false);
1022 copy_files_btn.set_active (true);
1023 copy_files_btn.set_sensitive (false);
1029 channel_combo.set_sensitive (true);
1030 action_combo.set_sensitive (true);
1031 where_combo.set_sensitive (true);
1033 /* if we get through this function successfully, this may be
1034 reset at the end, once we know if we can use hard links
1035 to do embedding (or if we are importing a MIDI file).
1038 if (Config->get_only_copy_imported_files()) {
1039 copy_files_btn.set_sensitive (false);
1041 copy_files_btn.set_sensitive (false);
1047 bool selection_includes_multichannel;
1048 bool selection_can_be_embedded_with_links = check_link_status (_session, paths);
1051 /* See if we are thinking about importing any MIDI files */
1052 vector<string>::iterator i = paths.begin ();
1053 while (i != paths.end() && SMFSource::safe_midi_file_extension (*i) == false) {
1056 bool const have_a_midi_file = (i != paths.end ());
1058 if (check_info (paths, same_size, src_needed, selection_includes_multichannel)) {
1059 Glib::signal_idle().connect (sigc::mem_fun (*this, &SoundFileOmega::bad_file_message));
1063 string existing_choice;
1064 vector<string> action_strings;
1066 resetting_ourselves = true;
1068 if (chooser.get_filter() == &audio_filter) {
1072 if (selected_audio_track_cnt > 0) {
1073 if (channel_combo.get_active_text().length()) {
1074 ImportDisposition id = get_channel_disposition();
1077 case Editing::ImportDistinctFiles:
1078 if (selected_audio_track_cnt == paths.size()) {
1079 action_strings.push_back (importmode2string (ImportToTrack));
1083 case Editing::ImportDistinctChannels:
1084 /* XXX it would be nice to allow channel-per-selected track
1085 but its too hard we don't want to deal with all the
1086 different per-file + per-track channel configurations.
1091 action_strings.push_back (importmode2string (ImportToTrack));
1101 if (selected_midi_track_cnt > 0) {
1102 action_strings.push_back (importmode2string (ImportToTrack));
1106 action_strings.push_back (importmode2string (ImportAsTrack));
1107 action_strings.push_back (importmode2string (ImportAsRegion));
1108 action_strings.push_back (importmode2string (ImportAsTapeTrack));
1110 existing_choice = action_combo.get_active_text();
1112 set_popdown_strings (action_combo, action_strings);
1114 /* preserve any existing choice, if possible */
1117 if (existing_choice.length()) {
1118 vector<string>::iterator x;
1119 for (x = action_strings.begin(); x != action_strings.end(); ++x) {
1120 if (*x == existing_choice) {
1121 action_combo.set_active_text (existing_choice);
1125 if (x == action_strings.end()) {
1126 action_combo.set_active_text (action_strings.front());
1129 action_combo.set_active_text (action_strings.front());
1132 resetting_ourselves = false;
1134 if ((mode = get_mode()) == ImportAsRegion) {
1135 where_combo.set_sensitive (false);
1137 where_combo.set_sensitive (true);
1140 vector<string> channel_strings;
1142 if (mode == ImportAsTrack || mode == ImportAsTapeTrack || mode == ImportToTrack) {
1143 channel_strings.push_back (_("one track per file"));
1145 if (selection_includes_multichannel) {
1146 channel_strings.push_back (_("one track per channel"));
1149 if (paths.size() > 1) {
1150 /* tape tracks are a single region per track, so we cannot
1151 sequence multiple files.
1153 if (mode != ImportAsTapeTrack) {
1154 channel_strings.push_back (_("sequence files"));
1157 channel_strings.push_back (_("all files in one track"));
1158 channel_strings.push_back (_("merge files"));
1164 channel_strings.push_back (_("one region per file"));
1166 if (selection_includes_multichannel) {
1167 channel_strings.push_back (_("one region per channel"));
1170 if (paths.size() > 1) {
1172 channel_strings.push_back (_("all files in one region"));
1177 resetting_ourselves = true;
1179 existing_choice = channel_combo.get_active_text();
1181 set_popdown_strings (channel_combo, channel_strings);
1183 /* preserve any existing choice, if possible */
1185 if (existing_choice.length()) {
1186 vector<string>::iterator x;
1187 for (x = channel_strings.begin(); x != channel_strings.end(); ++x) {
1188 if (*x == existing_choice) {
1189 channel_combo.set_active_text (existing_choice);
1193 if (x == channel_strings.end()) {
1194 channel_combo.set_active_text (channel_strings.front());
1197 channel_combo.set_active_text (channel_strings.front());
1200 resetting_ourselves = false;
1203 src_combo.set_sensitive (true);
1205 src_combo.set_sensitive (false);
1208 /* We must copy MIDI files or those from Freesound */
1209 bool const must_copy = have_a_midi_file || notebook.get_current_page() == 2;
1211 if (Config->get_only_copy_imported_files()) {
1213 if (selection_can_be_embedded_with_links && !must_copy) {
1214 copy_files_btn.set_sensitive (true);
1217 copy_files_btn.set_active (true);
1219 copy_files_btn.set_sensitive (false);
1225 copy_files_btn.set_active (true);
1227 copy_files_btn.set_sensitive (!must_copy);
1235 SoundFileOmega::bad_file_message()
1237 MessageDialog msg (*this,
1238 string_compose (_("One or more of the selected files\ncannot be used by %1"), PROGRAM_NAME),
1243 resetting_ourselves = true;
1244 chooser.unselect_uri (chooser.get_preview_uri());
1245 resetting_ourselves = false;
1251 SoundFileOmega::check_info (const vector<string>& paths, bool& same_size, bool& src_needed, bool& multichannel)
1260 multichannel = false;
1262 for (vector<string>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
1264 if (AudioFileSource::get_soundfile_info (*i, info, errmsg)) {
1265 if (info.channels > 1) {
1266 multichannel = true;
1271 if (sz != info.length) {
1276 if (info.samplerate != _session->frame_rate()) {
1280 } else if (SMFSource::safe_midi_file_extension (*i)) {
1284 if (reader.num_tracks() > 1) {
1285 multichannel = true; // "channel" == track here...
1288 /* XXX we need err = true handling here in case
1289 we can't check the file
1302 SoundFileOmega::check_link_status (const Session* s, const vector<string>& paths)
1304 std::string tmpdir(Glib::build_filename (s->session_directory().sound_path(), "linktest"));
1307 if (mkdir (tmpdir.c_str(), 0744)) {
1308 if (errno != EEXIST) {
1313 for (vector<string>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
1315 char tmpc[MAXPATHLEN+1];
1317 snprintf (tmpc, sizeof(tmpc), "%s/%s", tmpdir.c_str(), Glib::path_get_basename (*i).c_str());
1321 if (link ((*i).c_str(), tmpc)) {
1331 rmdir (tmpdir.c_str());
1335 SoundFileChooser::SoundFileChooser (Gtk::Window& parent, string title, ARDOUR::Session* s)
1336 : SoundFileBrowser (parent, title, s, false)
1338 chooser.set_select_multiple (false);
1339 found_list_view.get_selection()->set_mode (SELECTION_SINGLE);
1340 freesound_list_view.get_selection()->set_mode (SELECTION_SINGLE);
1344 SoundFileChooser::on_hide ()
1346 ArdourDialog::on_hide();
1350 _session->cancel_audition();
1355 SoundFileChooser::get_filename ()
1357 vector<string> paths;
1359 paths = get_paths ();
1361 if (paths.empty()) {
1365 if (!Glib::file_test (paths.front(), Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
1369 return paths.front();
1372 SoundFileOmega::SoundFileOmega (Gtk::Window& parent, string title, ARDOUR::Session* s,
1373 uint32_t selected_audio_tracks,
1374 uint32_t selected_midi_tracks,
1376 Editing::ImportMode mode_hint)
1377 : SoundFileBrowser (parent, title, s, persistent)
1378 , copy_files_btn ( _("Copy files to session"))
1379 , selected_audio_track_cnt (selected_audio_tracks)
1380 , selected_midi_track_cnt (selected_midi_tracks)
1386 set_size_request (-1, 450);
1388 block_two.set_border_width (12);
1389 block_three.set_border_width (12);
1390 block_four.set_border_width (12);
1392 options.set_spacing (12);
1395 str.push_back (_("file timestamp"));
1396 str.push_back (_("edit point"));
1397 str.push_back (_("playhead"));
1398 str.push_back (_("session start"));
1399 set_popdown_strings (where_combo, str);
1400 where_combo.set_active_text (str.front());
1402 Label* l = manage (new Label);
1403 l->set_markup (_("<b>Add files as ...</b>"));
1405 vbox = manage (new VBox);
1406 vbox->set_border_width (12);
1407 vbox->set_spacing (6);
1408 vbox->pack_start (*l, false, false);
1409 vbox->pack_start (action_combo, false, false);
1410 hbox = manage (new HBox);
1411 hbox->pack_start (*vbox, false, false);
1412 options.pack_start (*hbox, false, false);
1414 /* dummy entry for action combo so that it doesn't look odd if we
1415 come up with no tracks selected.
1419 str.push_back (importmode2string (mode_hint));
1420 set_popdown_strings (action_combo, str);
1421 action_combo.set_active_text (str.front());
1422 action_combo.set_sensitive (false);
1424 l = manage (new Label);
1425 l->set_markup (_("<b>Insert at</b>"));
1427 vbox = manage (new VBox);
1428 vbox->set_border_width (12);
1429 vbox->set_spacing (6);
1430 vbox->pack_start (*l, false, false);
1431 vbox->pack_start (where_combo, false, false);
1432 hbox = manage (new HBox);
1433 hbox->pack_start (*vbox, false, false);
1434 options.pack_start (*hbox, false, false);
1437 l = manage (new Label);
1438 l->set_markup (_("<b>Mapping</b>"));
1440 vbox = manage (new VBox);
1441 vbox->set_border_width (12);
1442 vbox->set_spacing (6);
1443 vbox->pack_start (*l, false, false);
1444 vbox->pack_start (channel_combo, false, false);
1445 hbox = manage (new HBox);
1446 hbox->pack_start (*vbox, false, false);
1447 options.pack_start (*hbox, false, false);
1450 str.push_back (_("one track per file"));
1451 set_popdown_strings (channel_combo, str);
1452 channel_combo.set_active_text (str.front());
1453 channel_combo.set_sensitive (false);
1455 l = manage (new Label);
1456 l->set_markup (_("<b>Conversion quality</b>"));
1458 vbox = manage (new VBox);
1459 vbox->set_border_width (12);
1460 vbox->set_spacing (6);
1461 vbox->pack_start (*l, false, false);
1462 vbox->pack_start (src_combo, false, false);
1463 hbox = manage (new HBox);
1464 hbox->pack_start (*vbox, false, false);
1465 options.pack_start (*hbox, false, false);
1468 str.push_back (_("Best"));
1469 str.push_back (_("Good"));
1470 str.push_back (_("Quick"));
1471 str.push_back (_("Fast"));
1472 str.push_back (_("Fastest"));
1474 set_popdown_strings (src_combo, str);
1475 src_combo.set_active_text (str.front());
1476 src_combo.set_sensitive (false);
1480 action_combo.signal_changed().connect (sigc::mem_fun (*this, &SoundFileOmega::reset_options_noret));
1481 channel_combo.signal_changed().connect (sigc::mem_fun (*this, &SoundFileOmega::reset_options_noret));
1483 copy_files_btn.set_active (true);
1485 Gtk::Label* copy_label = dynamic_cast<Gtk::Label*>(copy_files_btn.get_child());
1488 copy_label->set_size_request (175, -1);
1489 copy_label->set_line_wrap (true);
1492 block_four.pack_start (copy_files_btn, false, false);
1494 options.pack_start (block_four, false, false);
1496 get_vbox()->pack_start (options, false, false);
1498 /* setup disposition map */
1500 disposition_map.insert (pair<string,ImportDisposition>(_("one track per file"), ImportDistinctFiles));
1501 disposition_map.insert (pair<string,ImportDisposition>(_("one track per channel"), ImportDistinctChannels));
1502 disposition_map.insert (pair<string,ImportDisposition>(_("merge files"), ImportMergeFiles));
1503 disposition_map.insert (pair<string,ImportDisposition>(_("sequence files"), ImportSerializeFiles));
1505 disposition_map.insert (pair<string,ImportDisposition>(_("one region per file"), ImportDistinctFiles));
1506 disposition_map.insert (pair<string,ImportDisposition>(_("one region per channel"), ImportDistinctChannels));
1507 disposition_map.insert (pair<string,ImportDisposition>(_("all files in one region"), ImportMergeFiles));
1508 disposition_map.insert (pair<string,ImportDisposition>(_("all files in one track"), ImportMergeFiles));
1510 chooser.signal_selection_changed().connect (sigc::mem_fun (*this, &SoundFileOmega::file_selection_changed));
1512 /* set size requests for a couple of combos to allow them to display the longest text
1513 they will ever be asked to display. This prevents them being resized when the user
1514 selects a file to import, which in turn prevents the size of the dialog from jumping
1518 t.push_back (_("one track per file"));
1519 t.push_back (_("one track per channel"));
1520 t.push_back (_("sequence files"));
1521 t.push_back (_("all files in one region"));
1522 set_popdown_strings (channel_combo, t);
1525 t.push_back (importmode2string (ImportAsTrack));
1526 t.push_back (importmode2string (ImportToTrack));
1527 t.push_back (importmode2string (ImportAsRegion));
1528 t.push_back (importmode2string (ImportAsTapeTrack));
1529 set_popdown_strings (action_combo, t);
1533 SoundFileOmega::set_mode (ImportMode mode)
1535 action_combo.set_active_text (importmode2string (mode));
1539 SoundFileOmega::get_mode () const
1541 return string2importmode (action_combo.get_active_text());
1545 SoundFileOmega::on_hide ()
1547 ArdourDialog::on_hide();
1549 _session->cancel_audition();
1554 SoundFileOmega::get_position() const
1556 string str = where_combo.get_active_text();
1558 if (str == _("file timestamp")) {
1559 return ImportAtTimestamp;
1560 } else if (str == _("edit point")) {
1561 return ImportAtEditPoint;
1562 } else if (str == _("playhead")) {
1563 return ImportAtPlayhead;
1565 return ImportAtStart;
1570 SoundFileOmega::get_src_quality() const
1572 string str = where_combo.get_active_text();
1574 if (str == _("Best")) {
1576 } else if (str == _("Good")) {
1578 } else if (str == _("Quick")) {
1580 } else if (str == _("Fast")) {
1588 SoundFileOmega::get_channel_disposition () const
1590 /* we use a map here because the channel combo can contain different strings
1591 depending on the state of the other combos. the map contains all possible strings
1592 and the ImportDisposition enum that corresponds to it.
1595 string str = channel_combo.get_active_text();
1596 DispositionMap::const_iterator x = disposition_map.find (str);
1598 if (x == disposition_map.end()) {
1599 fatal << string_compose (_("programming error: %1 (%2)"), "unknown string for import disposition", str) << endmsg;
1607 SoundFileOmega::reset (uint32_t selected_audio_tracks, uint32_t selected_midi_tracks)
1609 selected_audio_track_cnt = selected_audio_tracks;
1610 selected_midi_track_cnt = selected_midi_tracks;
1612 if (selected_audio_track_cnt == 0 && selected_midi_track_cnt > 0) {
1613 chooser.set_filter (midi_filter);
1614 } else if (selected_midi_track_cnt == 0 && selected_audio_track_cnt > 0) {
1615 chooser.set_filter (audio_filter);
1617 chooser.set_filter (audio_and_midi_filter);
1624 SoundFileOmega::file_selection_changed ()
1626 if (resetting_ourselves) {
1630 if (!reset_options ()) {
1631 set_response_sensitive (RESPONSE_OK, false);
1633 if (chooser.get_filenames().size() > 0) {
1634 set_response_sensitive (RESPONSE_OK, true);
1636 set_response_sensitive (RESPONSE_OK, false);