65ae061e5c3da5b564376647be94212da200e49d
[ardour.git] / gtk2_ardour / sfdb_ui.cc
1 /*
2     Copyright (C) 2005-2006 Paul Davis
3
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.
8
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.
13
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.
17
18 */
19
20 #include <map>
21 #include <cerrno>
22 #include <sstream>
23
24 #include <unistd.h>
25 #include <sys/stat.h>
26 #include <sys/param.h>
27
28 #include <gtkmm/box.h>
29 #include <gtkmm/stock.h>
30 #include <glibmm/fileutils.h>
31
32 #include "pbd/convert.h"
33 #include "pbd/tokenizer.h"
34 #include "pbd/enumwriter.h"
35 #include "pbd/pthread_utils.h"
36 #include "pbd/xml++.h"
37
38 #include <gtkmm2ext/utils.h>
39
40 #include "evoral/SMF.hpp"
41
42 #include "ardour/amp.h"
43 #include "ardour/audio_library.h"
44 #include "ardour/auditioner.h"
45 #include "ardour/audioregion.h"
46 #include "ardour/audiofilesource.h"
47 #include "ardour/smf_source.h"
48 #include "ardour/region_factory.h"
49 #include "ardour/source_factory.h"
50 #include "ardour/session.h"
51 #include "ardour/session_directory.h"
52 #include "ardour/profile.h"
53
54 #include "ardour_ui.h"
55 #include "editing.h"
56 #include "gui_thread.h"
57 #include "prompter.h"
58 #include "sfdb_ui.h"
59 #include "editing.h"
60 #include "utils.h"
61 #include "gain_meter.h"
62
63 #ifdef FREESOUND
64 #include "sfdb_freesound_mootcher.h"
65 #endif
66
67 #include "i18n.h"
68
69 using namespace ARDOUR;
70 using namespace PBD;
71 using namespace std;
72 using namespace Gtk;
73 using namespace Gtkmm2ext;
74 using namespace Editing;
75
76 using Glib::ustring;
77
78 ustring SoundFileBrowser::persistent_folder;
79
80 static ImportMode
81 string2importmode (string str)
82 {
83         if (str == _("as new tracks")) {
84                 return ImportAsTrack;
85         } else if (str == _("to selected tracks")) {
86                 return ImportToTrack;
87         } else if (str == _("to region list")) {
88                 return ImportAsRegion;
89         } else if (str == _("as new tape tracks")) {
90                 return ImportAsTapeTrack;
91         }
92
93         warning << string_compose (_("programming error: unknown import mode string %1"), str) << endmsg;
94
95         return ImportAsTrack;
96 }
97
98 static string
99 importmode2string (ImportMode mode)
100 {
101         switch (mode) {
102         case ImportAsTrack:
103                 return _("as new tracks");
104         case ImportToTrack:
105                 return _("to selected tracks");
106         case ImportAsRegion:
107                 return _("to region list");
108         case ImportAsTapeTrack:
109                 return _("as new tape tracks");
110         }
111         /*NOTREACHED*/
112         return _("as new tracks");
113 }
114
115 SoundFileBox::SoundFileBox (bool persistent)
116         : table (6, 2),
117           length_clock ("sfboxLengthClock", !persistent, "EditCursorClock", false, false, true, false),
118           timecode_clock ("sfboxTimecodeClock", !persistent, "EditCursorClock", false, false, false, false),
119           main_box (false, 6),
120           autoplay_btn (_("Auto-play"))
121
122 {
123         set_name (X_("SoundFileBox"));
124         set_size_request (300, -1);
125
126         preview_label.set_markup (_("<b>Sound File Information</b>"));
127
128         border_frame.set_label_widget (preview_label);
129         border_frame.add (main_box);
130
131         pack_start (border_frame, true, true);
132         set_border_width (6);
133
134         main_box.set_border_width (6);
135
136         length.set_text (_("Length:"));
137         length.set_alignment (1, 0.5);
138         timecode.set_text (_("Timestamp:"));
139         timecode.set_alignment (1, 0.5);
140         format.set_text (_("Format:"));
141         format.set_alignment (1, 0.5);
142         channels.set_text (_("Channels:"));
143         channels.set_alignment (1, 0.5);
144         samplerate.set_text (_("Sample rate:"));
145         samplerate.set_alignment (1, 0.5);
146
147         format_text.set_max_width_chars (8);
148         format_text.set_ellipsize (Pango::ELLIPSIZE_END);
149         format_text.set_alignment (0, 1);
150
151         table.set_col_spacings (6);
152         table.set_homogeneous (false);
153         table.set_row_spacings (6);
154
155         table.attach (channels, 0, 1, 0, 1, FILL, FILL);
156         table.attach (samplerate, 0, 1, 1, 2, FILL, FILL);
157         table.attach (format, 0, 1, 2, 4, FILL, FILL);
158         table.attach (length, 0, 1, 4, 5, FILL, FILL);
159         table.attach (timecode, 0, 1, 5, 6, FILL, FILL);
160
161         table.attach (channels_value, 1, 2, 0, 1, FILL, FILL);
162         table.attach (samplerate_value, 1, 2, 1, 2, FILL, FILL);
163         table.attach (format_text, 1, 2, 2, 4, FILL, FILL);
164         table.attach (length_clock, 1, 2, 4, 5, FILL, FILL);
165         table.attach (timecode_clock, 1, 2, 5, 6, FILL, FILL);
166
167         length_clock.set_mode (ARDOUR_UI::instance()->secondary_clock.mode());
168         timecode_clock.set_mode (AudioClock::Timecode);
169
170         main_box.pack_start (table, false, false);
171
172         tags_entry.set_editable (true);
173         tags_entry.signal_focus_out_event().connect (sigc::mem_fun (*this, &SoundFileBox::tags_entry_left));
174
175         Label* label = manage (new Label (_("Tags:")));
176         label->set_alignment (0.0f, 0.5f);
177         main_box.pack_start (*label, false, false);
178         main_box.pack_start (tags_entry, true, true);
179
180         main_box.pack_start (bottom_box, false, false);
181
182         play_btn.set_image (*(manage (new Image (Stock::MEDIA_PLAY, ICON_SIZE_BUTTON))));
183         play_btn.set_label (_("Play"));
184
185         stop_btn.set_image (*(manage (new Image (Stock::MEDIA_STOP, ICON_SIZE_BUTTON))));
186         stop_btn.set_label (_("Stop"));
187
188         bottom_box.set_homogeneous (false);
189         bottom_box.set_spacing (6);
190         bottom_box.pack_start(play_btn, true, true);
191         bottom_box.pack_start(stop_btn, true, true);
192         bottom_box.pack_start(autoplay_btn, false, false);
193
194         play_btn.signal_clicked().connect (sigc::mem_fun (*this, &SoundFileBox::audition));
195         stop_btn.signal_clicked().connect (sigc::mem_fun (*this, &SoundFileBox::stop_audition));
196
197         channels_value.set_alignment (0.0f, 0.5f);
198         samplerate_value.set_alignment (0.0f, 0.5f);
199 }
200
201 void
202 SoundFileBox::set_session(Session* s)
203 {
204         SessionHandlePtr::set_session (s);
205
206         length_clock.set_session (s);
207         timecode_clock.set_session (s);
208
209         if (!_session) {
210                 play_btn.set_sensitive (false);
211                 stop_btn.set_sensitive (false);
212         }
213 }
214
215 bool
216 SoundFileBox::setup_labels (const ustring& filename)
217 {
218         if (!path.empty()) {
219                 // save existing tags
220                 tags_changed ();
221         }
222
223         path = filename;
224
225         string error_msg;
226
227         if(!AudioFileSource::get_soundfile_info (filename, sf_info, error_msg)) {
228
229                 preview_label.set_markup (_("<b>Sound File Information</b>"));
230                 format_text.set_text ("");
231                 channels_value.set_text ("");
232                 samplerate_value.set_text ("");
233                 tags_entry.get_buffer()->set_text ("");
234
235                 length_clock.set (0);
236                 timecode_clock.set (0);
237
238                 tags_entry.set_sensitive (false);
239                 play_btn.set_sensitive (false);
240
241                 return false;
242         }
243
244         preview_label.set_markup (string_compose ("<b>%1</b>", Glib::path_get_basename (filename)));
245         std::string n = sf_info.format_name;
246         if (n.substr (0, 8) == X_("Format: ")) {
247                 n = n.substr (8);
248         }
249         format_text.set_text (n);
250         channels_value.set_text (to_string (sf_info.channels, std::dec));
251
252         if (_session && sf_info.samplerate != _session->frame_rate()) {
253                 samplerate.set_markup (string_compose ("<b>%1</b>", _("Sample rate:")));
254                 samplerate_value.set_markup (string_compose (X_("<b>%1 Hz</b>"), sf_info.samplerate));
255                 samplerate_value.set_name ("NewSessionSR1Label");
256                 samplerate.set_name ("NewSessionSR1Label");
257         } else {
258                 samplerate.set_text (_("Sample rate:"));
259                 samplerate_value.set_text (string_compose (X_("%1 Hz"), sf_info.samplerate));
260                 samplerate_value.set_name ("NewSessionSR2Label");
261                 samplerate.set_name ("NewSessionSR2Label");
262         }
263
264         nframes_t const nfr = _session ? _session->nominal_frame_rate() : 25;
265         double src_coef = (double) nfr / sf_info.samplerate;
266
267         length_clock.set (sf_info.length * src_coef + 0.5, true);
268         timecode_clock.set (sf_info.timecode * src_coef + 0.5, true);
269
270         // this is a hack that is fixed in trunk, i think (august 26th, 2007)
271
272         vector<string> tags = Library->get_tags (string ("//") + filename);
273
274         stringstream tag_string;
275         for (vector<string>::iterator i = tags.begin(); i != tags.end(); ++i) {
276                 if (i != tags.begin()) {
277                         tag_string << ", ";
278                 }
279                 tag_string << *i;
280         }
281         tags_entry.get_buffer()->set_text (tag_string.str());
282
283         tags_entry.set_sensitive (true);
284         if (_session) {
285                 play_btn.set_sensitive (true);
286         }
287
288         return true;
289 }
290
291 bool
292 SoundFileBox::autoplay() const
293 {
294         return autoplay_btn.get_active();
295 }
296
297 bool
298 SoundFileBox::audition_oneshot()
299 {
300         audition ();
301         return false;
302 }
303
304 void
305 SoundFileBox::audition ()
306 {
307         if (!_session) {
308                 return;
309         }
310
311         _session->cancel_audition();
312
313         if (!Glib::file_test (path, Glib::FILE_TEST_EXISTS)) {
314                 warning << string_compose(_("Could not read file: %1 (%2)."), path, strerror(errno)) << endmsg;
315                 return;
316         }
317
318         boost::shared_ptr<Region> r;
319         SourceList srclist;
320         boost::shared_ptr<AudioFileSource> afs;
321         bool old_sbp = AudioSource::get_build_peakfiles ();
322
323         /* don't even think of building peakfiles for these files */
324
325         AudioSource::set_build_peakfiles (false);
326
327         for (int n = 0; n < sf_info.channels; ++n) {
328                 try {
329                         afs = boost::dynamic_pointer_cast<AudioFileSource> (
330                                         SourceFactory::createReadable (DataType::AUDIO, *_session,
331                                                         path, n, Source::Flag (0), false));
332
333                         srclist.push_back(afs);
334
335                 } catch (failed_constructor& err) {
336                         error << _("Could not access soundfile: ") << path << endmsg;
337                         AudioSource::set_build_peakfiles (old_sbp);
338                         return;
339                 }
340         }
341
342         AudioSource::set_build_peakfiles (old_sbp);
343
344         if (srclist.empty()) {
345                 return;
346         }
347
348         afs = boost::dynamic_pointer_cast<AudioFileSource> (srclist[0]);
349         string rname = region_name_from_path (afs->path(), false);
350         
351         PropertyList plist; 
352         
353         plist.add (ARDOUR::Properties::start, 0);
354         plist.add (ARDOUR::Properties::length, srclist[0]->length(srclist[0]->timeline_position()));
355         plist.add (ARDOUR::Properties::name, rname);
356         plist.add (ARDOUR::Properties::layer, 0);
357         
358         r = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (srclist, plist, false));
359
360         _session->audition_region(r);
361 }
362
363 void
364 SoundFileBox::stop_audition ()
365 {
366         if (_session) {
367                 _session->cancel_audition();
368         }
369 }
370
371 bool
372 SoundFileBox::tags_entry_left (GdkEventFocus *)
373 {
374         tags_changed ();
375         return false;
376 }
377
378 void
379 SoundFileBox::tags_changed ()
380 {
381         string tag_string = tags_entry.get_buffer()->get_text ();
382
383         if (tag_string.empty()) {
384                 return;
385         }
386
387         vector<string> tags;
388
389         if (!PBD::tokenize (tag_string, string(",\n"), std::back_inserter (tags), true)) {
390                 warning << _("SoundFileBox: Could not tokenize string: ") << tag_string << endmsg;
391                 return;
392         }
393
394         save_tags (tags);
395 }
396
397 void
398 SoundFileBox::save_tags (const vector<string>& tags)
399 {
400         Library->set_tags (string ("//") + path, tags);
401         Library->save_changes ();
402 }
403
404 SoundFileBrowser::SoundFileBrowser (Gtk::Window& parent, string title, ARDOUR::Session* s, bool persistent)
405         : ArdourDialog (parent, title, false, false),
406           found_list (ListStore::create(found_list_columns)),
407           freesound_list (ListStore::create(freesound_list_columns)),
408           chooser (FILE_CHOOSER_ACTION_OPEN),
409           preview (persistent),
410           found_search_btn (_("Search")),
411           found_list_view (found_list),
412           freesound_search_btn (_("Start Downloading")),
413           freesound_list_view (freesound_list)
414 {
415         resetting_ourselves = false;
416         gm = 0;
417
418         resetting_ourselves = false;
419         gm = 0;
420
421         if (ARDOUR::Profile->get_sae()) {
422                 chooser.add_shortcut_folder_uri("file:///Library/GarageBand/Apple Loops");
423                 chooser.add_shortcut_folder_uri("file:///Library/Application Support/GarageBand/Instrument Library/Sampler/Sampler Files");
424         }
425
426
427         //add the file chooser
428         {
429                 chooser.set_border_width (12);
430
431                 audio_filter.add_custom (FILE_FILTER_FILENAME, sigc::mem_fun(*this, &SoundFileBrowser::on_audio_filter));
432                 audio_filter.set_name (_("Audio files"));
433
434                 midi_filter.add_custom (FILE_FILTER_FILENAME, sigc::mem_fun(*this, &SoundFileBrowser::on_midi_filter));
435                 midi_filter.set_name (_("MIDI files"));
436
437                 matchall_filter.add_pattern ("*.*");
438                 matchall_filter.set_name (_("All files"));
439
440                 chooser.add_filter (audio_filter);
441                 chooser.add_filter (midi_filter);
442                 chooser.add_filter (matchall_filter);
443                 chooser.set_select_multiple (true);
444                 chooser.signal_update_preview().connect(sigc::mem_fun(*this, &SoundFileBrowser::update_preview));
445                 chooser.signal_file_activated().connect (sigc::mem_fun (*this, &SoundFileBrowser::chooser_file_activated));
446
447                 if (!persistent_folder.empty()) {
448                         chooser.set_current_folder (persistent_folder);
449                 }
450                 notebook.append_page (chooser, _("Browse Files"));
451         }
452
453         hpacker.set_spacing (6);
454         hpacker.pack_start (notebook, true, true);
455         hpacker.pack_start (preview, false, false);
456
457         get_vbox()->pack_start (hpacker, true, true);
458
459         //add tag search
460         {
461                 VBox* vbox;
462                 HBox* hbox;
463
464
465                 hbox = manage(new HBox);
466                 hbox->pack_start (found_entry);
467                 hbox->pack_start (found_search_btn);
468
469                 Gtk::ScrolledWindow *scroll = manage(new ScrolledWindow);
470                 scroll->add(found_list_view);
471                 scroll->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
472
473                 vbox = manage(new VBox);
474                 vbox->pack_start (*hbox, PACK_SHRINK);
475                 vbox->pack_start (*scroll);
476
477                 found_list_view.append_column(_("Paths"), found_list_columns.pathname);
478
479                 found_list_view.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &SoundFileBrowser::found_list_view_selected));
480
481                 found_list_view.signal_row_activated().connect (sigc::mem_fun (*this, &SoundFileBrowser::found_list_view_activated));
482
483                 found_search_btn.signal_clicked().connect(sigc::mem_fun(*this, &SoundFileBrowser::found_search_clicked));
484                 found_entry.signal_activate().connect(sigc::mem_fun(*this, &SoundFileBrowser::found_search_clicked));
485
486                 notebook.append_page (*vbox, _("Search Tags"));
487         }
488
489         //add freesound search
490 #ifdef FREESOUND
491         {
492                 VBox* vbox;
493                 HBox* passbox;
494                 Label* label;
495
496                 passbox = manage(new HBox);
497                 passbox->set_border_width (12);
498                 passbox->set_spacing (6);
499
500                 label = manage (new Label);
501                 label->set_text (_("User:"));
502                 passbox->pack_start (*label, false, false);
503                 passbox->pack_start (freesound_name_entry);
504                 label = manage (new Label);
505                 label->set_text (_("Password:"));
506                 passbox->pack_start (*label, false, false);
507                 passbox->pack_start (freesound_pass_entry);
508                 label = manage (new Label);
509                 label->set_text (_("Tags:"));
510                 passbox->pack_start (*label, false, false);
511                 passbox->pack_start (freesound_entry, false, false);
512                 passbox->pack_start (freesound_search_btn, false, false);
513
514                 Gtk::ScrolledWindow *scroll = manage(new ScrolledWindow);
515                 scroll->add(freesound_list_view);
516                 scroll->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
517
518                 vbox = manage(new VBox);
519                 vbox->pack_start (*passbox, PACK_SHRINK);
520                 vbox->pack_start(*scroll);
521
522                 //vbox->pack_start (freesound_list_view);
523
524                 freesound_list_view.append_column(_("Paths"), freesound_list_columns.pathname);
525                 freesound_list_view.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &SoundFileBrowser::freesound_list_view_selected));
526
527                 //freesound_list_view.get_selection()->set_mode (SELECTION_MULTIPLE);
528                 freesound_list_view.signal_row_activated().connect (sigc::mem_fun (*this, &SoundFileBrowser::freesound_list_view_activated));
529                 freesound_search_btn.signal_clicked().connect(sigc::mem_fun(*this, &SoundFileBrowser::freesound_search_clicked));
530                 freesound_entry.signal_activate().connect(sigc::mem_fun(*this, &SoundFileBrowser::freesound_search_clicked));
531                 notebook.append_page (*vbox, _("Search Freesound"));
532         }
533 #endif
534
535
536         notebook.set_size_request (500, -1);
537
538         set_session (s);
539
540         add_button (Stock::CANCEL, RESPONSE_CANCEL);
541         add_button (Stock::APPLY, RESPONSE_APPLY);
542         add_button (Stock::OK, RESPONSE_OK);
543
544 }
545
546 SoundFileBrowser::~SoundFileBrowser ()
547 {
548         persistent_folder = chooser.get_current_folder();
549 }
550
551
552 void
553 SoundFileBrowser::on_show ()
554 {
555         ArdourDialog::on_show ();
556         start_metering ();
557 }
558
559 void
560 SoundFileBrowser::clear_selection ()
561 {
562         chooser.unselect_all ();
563         found_list_view.get_selection()->unselect_all ();
564 }
565
566 void
567 SoundFileBrowser::chooser_file_activated ()
568 {
569         preview.audition ();
570 }
571
572 void
573 SoundFileBrowser::found_list_view_activated (const TreeModel::Path&, TreeViewColumn*)
574 {
575         preview.audition ();
576 }
577
578 void
579 SoundFileBrowser::freesound_list_view_activated (const TreeModel::Path&, TreeViewColumn*)
580 {
581         preview.audition ();
582 }
583
584 void
585 SoundFileBrowser::set_session (Session* s)
586 {
587         ArdourDialog::set_session (s);
588         preview.set_session (s);
589
590         if (_session) {
591                 add_gain_meter ();
592         } else {
593                 remove_gain_meter ();
594         }
595 }
596
597 void
598 SoundFileBrowser::add_gain_meter ()
599 {
600         delete gm;
601
602         gm = new GainMeter (_session, 250);
603
604         boost::shared_ptr<Route> r = _session->the_auditioner ();
605
606         gm->set_controls (r, r->shared_peak_meter(), r->amp());
607
608         meter_packer.set_border_width (12);
609         meter_packer.pack_start (*gm, false, true);
610         hpacker.pack_end (meter_packer, false, false);
611         meter_packer.show_all ();
612         start_metering ();
613 }
614
615 void
616 SoundFileBrowser::remove_gain_meter ()
617 {
618         if (gm) {
619                 meter_packer.remove (*gm);
620                 hpacker.remove (meter_packer);
621                 delete gm;
622                 gm = 0;
623         }
624 }
625
626 void
627 SoundFileBrowser::start_metering ()
628 {
629         metering_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (sigc::mem_fun(*this, &SoundFileBrowser::meter));
630 }
631
632 void
633 SoundFileBrowser::stop_metering ()
634 {
635         metering_connection.disconnect();
636 }
637
638 void
639 SoundFileBrowser::meter ()
640 {
641         if (is_mapped () && _session && gm) {
642                 gm->update_meters ();
643         }
644 }
645
646 bool
647 SoundFileBrowser::on_audio_filter (const FileFilter::Info& filter_info)
648 {
649         return AudioFileSource::safe_audio_file_extension (filter_info.filename);
650 }
651
652 bool
653 SoundFileBrowser::on_midi_filter (const FileFilter::Info& filter_info)
654 {
655         return SMFSource::safe_midi_file_extension (filter_info.filename);
656 }
657
658 void
659 SoundFileBrowser::update_preview ()
660 {
661         if (preview.setup_labels (chooser.get_filename())) {
662                 if (preview.autoplay()) {
663                         Glib::signal_idle().connect (sigc::mem_fun (preview, &SoundFileBox::audition_oneshot));
664                 }
665         }
666 }
667
668 void
669 SoundFileBrowser::found_list_view_selected ()
670 {
671         if (!reset_options ()) {
672                 set_response_sensitive (RESPONSE_OK, false);
673         } else {
674                 ustring file;
675
676                 TreeView::Selection::ListHandle_Path rows = found_list_view.get_selection()->get_selected_rows ();
677
678                 if (!rows.empty()) {
679                         TreeIter iter = found_list->get_iter(*rows.begin());
680                         file = (*iter)[found_list_columns.pathname];
681                         chooser.set_filename (file);
682                         set_response_sensitive (RESPONSE_OK, true);
683                 } else {
684                         set_response_sensitive (RESPONSE_OK, false);
685                 }
686
687                 preview.setup_labels (file);
688         }
689 }
690
691 void
692 SoundFileBrowser::freesound_list_view_selected ()
693 {
694         if (!reset_options ()) {
695                 set_response_sensitive (RESPONSE_OK, false);
696         } else {
697                 ustring file;
698
699                 TreeView::Selection::ListHandle_Path rows = freesound_list_view.get_selection()->get_selected_rows ();
700
701                 if (!rows.empty()) {
702                         TreeIter iter = freesound_list->get_iter(*rows.begin());
703                         file = (*iter)[freesound_list_columns.pathname];
704                         chooser.set_filename (file);
705                         set_response_sensitive (RESPONSE_OK, true);
706                 } else {
707                         set_response_sensitive (RESPONSE_OK, false);
708                 }
709
710                 preview.setup_labels (file);
711         }
712 }
713
714 void
715 SoundFileBrowser::found_search_clicked ()
716 {
717         string tag_string = found_entry.get_text ();
718
719         vector<string> tags;
720
721         if (!PBD::tokenize (tag_string, string(","), std::back_inserter (tags), true)) {
722                 warning << _("SoundFileBrowser: Could not tokenize string: ") << tag_string << endmsg;
723                 return;
724         }
725
726         vector<string> results;
727         Library->search_members_and (results, tags);
728
729         found_list->clear();
730         for (vector<string>::iterator i = results.begin(); i != results.end(); ++i) {
731                 TreeModel::iterator new_row = found_list->append();
732                 TreeModel::Row row = *new_row;
733                 string path = Glib::filename_from_uri (string ("file:") + *i);
734                 row[found_list_columns.pathname] = path;
735         }
736 }
737
738 void*
739 freesound_search_thread_entry (void* arg)
740 {
741         SessionEvent::create_per_thread_pool ("freesound events", 64);
742
743         static_cast<SoundFileBrowser*>(arg)->freesound_search_thread ();
744
745         return 0;
746 }
747
748 bool searching = false;
749 bool canceling = false;
750
751 void
752 SoundFileBrowser::freesound_search_clicked ()
753 {
754         if (canceling)  //already canceling, button does nothing
755                 return;
756
757         if ( searching ) {
758                 freesound_search_btn.set_label(_("Cancelling.."));
759                 canceling = true;
760         } else {
761                 searching = true;
762                 freesound_search_btn.set_label(_("Cancel"));
763                 pthread_t freesound_thr;
764                 pthread_create_and_store ("freesound_search", &freesound_thr, freesound_search_thread_entry, this);
765         }
766 }
767
768 void
769 SoundFileBrowser::freesound_search_thread()
770 {
771 #if 0
772
773         THIS IS ALL TOTALLY THREAD-ILLEGAL ... YOU CANNOT DO GTK STUFF IN THIS THREAD
774
775 #ifdef FREESOUND
776         freesound_list->clear();
777
778         string path;
779         path = Glib::get_home_dir();
780         path += "/Freesound/";
781         Mootcher theMootcher(path.c_str());
782
783         string name_string = freesound_name_entry.get_text ();
784         string pass_string = freesound_pass_entry.get_text ();
785         string search_string = freesound_entry.get_text ();
786
787         if ( theMootcher.doLogin( name_string, pass_string ) ) {
788
789                 string theString = theMootcher.searchText(search_string);
790
791                 XMLTree doc;
792                 doc.read_buffer( theString );
793                 XMLNode *root = doc.root();
794
795                 if (root==NULL) return;
796
797                 if ( strcmp(root->name().c_str(), "freesound") == 0) {
798
799                         XMLNode *node = 0;
800                         XMLNodeList children = root->children();
801                         XMLNodeConstIterator niter;
802                         for (niter = children.begin(); niter != children.end() && !canceling; ++niter) {
803                                 node = *niter;
804                                 if( strcmp( node->name().c_str(), "sample") == 0 ){
805                                         XMLProperty *prop=node->property ("id");
806                                         string filename = theMootcher.getFile( prop->value().c_str() );
807                                         if ( filename != "" ) {
808                                                 TreeModel::iterator new_row = freesound_list->append();
809                                                 TreeModel::Row row = *new_row;
810                                                 string path = Glib::filename_from_uri (string ("file:") + filename);
811                                                 row[freesound_list_columns.pathname] = path;
812                                         }
813                                 }
814                         }
815                 }
816         }
817
818         searching = false;
819         canceling = false;
820         freesound_search_btn.set_label(_("Start Downloading"));
821 #endif
822 #endif
823
824 }
825
826 vector<ustring>
827 SoundFileBrowser::get_paths ()
828 {
829         vector<ustring> results;
830
831         int n = notebook.get_current_page ();
832
833         if (n == 0) {
834                 vector<ustring> filenames = chooser.get_filenames();
835                 vector<ustring>::iterator i;
836
837                 for (i = filenames.begin(); i != filenames.end(); ++i) {
838                         struct stat buf;
839                         if ((!stat((*i).c_str(), &buf)) && S_ISREG(buf.st_mode)) {
840                                 results.push_back (*i);
841                         }
842                 }
843
844         } else if (n==1){
845
846                 typedef TreeView::Selection::ListHandle_Path ListPath;
847
848                 ListPath rows = found_list_view.get_selection()->get_selected_rows ();
849                 for (ListPath::iterator i = rows.begin() ; i != rows.end(); ++i) {
850                         TreeIter iter = found_list->get_iter(*i);
851                         ustring str = (*iter)[found_list_columns.pathname];
852
853                         results.push_back (str);
854                 }
855         } else {
856
857                 typedef TreeView::Selection::ListHandle_Path ListPath;
858
859                 ListPath rows = freesound_list_view.get_selection()->get_selected_rows ();
860                 for (ListPath::iterator i = rows.begin() ; i != rows.end(); ++i) {
861                         TreeIter iter = freesound_list->get_iter(*i);
862                         ustring str = (*iter)[freesound_list_columns.pathname];
863
864                         results.push_back (str);
865                 }
866         }
867
868         return results;
869 }
870
871 void
872 SoundFileOmega::reset_options_noret ()
873 {
874         if (!resetting_ourselves) {
875                 (void) reset_options ();
876         }
877 }
878
879 bool
880 SoundFileOmega::reset_options ()
881 {
882         vector<ustring> paths = get_paths ();
883
884         if (paths.empty()) {
885
886                 channel_combo.set_sensitive (false);
887                 action_combo.set_sensitive (false);
888                 where_combo.set_sensitive (false);
889                 copy_files_btn.set_sensitive (false);
890
891                 return false;
892
893         } else {
894
895                 channel_combo.set_sensitive (true);
896                 action_combo.set_sensitive (true);
897                 where_combo.set_sensitive (true);
898
899                 /* if we get through this function successfully, this may be
900                    reset at the end, once we know if we can use hard links
901                    to do embedding
902                 */
903
904                 if (Config->get_only_copy_imported_files()) {
905                         copy_files_btn.set_sensitive (false);
906                 } else {
907                         copy_files_btn.set_sensitive (false);
908                 }
909         }
910
911         bool same_size;
912         bool src_needed;
913         bool selection_includes_multichannel;
914         bool selection_can_be_embedded_with_links = check_link_status (_session, paths);
915         ImportMode mode;
916
917         if (check_info (paths, same_size, src_needed, selection_includes_multichannel)) {
918                 Glib::signal_idle().connect (sigc::mem_fun (*this, &SoundFileOmega::bad_file_message));
919                 return false;
920         }
921
922         ustring existing_choice;
923         vector<string> action_strings;
924
925         if (selected_track_cnt > 0) {
926                 if (channel_combo.get_active_text().length()) {
927                         ImportDisposition id = get_channel_disposition();
928
929                         switch (id) {
930                         case Editing::ImportDistinctFiles:
931                                 if (selected_track_cnt == paths.size()) {
932                                         action_strings.push_back (importmode2string (ImportToTrack));
933                                 }
934                                 break;
935
936                         case Editing::ImportDistinctChannels:
937                                 /* XXX it would be nice to allow channel-per-selected track
938                                    but its too hard we don't want to deal with all the
939                                    different per-file + per-track channel configurations.
940                                 */
941                                 break;
942
943                         default:
944                                 action_strings.push_back (importmode2string (ImportToTrack));
945                                 break;
946                         }
947                 }
948         }
949
950         action_strings.push_back (importmode2string (ImportAsTrack));
951         action_strings.push_back (importmode2string (ImportAsRegion));
952         action_strings.push_back (importmode2string (ImportAsTapeTrack));
953
954         resetting_ourselves = true;
955
956         existing_choice = action_combo.get_active_text();
957
958         set_popdown_strings (action_combo, action_strings);
959
960         /* preserve any existing choice, if possible */
961
962
963         if (existing_choice.length()) {
964                 vector<string>::iterator x;
965                 for (x = action_strings.begin(); x != action_strings.end(); ++x) {
966                         if (*x == existing_choice) {
967                                 action_combo.set_active_text (existing_choice);
968                                 break;
969                         }
970                 }
971                 if (x == action_strings.end()) {
972                         action_combo.set_active_text (action_strings.front());
973                 }
974         } else {
975                 action_combo.set_active_text (action_strings.front());
976         }
977
978         resetting_ourselves = false;
979
980         if ((mode = get_mode()) == ImportAsRegion) {
981                 where_combo.set_sensitive (false);
982         } else {
983                 where_combo.set_sensitive (true);
984         }
985
986         vector<string> channel_strings;
987
988         if (mode == ImportAsTrack || mode == ImportAsTapeTrack || mode == ImportToTrack) {
989                 channel_strings.push_back (_("one track per file"));
990
991                 if (selection_includes_multichannel) {
992                         channel_strings.push_back (_("one track per channel"));
993                 }
994
995                 if (paths.size() > 1) {
996                         /* tape tracks are a single region per track, so we cannot
997                            sequence multiple files.
998                         */
999                         if (mode != ImportAsTapeTrack) {
1000                                 channel_strings.push_back (_("sequence files"));
1001                         }
1002                         if (same_size) {
1003                                 channel_strings.push_back (_("all files in one region"));
1004                         }
1005
1006                 }
1007
1008         } else {
1009                 channel_strings.push_back (_("one region per file"));
1010
1011                 if (selection_includes_multichannel) {
1012                         channel_strings.push_back (_("one region per channel"));
1013                 }
1014
1015                 if (paths.size() > 1) {
1016                         if (same_size) {
1017                                 channel_strings.push_back (_("all files in one region"));
1018                         }
1019                 }
1020         }
1021
1022         existing_choice = channel_combo.get_active_text();
1023
1024         set_popdown_strings (channel_combo, channel_strings);
1025
1026         /* preserve any existing choice, if possible */
1027
1028         if (existing_choice.length()) {
1029                 vector<string>::iterator x;
1030                 for (x = channel_strings.begin(); x != channel_strings.end(); ++x) {
1031                         if (*x == existing_choice) {
1032                                 channel_combo.set_active_text (existing_choice);
1033                                 break;
1034                         }
1035                 }
1036                 if (x == channel_strings.end()) {
1037                         channel_combo.set_active_text (channel_strings.front());
1038                 }
1039         } else {
1040                 channel_combo.set_active_text (channel_strings.front());
1041         }
1042
1043         if (src_needed) {
1044                 src_combo.set_sensitive (true);
1045         } else {
1046                 src_combo.set_sensitive (false);
1047         }
1048
1049         if (Config->get_only_copy_imported_files()) {
1050
1051                 if (selection_can_be_embedded_with_links) {
1052                         copy_files_btn.set_sensitive (true);
1053                 } else {
1054                         copy_files_btn.set_sensitive (false);
1055                 }
1056
1057         }  else {
1058
1059                 copy_files_btn.set_sensitive (true);
1060         }
1061
1062         return true;
1063 }
1064
1065
1066 bool
1067 SoundFileOmega::bad_file_message()
1068 {
1069         MessageDialog msg (*this,
1070                            _("One or more of the selected files\ncannot be used by Ardour"),
1071                            true,
1072                            Gtk::MESSAGE_INFO,
1073                            Gtk::BUTTONS_OK);
1074         msg.run ();
1075         resetting_ourselves = true;
1076         chooser.unselect_uri (chooser.get_preview_uri());
1077         resetting_ourselves = false;
1078
1079         return false;
1080 }
1081
1082 bool
1083 SoundFileOmega::check_info (const vector<ustring>& paths, bool& same_size, bool& src_needed, bool& multichannel)
1084 {
1085         SoundFileInfo info;
1086         nframes64_t sz = 0;
1087         bool err = false;
1088         string errmsg;
1089
1090         same_size = true;
1091         src_needed = false;
1092         multichannel = false;
1093
1094         for (vector<ustring>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
1095
1096                 if (AudioFileSource::get_soundfile_info (*i, info, errmsg)) {
1097                         if (info.channels > 1) {
1098                                 multichannel = true;
1099                         }
1100                         if (sz == 0) {
1101                                 sz = info.length;
1102                         } else {
1103                                 if (sz != info.length) {
1104                                         same_size = false;
1105                                 }
1106                         }
1107
1108                         if ((nframes_t) info.samplerate != _session->frame_rate()) {
1109                                 src_needed = true;
1110                         }
1111
1112                 } else if (SMFSource::safe_midi_file_extension (*i)) {
1113
1114                         Evoral::SMF reader;
1115                         reader.open(*i);
1116                         if (reader.num_tracks() > 1) {
1117                                 multichannel = true; // "channel" == track here...
1118                         }
1119
1120                         /* XXX we need err = true handling here in case
1121                            we can't check the file
1122                         */
1123
1124                 } else {
1125                         err = true;
1126                 }
1127         }
1128
1129         return err;
1130 }
1131
1132
1133 bool
1134 SoundFileOmega::check_link_status (const Session* s, const vector<ustring>& paths)
1135 {
1136         sys::path path = s->session_directory().sound_path() / "linktest";
1137         string tmpdir = path.to_string();
1138         bool ret = false;
1139
1140         if (mkdir (tmpdir.c_str(), 0744)) {
1141                 if (errno != EEXIST) {
1142                         return false;
1143                 }
1144         }
1145
1146         for (vector<ustring>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
1147
1148                 char tmpc[MAXPATHLEN+1];
1149
1150                 snprintf (tmpc, sizeof(tmpc), "%s/%s", tmpdir.c_str(), Glib::path_get_basename (*i).c_str());
1151
1152                 /* can we link ? */
1153
1154                 if (link ((*i).c_str(), tmpc)) {
1155                         goto out;
1156                 }
1157
1158                 unlink (tmpc);
1159         }
1160
1161         ret = true;
1162
1163   out:
1164         rmdir (tmpdir.c_str());
1165         return ret;
1166 }
1167
1168 SoundFileChooser::SoundFileChooser (Gtk::Window& parent, string title, ARDOUR::Session* s)
1169         : SoundFileBrowser (parent, title, s, false)
1170 {
1171         chooser.set_select_multiple (false);
1172         found_list_view.get_selection()->set_mode (SELECTION_SINGLE);
1173         freesound_list_view.get_selection()->set_mode (SELECTION_SINGLE);
1174 }
1175
1176 void
1177 SoundFileChooser::on_hide ()
1178 {
1179         ArdourDialog::on_hide();
1180         stop_metering ();
1181
1182         if (_session) {
1183                 _session->cancel_audition();
1184         }
1185 }
1186
1187 ustring
1188 SoundFileChooser::get_filename ()
1189 {
1190         vector<ustring> paths;
1191
1192         paths = get_paths ();
1193
1194         if (paths.empty()) {
1195                 return ustring ();
1196         }
1197
1198         if (!Glib::file_test (paths.front(), Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
1199                 return ustring();
1200         }
1201
1202         return paths.front();
1203 }
1204
1205 SoundFileOmega::SoundFileOmega (Gtk::Window& parent, string title, ARDOUR::Session* s, int selected_tracks, bool persistent,
1206                                 Editing::ImportMode mode_hint)
1207         : SoundFileBrowser (parent, title, s, persistent),
1208           copy_files_btn ( _("Copy files to session")),
1209           selected_track_cnt (selected_tracks)
1210 {
1211         VBox* vbox;
1212         HBox* hbox;
1213         vector<string> str;
1214
1215         set_size_request (-1, 450);
1216
1217         block_two.set_border_width (12);
1218         block_three.set_border_width (12);
1219         block_four.set_border_width (12);
1220
1221         options.set_spacing (12);
1222
1223         str.clear ();
1224         str.push_back (_("file timestamp"));
1225         str.push_back (_("edit point"));
1226         str.push_back (_("playhead"));
1227         str.push_back (_("session start"));
1228         set_popdown_strings (where_combo, str);
1229         where_combo.set_active_text (str.front());
1230
1231         Label* l = manage (new Label);
1232         l->set_text (_("Add files:"));
1233
1234         hbox = manage (new HBox);
1235         hbox->set_border_width (12);
1236         hbox->set_spacing (6);
1237         hbox->pack_start (*l, false, false);
1238         hbox->pack_start (action_combo, false, false);
1239         vbox = manage (new VBox);
1240         vbox->pack_start (*hbox, false, false);
1241         options.pack_start (*vbox, false, false);
1242
1243         /* dummy entry for action combo so that it doesn't look odd if we
1244            come up with no tracks selected.
1245         */
1246
1247         str.clear ();
1248         str.push_back (importmode2string (mode_hint));
1249         set_popdown_strings (action_combo, str);
1250         action_combo.set_active_text (str.front());
1251         action_combo.set_sensitive (false);
1252
1253         l = manage (new Label);
1254         l->set_text (_("Insert at:"));
1255
1256         hbox = manage (new HBox);
1257         hbox->set_border_width (12);
1258         hbox->set_spacing (6);
1259         hbox->pack_start (*l, false, false);
1260         hbox->pack_start (where_combo, false, false);
1261         vbox = manage (new VBox);
1262         vbox->pack_start (*hbox, false, false);
1263         options.pack_start (*vbox, false, false);
1264
1265
1266         l = manage (new Label);
1267         l->set_text (_("Mapping:"));
1268
1269         hbox = manage (new HBox);
1270         hbox->set_border_width (12);
1271         hbox->set_spacing (6);
1272         hbox->pack_start (*l, false, false);
1273         hbox->pack_start (channel_combo, false, false);
1274         vbox = manage (new VBox);
1275         vbox->pack_start (*hbox, false, false);
1276         options.pack_start (*vbox, false, false);
1277
1278         str.clear ();
1279         str.push_back (_("one track per file"));
1280         set_popdown_strings (channel_combo, str);
1281         channel_combo.set_active_text (str.front());
1282         channel_combo.set_sensitive (false);
1283
1284         l = manage (new Label);
1285         l->set_text (_("Conversion quality:"));
1286
1287         hbox = manage (new HBox);
1288         hbox->set_border_width (12);
1289         hbox->set_spacing (6);
1290         hbox->pack_start (*l, false, false);
1291         hbox->pack_start (src_combo, false, false);
1292         vbox = manage (new VBox);
1293         vbox->pack_start (*hbox, false, false);
1294         options.pack_start (*vbox, false, false);
1295
1296         str.clear ();
1297         str.push_back (_("Best"));
1298         str.push_back (_("Good"));
1299         str.push_back (_("Quick"));
1300         str.push_back (_("Fast"));
1301         str.push_back (_("Fastest"));
1302
1303         set_popdown_strings (src_combo, str);
1304         src_combo.set_active_text (str.front());
1305         src_combo.set_sensitive (false);
1306
1307         reset_options ();
1308
1309         action_combo.signal_changed().connect (sigc::mem_fun (*this, &SoundFileOmega::reset_options_noret));
1310
1311         copy_files_btn.set_active (true);
1312
1313         block_four.pack_start (copy_files_btn, false, false);
1314
1315         options.pack_start (block_four, false, false);
1316
1317         get_vbox()->pack_start (options, false, false);
1318
1319         /* setup disposition map */
1320
1321         disposition_map.insert (pair<ustring,ImportDisposition>(_("one track per file"), ImportDistinctFiles));
1322         disposition_map.insert (pair<ustring,ImportDisposition>(_("one track per channel"), ImportDistinctChannels));
1323         disposition_map.insert (pair<ustring,ImportDisposition>(_("merge files"), ImportMergeFiles));
1324         disposition_map.insert (pair<ustring,ImportDisposition>(_("sequence files"), ImportSerializeFiles));
1325
1326         disposition_map.insert (pair<ustring,ImportDisposition>(_("one region per file"), ImportDistinctFiles));
1327         disposition_map.insert (pair<ustring,ImportDisposition>(_("one region per channel"), ImportDistinctChannels));
1328         disposition_map.insert (pair<ustring,ImportDisposition>(_("all files in one region"), ImportMergeFiles));
1329
1330         chooser.signal_selection_changed().connect (sigc::mem_fun (*this, &SoundFileOmega::file_selection_changed));
1331
1332         /* set size requests for a couple of combos to allow them to display the longest text
1333            they will ever be asked to display.  This prevents them being resized when the user
1334            selects a file to import, which in turn prevents the size of the dialog from jumping
1335            around. */
1336
1337         vector<string> t;
1338         t.push_back (_("one track per file"));
1339         t.push_back (_("one track per channel"));
1340         t.push_back (_("sequence files"));
1341         t.push_back (_("all files in one region"));
1342         set_size_request_to_display_given_text (channel_combo, t, COMBO_FUDGE + 10, 15);
1343
1344         t.clear ();
1345         t.push_back (importmode2string (ImportAsTrack));
1346         t.push_back (importmode2string (ImportToTrack));
1347         t.push_back (importmode2string (ImportAsRegion));
1348         t.push_back (importmode2string (ImportAsTapeTrack));
1349         set_size_request_to_display_given_text (action_combo, t, COMBO_FUDGE + 10, 15);
1350 }
1351
1352 void
1353 SoundFileOmega::set_mode (ImportMode mode)
1354 {
1355         action_combo.set_active_text (importmode2string (mode));
1356 }
1357
1358 ImportMode
1359 SoundFileOmega::get_mode () const
1360 {
1361         return string2importmode (action_combo.get_active_text());
1362 }
1363
1364 void
1365 SoundFileOmega::on_hide ()
1366 {
1367         ArdourDialog::on_hide();
1368         if (_session) {
1369                 _session->cancel_audition();
1370         }
1371 }
1372
1373 ImportPosition
1374 SoundFileOmega::get_position() const
1375 {
1376         ustring str = where_combo.get_active_text();
1377
1378         if (str == _("file timestamp")) {
1379                 return ImportAtTimestamp;
1380         } else if (str == _("edit point")) {
1381                 return ImportAtEditPoint;
1382         } else if (str == _("playhead")) {
1383                 return ImportAtPlayhead;
1384         } else {
1385                 return ImportAtStart;
1386         }
1387 }
1388
1389 SrcQuality
1390 SoundFileOmega::get_src_quality() const
1391 {
1392         ustring str = where_combo.get_active_text();
1393
1394         if (str == _("Best")) {
1395                 return SrcBest;
1396         } else if (str == _("Good")) {
1397                 return SrcGood;
1398         } else if (str == _("Quick")) {
1399                 return SrcQuick;
1400         } else if (str == _("Fast")) {
1401                 return SrcFast;
1402         } else {
1403                 return SrcFastest;
1404         }
1405 }
1406
1407 ImportDisposition
1408 SoundFileOmega::get_channel_disposition () const
1409 {
1410         /* we use a map here because the channel combo can contain different strings
1411            depending on the state of the other combos. the map contains all possible strings
1412            and the ImportDisposition enum that corresponds to it.
1413         */
1414
1415         ustring str = channel_combo.get_active_text();
1416         DispositionMap::const_iterator x = disposition_map.find (str);
1417
1418         if (x == disposition_map.end()) {
1419                 fatal << string_compose (_("programming error: %1 (%2)"), "unknown string for import disposition", str) << endmsg;
1420                 /*NOTREACHED*/
1421         }
1422
1423         return x->second;
1424 }
1425
1426 void
1427 SoundFileOmega::reset (int selected_tracks)
1428 {
1429         selected_track_cnt = selected_tracks;
1430         reset_options ();
1431 }
1432
1433 void
1434 SoundFileOmega::file_selection_changed ()
1435 {
1436         if (resetting_ourselves) {
1437                 return;
1438         }
1439
1440         if (!reset_options ()) {
1441                 set_response_sensitive (RESPONSE_OK, false);
1442         } else {
1443                 if (chooser.get_filenames().size() > 0) {
1444                         set_response_sensitive (RESPONSE_OK, true);
1445                 } else {
1446                         set_response_sensitive (RESPONSE_OK, false);
1447                 }
1448         }
1449 }
1450