diff --git a/gtk2_ardour/sfdb_freesound_mootcher.cc b/gtk2_ardour/sfdb_freesound_mootcher.cc index e8b44ff4a7..38d53aaa70 100644 --- a/gtk2_ardour/sfdb_freesound_mootcher.cc +++ b/gtk2_ardour/sfdb_freesound_mootcher.cc @@ -54,6 +54,7 @@ #include "ardour/audio_library.h" #include "ardour/rc_configuration.h" +#include "pbd/pthread_utils.h" using namespace PBD; @@ -119,7 +120,8 @@ size_t Mootcher::WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void //------------------------------------------------------------------------ -std::string Mootcher::sortMethodString(enum sortMethod sort) { +std::string Mootcher::sortMethodString(enum sortMethod sort) +{ // given a sort type, returns the string value to be passed to the API to // sort the results in the requested way. @@ -200,6 +202,18 @@ std::string Mootcher::doRequest(std::string uri, std::string params) } +std::string Mootcher::searchSimilar(std::string id) +{ + std::string params = ""; + + params += "&fields=id,original_filename,duration,filesize,samplerate,license,serve"; + params += "&num_results=100"; + + return doRequest("/sounds/" + id + "/similar", params); +} + +//------------------------------------------------------------------------ + std::string Mootcher::searchText(std::string query, int page, std::string filter, enum sortMethod sort) { std::string params = ""; @@ -294,9 +308,62 @@ int audioFileWrite(void *buffer, size_t size, size_t nmemb, void *file) }; //------------------------------------------------------------------------ -std::string Mootcher::getAudioFile(std::string originalFileName, std::string ID, std::string audioURL, SoundFileBrowser *caller) + +void * +Mootcher::threadFunc() { +CURLcode res; + + res = curl_easy_perform (curl); + fclose (theFile); + curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 1); // turn off the progress bar + + if (res != CURLE_OK) { + /* it's not an error if the user pressed the stop button */ + if (res != CURLE_ABORTED_BY_CALLBACK) { + error << string_compose (_("curl error %1 (%2)"), res, curl_easy_strerror(res)) << endmsg; + } + remove ( (audioFileName+".part").c_str() ); + } else { + rename ( (audioFileName+".part").c_str(), audioFileName.c_str() ); + // now download the tags &c. + getSoundResourceFile(ID); + } + + return (void *) res; +} + +static int +donewithMootcher(void *arg) +{ + Mootcher *thisMootcher = (Mootcher *) arg; + + // update the sound info pane if the selection in the list box is still us + thisMootcher->sfb->refresh_display(thisMootcher->ID, thisMootcher->audioFileName); + + delete(thisMootcher); + return 0; +} + +static void * +freesound_download_thread_func(void *arg) +{ + Mootcher *thisMootcher = (Mootcher *) arg; + void *res; + + // std::cerr << "freesound_download_thread_func(" << arg << ")" << std::endl; + res = thisMootcher->threadFunc(); + g_idle_add(donewithMootcher, thisMootcher); + + return res; +} + + +//------------------------------------------------------------------------ + +bool Mootcher::checkAudioFile(std::string originalFileName, std::string theID) { ensureWorkingDir(); + ID = theID; audioFileName = Glib::build_filename (basePath, ID + "-" + originalFileName); // check to see if audio file already exists @@ -305,29 +372,31 @@ std::string Mootcher::getAudioFile(std::string originalFileName, std::string ID, fseek (testFile , 0 , SEEK_END); if (ftell (testFile) > 256) { fclose (testFile); - return audioFileName; + return true; } - // else file was small, probably an error, delete it and try again + // else file was small, probably an error, delete it fclose(testFile); remove( audioFileName.c_str() ); } + return false; +} + + +bool Mootcher::fetchAudioFile(std::string originalFileName, std::string theID, std::string audioURL, SoundFileBrowser *caller) +{ + ensureWorkingDir(); + ID = theID; + audioFileName = Glib::build_filename (basePath, ID + "-" + originalFileName); if (!curl) { - return ""; + return false; } - - // if already cancelling a previous download, bail out here ( this can happen b/c getAudioFile gets called by various UI update funcs ) - if ( caller->freesound_download_cancel ) { - return ""; - } - // now download the actual file - FILE* theFile; - theFile = g_fopen( audioFileName.c_str(), "wb" ); + theFile = g_fopen( (audioFileName + ".part").c_str(), "wb" ); if (!theFile) { - return ""; + return false; } // create the download url @@ -338,57 +407,71 @@ std::string Mootcher::getAudioFile(std::string originalFileName, std::string ID, curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, audioFileWrite); curl_easy_setopt(curl, CURLOPT_WRITEDATA, theFile); - /* hack to get rid of the barber-pole stripes */ - caller->freesound_progress_bar.hide(); - caller->freesound_progress_bar.show(); - std::string prog; prog = string_compose (_("%1"), originalFileName); - caller->freesound_progress_bar.set_text(prog); + progress_bar.set_text(prog); + + Gtk::VBox *freesound_vbox = dynamic_cast (caller->notebook.get_nth_page(2)); + freesound_vbox->pack_start(progress_hbox, Gtk::PACK_SHRINK); + progress_hbox.show(); + cancel_download = false; + sfb = caller; curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 0); // turn on the progress bar curl_easy_setopt (curl, CURLOPT_PROGRESSFUNCTION, progress_callback); - curl_easy_setopt (curl, CURLOPT_PROGRESSDATA, caller); + curl_easy_setopt (curl, CURLOPT_PROGRESSDATA, this); - CURLcode res = curl_easy_perform(curl); - fclose(theFile); + pthread_t freesound_download_thread; + pthread_create_and_store("freesound_import", &freesound_download_thread, freesound_download_thread_func, this); - curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 1); // turn off the progress bar - caller->freesound_progress_bar.set_fraction(0.0); - caller->freesound_progress_bar.set_text(""); - - if( res != 0 ) { - /* it's not an error if the user pressed the stop button */ - if (res != CURLE_ABORTED_BY_CALLBACK) { - error << string_compose (_("curl error %1 (%2)"), res, curl_easy_strerror(res)) << endmsg; - } - remove( audioFileName.c_str() ); - return ""; - } else { - // now download the tags &c. - getSoundResourceFile(ID); - } - - return audioFileName; + return true; } //--------- -int Mootcher::progress_callback(void *caller, double dltotal, double dlnow, double /*ultotal*/, double /*ulnow*/) -{ +struct progressInfo { + Gtk::ProgressBar *bar; + double dltotal; + double dlnow; +}; -SoundFileBrowser *sfb = (SoundFileBrowser *) caller; - //XXX I hope it's OK to do GTK things in this callback. Otherwise - // I'll have to do stuff like in interthread_progress_window. - if (sfb->freesound_download_cancel) { - return -1; - } - - - sfb->freesound_progress_bar.set_fraction(dlnow/dltotal); - /* Make sure the progress widget gets updated */ - while (Glib::MainContext::get_default()->iteration (false)) { - /* do nothing */ +static int +updateProgress(void *arg) +{ + struct progressInfo *progress = (struct progressInfo *) arg; + if (progress->dltotal > 0) { + double fraction = progress->dlnow / progress->dltotal; + // std::cerr << "progress idle: " << progress->bar->get_text() << ". " << progress->dlnow << " / " << progress->dltotal << " = " << fraction << std::endl; + if (fraction > 1.0) { + fraction = 1.0; + } else if (fraction < 0.0) { + fraction = 0.0; + } + progress->bar->set_fraction(fraction); } + + delete progress; + return 0; +} + +int +Mootcher::progress_callback(void *caller, double dltotal, double dlnow, double /*ultotal*/, double /*ulnow*/) +{ + // It may seem curious to pass a pointer to an instance of an object to a static + // member function, but we can't use a normal member function as a curl progress callback, + // and we want access to some private members of Mootcher. + + Mootcher *thisMootcher = (Mootcher *) caller; + + if (thisMootcher->cancel_download) { + return -1; + } + + struct progressInfo *progress = new struct progressInfo; + progress->bar = &thisMootcher->progress_bar; + progress->dltotal = dltotal; + progress->dlnow = dlnow; + + g_idle_add(updateProgress, progress); return 0; } diff --git a/gtk2_ardour/sfdb_freesound_mootcher.h b/gtk2_ardour/sfdb_freesound_mootcher.h index 8956e349eb..ee65020021 100644 --- a/gtk2_ardour/sfdb_freesound_mootcher.h +++ b/gtk2_ardour/sfdb_freesound_mootcher.h @@ -71,8 +71,14 @@ public: Mootcher(); ~Mootcher(); - std::string getAudioFile(std::string originalFileName, std::string ID, std::string audioURL, SoundFileBrowser *caller); + bool checkAudioFile(std::string originalFileName, std::string ID); + bool fetchAudioFile(std::string originalFileName, std::string ID, std::string audioURL, SoundFileBrowser *caller); std::string searchText(std::string query, int page, std::string filter, enum sortMethod sort); + std::string searchSimilar(std::string id); + void * threadFunc(); + SoundFileBrowser *sfb; + std::string audioFileName; + std::string ID; private: @@ -89,6 +95,18 @@ private: CURL *curl; char errorBuffer[CURL_ERROR_SIZE]; // storage for cUrl error message + FILE* theFile; + + Gtk::HBox progress_hbox; + Gtk::ProgressBar progress_bar; + Gtk::Button cancel_download_btn; + + bool cancel_download; + void cancelDownload() { + cancel_download = true; + progress_hbox.hide(); + } + std::string basePath; std::string xmlLocation; }; diff --git a/gtk2_ardour/sfdb_ui.cc b/gtk2_ardour/sfdb_ui.cc index 85b4f7d7ef..e8db9d4939 100644 --- a/gtk2_ardour/sfdb_ui.cc +++ b/gtk2_ardour/sfdb_ui.cc @@ -80,6 +80,7 @@ using namespace Editing; using std::string; string SoundFileBrowser::persistent_folder; +typedef TreeView::Selection::ListHandle_Path ListPath; static ImportMode string2importmode (string str) @@ -512,8 +513,6 @@ SoundFileBrowser::SoundFileBrowser (string title, ARDOUR::Session* s, bool persi found_search_btn.signal_clicked().connect(sigc::mem_fun(*this, &SoundFileBrowser::found_search_clicked)); found_entry.signal_activate().connect(sigc::mem_fun(*this, &SoundFileBrowser::found_search_clicked)); - freesound_stop_btn.signal_clicked().connect(sigc::mem_fun(*this, &SoundFileBrowser::freesound_stop_clicked)); - notebook.append_page (*vbox, _("Search Tags")); #ifdef FREESOUND @@ -555,9 +554,9 @@ SoundFileBrowser::SoundFileBrowser (string title, ARDOUR::Session* s, bool persi freesound_more_btn.set_label(_("More")); freesound_more_btn.set_sensitive(false); - passbox->pack_end (freesound_stop_btn, false, false); - freesound_stop_btn.set_label(_("Stop")); - freesound_stop_btn.set_sensitive(false); + passbox->pack_start (freesound_similar_btn, false, false); + freesound_similar_btn.set_label(_("Similar")); + freesound_similar_btn.set_sensitive(false); scroll = manage(new ScrolledWindow); scroll->add(freesound_list_view); @@ -566,9 +565,8 @@ SoundFileBrowser::SoundFileBrowser (string title, ARDOUR::Session* s, bool persi vbox = manage(new VBox); vbox->set_spacing (3); vbox->pack_start (*passbox, PACK_SHRINK); - vbox->pack_start (freesound_progress_bar, PACK_SHRINK); vbox->pack_start (*scroll); - + freesound_list_view.append_column(_("ID") , freesound_list_columns.id); freesound_list_view.append_column(_("Filename"), freesound_list_columns.filename); // freesound_list_view.append_column(_("URI") , freesound_list_columns.uri); @@ -577,20 +575,22 @@ SoundFileBrowser::SoundFileBrowser (string title, ARDOUR::Session* s, bool persi freesound_list_view.append_column(_("Samplerate"), freesound_list_columns.smplrate); freesound_list_view.append_column(_("License"), freesound_list_columns.license); freesound_list_view.get_column(0)->set_alignment(0.5); - freesound_list_view.get_column(1)->set_expand(true); + freesound_list_view.get_column(1)->set_expand(true); // filename + freesound_list_view.get_column(1)->set_resizable(true); // filename freesound_list_view.get_column(2)->set_alignment(0.5); freesound_list_view.get_column(3)->set_alignment(0.5); freesound_list_view.get_column(4)->set_alignment(0.5); freesound_list_view.get_column(5)->set_alignment(0.5); freesound_list_view.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &SoundFileBrowser::freesound_list_view_selected)); + freesound_list_view.set_tooltip_column(1); freesound_list_view.get_selection()->set_mode (SELECTION_MULTIPLE); freesound_list_view.signal_row_activated().connect (sigc::mem_fun (*this, &SoundFileBrowser::freesound_list_view_activated)); freesound_search_btn.signal_clicked().connect(sigc::mem_fun(*this, &SoundFileBrowser::freesound_search_clicked)); freesound_entry.signal_activate().connect(sigc::mem_fun(*this, &SoundFileBrowser::freesound_search_clicked)); freesound_more_btn.signal_clicked().connect(sigc::mem_fun(*this, &SoundFileBrowser::freesound_more_clicked)); - freesound_stop_btn.signal_clicked().connect(sigc::mem_fun(*this, &SoundFileBrowser::freesound_stop_clicked)); + freesound_similar_btn.signal_clicked().connect(sigc::mem_fun(*this, &SoundFileBrowser::freesound_similar_clicked)); notebook.append_page (*vbox, _("Search Freesound")); #endif @@ -787,7 +787,7 @@ SoundFileBrowser::found_list_view_selected () } else { string file; - TreeView::Selection::ListHandle_Path rows = found_list_view.get_selection()->get_selected_rows (); + ListPath rows = found_list_view.get_selection()->get_selected_rows (); if (!rows.empty()) { TreeIter iter = found_list->get_iter(*rows.begin()); @@ -802,55 +802,6 @@ SoundFileBrowser::found_list_view_selected () } } -void -SoundFileBrowser::freesound_list_view_selected () -{ - freesound_download_cancel = false; - freesound_stop_btn.set_sensitive(true); - -#ifdef FREESOUND - if (!reset_options ()) { - set_action_sensitive (false); - } else { - Mootcher mootcher; - string file; - - TreeView::Selection::ListHandle_Path rows = freesound_list_view.get_selection()->get_selected_rows (); - - if (!rows.empty()) { - TreeIter iter = freesound_list->get_iter(*rows.begin()); - - string id = (*iter)[freesound_list_columns.id]; - string uri = (*iter)[freesound_list_columns.uri]; - string ofn = (*iter)[freesound_list_columns.filename]; - - // download the sound file - GdkCursor *prev_cursor; - prev_cursor = gdk_window_get_cursor (get_window()->gobj()); - gdk_window_set_cursor (get_window()->gobj(), gdk_cursor_new(GDK_WATCH)); - gdk_flush(); - - file = mootcher.getAudioFile(ofn, id, uri, this); - - gdk_window_set_cursor (get_window()->gobj(), prev_cursor); - - if (file != "") { - chooser.set_filename (file); - set_action_sensitive (true); - } - } else { - set_action_sensitive (false); - } - - freesound_progress_bar.set_text( - string_compose(P_("found %1 match", "found %1 matches", matches), matches)); - - preview.setup_labels (file); - } -#endif - freesound_stop_btn.set_sensitive(false); -} - void SoundFileBrowser::found_search_clicked () { @@ -875,6 +826,91 @@ SoundFileBrowser::found_search_clicked () } } + +std::string +SoundFileBrowser::freesound_get_audio_file(Gtk::TreeIter iter) +{ + + Mootcher *mootcher = new Mootcher; + std::string file; + + string id = (*iter)[freesound_list_columns.id]; + string uri = (*iter)[freesound_list_columns.uri]; + string ofn = (*iter)[freesound_list_columns.filename]; + + if (mootcher->checkAudioFile(ofn, id)) { + // file already exists, no need to download it again + file = mootcher->audioFileName; + delete mootcher; + (*iter)[freesound_list_columns.started] = false; + return file; + } + if (!(*iter)[freesound_list_columns.started]) { + // start downloading the sound file + (*iter)[freesound_list_columns.started] = true; + mootcher->fetchAudioFile(ofn, id, uri, this); + } + return ""; +} + +void +SoundFileBrowser::freesound_list_view_selected () +{ + + if (!reset_options ()) { + set_action_sensitive (false); + } else { + std::string file; + ListPath rows = freesound_list_view.get_selection()->get_selected_rows (); + for (ListPath::iterator i = rows.begin() ; i != rows.end(); ++i) { + file = freesound_get_audio_file (freesound_list->get_iter(*i)); + } + + switch (rows.size()) { + case 0: + // nothing selected + freesound_similar_btn.set_sensitive(false); + set_action_sensitive (false); + break; + case 1: + // exactly one item selected + if (file != "") { + // file exists on disk already + chooser.set_filename (file); + preview.setup_labels (file); + set_action_sensitive (true); + } + freesound_similar_btn.set_sensitive(true); + break; + default: + // multiple items selected + preview.setup_labels (""); + freesound_similar_btn.set_sensitive(false); + break; + } + + } +} + +void +SoundFileBrowser::refresh_display(std::string ID, std::string file) +{ + // called when the mootcher has finished downloading a file + ListPath rows = freesound_list_view.get_selection()->get_selected_rows (); + if (rows.size() == 1) { + // there's a single item selected in the freesound list + //XXX make a function to be used to construct the actual file name both here and in the mootcher + Gtk::TreeIter row = freesound_list->get_iter(*rows.begin()); + std::string selected_ID = (*row)[freesound_list_columns.id]; + if (ID == selected_ID) { + // the selected item in the freesound list is the item that has just finished downloading + chooser.set_filename(file); + preview.setup_labels (file); + set_action_sensitive (true); + } + } +} + void SoundFileBrowser::freesound_search_clicked () { @@ -895,18 +931,32 @@ SoundFileBrowser::freesound_more_clicked () } void -SoundFileBrowser::freesound_stop_clicked () +SoundFileBrowser::freesound_similar_clicked () { - freesound_download_cancel = true; -} + ListPath rows = freesound_list_view.get_selection()->get_selected_rows (); + if (rows.size() == 1) { + Mootcher mootcher; + string id; + Gtk::TreeIter iter = freesound_list->get_iter(*rows.begin()); + id = (*iter)[freesound_list_columns.id]; + freesound_list->clear(); + GdkCursor *prev_cursor; + prev_cursor = gdk_window_get_cursor (get_window()->gobj()); + gdk_window_set_cursor (get_window()->gobj(), gdk_cursor_new(GDK_WATCH)); + gdk_flush(); + + std::string theString = mootcher.searchSimilar(id); + + gdk_window_set_cursor (get_window()->gobj(), prev_cursor); + handle_freesound_results(theString); + } +} void SoundFileBrowser::freesound_search() { -#ifdef FREESOUND Mootcher mootcher; - freesound_list_view.get_column(1)->set_sizing(TREE_VIEW_COLUMN_GROW_ONLY); string search_string = freesound_entry.get_text (); enum sortMethod sort_method = (enum sortMethod) freesound_sort.get_active_row_number(); @@ -914,7 +964,6 @@ SoundFileBrowser::freesound_search() GdkCursor *prev_cursor; prev_cursor = gdk_window_get_cursor (get_window()->gobj()); gdk_window_set_cursor (get_window()->gobj(), gdk_cursor_new(GDK_WATCH)); - freesound_progress_bar.set_fraction(0.0); gdk_flush(); std::string theString = mootcher.searchText( @@ -929,7 +978,11 @@ SoundFileBrowser::freesound_search() ); gdk_window_set_cursor (get_window()->gobj(), prev_cursor); + handle_freesound_results(theString); +} +void +SoundFileBrowser::handle_freesound_results(std::string theString) { XMLTree doc; doc.read_buffer( theString ); XMLNode *root = doc.root(); @@ -1065,14 +1118,6 @@ SoundFileBrowser::freesound_search() matches++; } } - - if (matches == 0) { - freesound_progress_bar.set_text(_("Search returned no results.")); - } else { - freesound_progress_bar.set_text(string_compose(P_("Found %1 match", "Found %1 matches", matches), matches)); - } - freesound_list_view.get_column(1)->set_sizing(TREE_VIEW_COLUMN_AUTOSIZE); -#endif } vector @@ -1093,9 +1138,7 @@ SoundFileBrowser::get_paths () } } - } else if (n==1){ - - typedef TreeView::Selection::ListHandle_Path ListPath; + } else if (n == 1) { ListPath rows = found_list_view.get_selection()->get_selected_rows (); for (ListPath::iterator i = rows.begin() ; i != rows.end(); ++i) { @@ -1106,28 +1149,12 @@ SoundFileBrowser::get_paths () } } else { #ifdef FREESOUND - typedef TreeView::Selection::ListHandle_Path ListPath; - Mootcher mootcher; - ListPath rows = freesound_list_view.get_selection()->get_selected_rows (); for (ListPath::iterator i = rows.begin() ; i != rows.end(); ++i) { - TreeIter iter = freesound_list->get_iter(*i); - string id = (*iter)[freesound_list_columns.id]; - string uri = (*iter)[freesound_list_columns.uri]; - string ofn = (*iter)[freesound_list_columns.filename]; - - GdkCursor *prev_cursor; - prev_cursor = gdk_window_get_cursor (get_window()->gobj()); - gdk_window_set_cursor (get_window()->gobj(), gdk_cursor_new(GDK_WATCH)); - gdk_flush(); - - string str = mootcher.getAudioFile(ofn, id, uri, this); + string str = freesound_get_audio_file (freesound_list->get_iter(*i)); if (str != "") { results.push_back (str); } - - gdk_window_set_cursor (get_window()->gobj(), prev_cursor); - } #endif } diff --git a/gtk2_ardour/sfdb_ui.h b/gtk2_ardour/sfdb_ui.h index bed800ad73..5d8decddf4 100644 --- a/gtk2_ardour/sfdb_ui.h +++ b/gtk2_ardour/sfdb_ui.h @@ -131,6 +131,7 @@ class SoundFileBrowser : public ArdourWindow Gtk::TreeModelColumn filesize; Gtk::TreeModelColumn smplrate; Gtk::TreeModelColumn license; + Gtk::TreeModelColumn started; FreesoundColumns() { add(id); @@ -140,6 +141,7 @@ class SoundFileBrowser : public ArdourWindow add(filesize); add(smplrate); add(license); + add(started); } }; @@ -150,8 +152,9 @@ class SoundFileBrowser : public ArdourWindow Glib::RefPtr freesound_list; Gtk::Button freesound_more_btn; - Gtk::Button freesound_stop_btn; + Gtk::Button freesound_similar_btn; + void handle_freesound_results(std::string theString); public: SoundFileBrowser (std::string title, ARDOUR::Session* _s, bool persistent); virtual ~SoundFileBrowser (); @@ -177,11 +180,10 @@ class SoundFileBrowser : public ArdourWindow Gtk::Button freesound_search_btn; Gtk::TreeView freesound_list_view; - Gtk::ProgressBar freesound_progress_bar; - - bool freesound_download_cancel; + Gtk::Notebook notebook; void freesound_search(); + void refresh_display(std::string ID, std::string file); protected: bool resetting_ourselves; @@ -203,7 +205,6 @@ class SoundFileBrowser : public ArdourWindow static std::string persistent_folder; - Gtk::Notebook notebook; GainMeter* gm; Gtk::VBox meter_packer; @@ -224,10 +225,11 @@ class SoundFileBrowser : public ArdourWindow void freesound_list_view_activated (const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn*); void freesound_search_clicked (); void freesound_more_clicked (); - void freesound_stop_clicked (); + void freesound_similar_clicked (); int freesound_page; void chooser_file_activated (); + std::string freesound_get_audio_file(Gtk::TreeIter iter); bool on_audio_filter (const Gtk::FileFilter::Info& filter_info); bool on_midi_filter (const Gtk::FileFilter::Info& filter_info);