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