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