From e3be6836ca752b549a51ce06224bf5e2921cb17f Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Fri, 17 Dec 2021 00:36:06 +0100 Subject: [PATCH] Trigger-clip-picker audition --- gtk2_ardour/sfdb_ui.cc | 1 + gtk2_ardour/trigger_clip_picker.cc | 234 ++++++++++++++++++++++++++--- gtk2_ardour/trigger_clip_picker.h | 22 ++- gtk2_ardour/trigger_page.cc | 1 + 4 files changed, 239 insertions(+), 19 deletions(-) diff --git a/gtk2_ardour/sfdb_ui.cc b/gtk2_ardour/sfdb_ui.cc index d781a5e11e..2f30cd2c43 100644 --- a/gtk2_ardour/sfdb_ui.cc +++ b/gtk2_ardour/sfdb_ui.cc @@ -291,6 +291,7 @@ SoundFileBox::audition_active(bool active) { seek_slider.set_sensitive (active); if (!active) { seek_slider.set_value(0); + _seeking = false; } } diff --git a/gtk2_ardour/trigger_clip_picker.cc b/gtk2_ardour/trigger_clip_picker.cc index abd9428b96..5f9fb2df53 100644 --- a/gtk2_ardour/trigger_clip_picker.cc +++ b/gtk2_ardour/trigger_clip_picker.cc @@ -26,10 +26,17 @@ #include "pbd/search_path.h" #include "ardour/audiofilesource.h" +#include "ardour/audioregion.h" +#include "ardour/auditioner.h" #include "ardour/directory_names.h" #include "ardour/filesystem_paths.h" +#include "ardour/midi_region.h" +#include "ardour/region_factory.h" #include "ardour/smf_source.h" +#include "ardour/source_factory.h" +#include "ardour/srcfilesource.h" +#include "gtkmm2ext/gui_thread.h" #include "gtkmm2ext/menu_elems.h" #include "gtkmm2ext/utils.h" @@ -38,25 +45,29 @@ #include "pbd/i18n.h" using namespace Gtk; +using namespace PBD; using namespace ARDOUR; TriggerClipPicker::TriggerClipPicker () : _fcd (_("Select Sample Folder"), FILE_CHOOSER_ACTION_SELECT_FOLDER) + , _play_btn (Stock::MEDIA_PLAY) + , _stop_btn (Stock::MEDIA_STOP) + , _seek_slider (0, 1000, 1) + , _seeking (false) { - _scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC); - _scroller.add (_view); - + /* Setup Dropdown / File Browser */ Gtkmm2ext::add_volume_shortcuts (_fcd); + _fcd.add_button (Stock::CANCEL, RESPONSE_CANCEL); _fcd.add_button (Stock::OK, RESPONSE_ACCEPT); - PBD::Searchpath spath (ardour_data_search_path ()); - spath.add_subdirectory_to_paths(media_dir_name); + Searchpath spath (ardour_data_search_path ()); + spath.add_subdirectory_to_paths (media_dir_name); for (auto const& f : spath) { maybe_add_dir (f); } - maybe_add_dir (Glib::build_filename (user_config_directory(), media_dir_name)); + maybe_add_dir (Glib::build_filename (user_config_directory (), media_dir_name)); for (auto const& f : _fcd.list_shortcut_folders ()) { maybe_add_dir (f); @@ -66,9 +77,35 @@ TriggerClipPicker::TriggerClipPicker () _dir.AddMenuElem (Menu_Helpers::SeparatorElem ()); _dir.AddMenuElem (Menu_Helpers::MenuElem (_("Other..."), sigc::mem_fun (*this, &TriggerClipPicker::open_dir))); + /* Audition */ + _seek_slider.set_draw_value (false); + + _seek_slider.add_events (Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK); + _seek_slider.signal_button_press_event ().connect (sigc::mem_fun (*this, &TriggerClipPicker::seek_button_press), false); + _seek_slider.signal_button_release_event ().connect (sigc::mem_fun (*this, &TriggerClipPicker::seek_button_release), false); + + _play_btn.set_sensitive (false); + _stop_btn.set_sensitive (false); + _seek_slider.set_sensitive (false); + + _play_btn.signal_clicked ().connect (sigc::mem_fun (*this, &TriggerClipPicker::audition_selected)); + _stop_btn.signal_clicked ().connect (sigc::mem_fun (*this, &TriggerClipPicker::stop_audition)); + + /* Layout */ + + _auditable.attach (_play_btn, 0, 1, 0, 1, EXPAND | FILL, SHRINK); + _auditable.attach (_stop_btn, 1, 2, 0, 1, EXPAND | FILL, SHRINK); + _auditable.attach (_seek_slider, 0, 2, 1, 2, EXPAND | FILL, SHRINK); + _auditable.set_spacings (6); + + _scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC); + _scroller.add (_view); + pack_start (_dir, false, false); pack_start (_scroller); + pack_start (_auditable, false, false); + /* TreeView */ _model = TreeStore::create (_columns); _view.set_model (_model); _view.append_column (_("File Name"), _columns.name); @@ -85,10 +122,12 @@ TriggerClipPicker::TriggerClipPicker () _view.signal_row_activated ().connect (sigc::mem_fun (*this, &TriggerClipPicker::row_activated)); _view.signal_drag_data_get ().connect (sigc::mem_fun (*this, &TriggerClipPicker::drag_data_get)); + /* show off */ _scroller.show (); _view.show (); _dir.show (); + /* fill treeview with data */ _dir.items ().front ().activate (); } @@ -96,6 +135,23 @@ TriggerClipPicker::~TriggerClipPicker () { } +void +TriggerClipPicker::set_session (Session* s) +{ + SessionHandlePtr::set_session (s); + + if (!_session) { + _play_btn.set_sensitive (false); + _stop_btn.set_sensitive (false); + _seek_slider.set_sensitive (false); + _auditioner_connections.drop_connections (); + } else { + _auditioner_connections.drop_connections (); + _session->AuditionActive.connect (_auditioner_connections, invalidator (*this), boost::bind (&TriggerClipPicker::audition_active, this, _1), gui_context ()); + _session->the_auditioner ()->AuditionProgress.connect (_auditioner_connections, invalidator (*this), boost::bind (&TriggerClipPicker::audition_progress, this, _1, _2), gui_context ()); + } +} + void TriggerClipPicker::maybe_add_dir (std::string const& dir) { @@ -107,22 +163,24 @@ TriggerClipPicker::maybe_add_dir (std::string const& dir) void TriggerClipPicker::row_selected () { - if (_view.get_selection ()->count_selected_rows () < 1) { + if (!_session) { return; } - - TreeView::Selection::ListHandle_Path rows = _view.get_selection ()->get_selected_rows (); - for (TreeView::Selection::ListHandle_Path::iterator i = rows.begin (); i != rows.end (); ++i) { - TreeIter iter; - if ((iter = _model->get_iter (*i))) { - } + _session->cancel_audition (); + if (_view.get_selection ()->count_selected_rows () < 1) { + _play_btn.set_sensitive (false); + } else { + _play_btn.set_sensitive (true); } } void -TriggerClipPicker::row_activated (TreeModel::Path const&, TreeViewColumn*) +TriggerClipPicker::row_activated (TreeModel::Path const& p, TreeViewColumn*) { - // TODO audition + TreeModel::iterator i = _model->get_iter (p); + if (i) { + audition ((*i)[_columns.path]); + } } void @@ -159,9 +217,9 @@ TriggerClipPicker::open_dir () { Gtk::Window* tlw = dynamic_cast (get_toplevel ()); if (tlw) { - _fcd.set_transient_for (*tlw); + _fcd.set_transient_for (*tlw); } - int result = _fcd.run(); + int result = _fcd.run (); _fcd.hide (); switch (result) { case RESPONSE_ACCEPT: @@ -176,7 +234,7 @@ void TriggerClipPicker::list_dir (std::string const& dir) { std::vector fl; - PBD::find_files_matching_filter (fl, dir, audio_midi_suffix, 0, false, true, false); + find_files_matching_filter (fl, dir, audio_midi_suffix, 0, false, true, false); _dir.set_active (Glib::path_get_basename (dir)); _model->clear (); @@ -186,3 +244,143 @@ TriggerClipPicker::list_dir (std::string const& dir) row[_columns.path] = f; } } + +void +TriggerClipPicker::stop_audition () +{ + if (_session) { + _session->cancel_audition (); + } +} + +void +TriggerClipPicker::audition_active (bool active) +{ + _stop_btn.set_sensitive (active); + _seek_slider.set_sensitive (active); + + if (!active) { + _seek_slider.set_value (0); + _seeking = false; + } +} + +void +TriggerClipPicker::audition_progress (ARDOUR::samplecnt_t pos, ARDOUR::samplecnt_t len) +{ + if (!_seeking) { + _seek_slider.set_value (1000.0 * pos / len); + _seek_slider.set_sensitive (true); + } +} + +bool +TriggerClipPicker::seek_button_press (GdkEventButton*) +{ + _seeking = true; + return false; +} + +bool +TriggerClipPicker::seek_button_release (GdkEventButton*) +{ + _seeking = false; + _session->the_auditioner ()->seek_to_percent (_seek_slider.get_value () / 10.0); + _seek_slider.set_sensitive (false); + return false; +} + +void +TriggerClipPicker::audition_selected () +{ + if (_view.get_selection ()->count_selected_rows () < 1) { + return; + } + TreeView::Selection::ListHandle_Path rows = _view.get_selection ()->get_selected_rows (); + TreeIter i = _model->get_iter (*rows.begin ()); + audition ((*i)[_columns.path]); +} + +void +TriggerClipPicker::audition (std::string const& path) +{ + if (!_session) { + return; + } + _session->cancel_audition (); + + if (!Glib::file_test (path, Glib::FILE_TEST_EXISTS)) { + warning << string_compose (_("Could not read file: %1 (%2)."), path, strerror (errno)) << endmsg; + return; + } + + boost::shared_ptr r; + + if (SMFSource::valid_midi_file (path)) { + boost::shared_ptr ms = boost::dynamic_pointer_cast (SourceFactory::createExternal (DataType::MIDI, *_session, path, 0, Source::Flag (0), false)); + + std::string rname = region_name_from_path (ms->path (), false); + + PropertyList plist; + plist.add (ARDOUR::Properties::start, timepos_t (Temporal::Beats ())); + plist.add (ARDOUR::Properties::length, ms->length ()); + plist.add (ARDOUR::Properties::name, rname); + plist.add (ARDOUR::Properties::layer, 0); + + r = boost::dynamic_pointer_cast (RegionFactory::create (boost::dynamic_pointer_cast (ms), plist, false)); + assert (r); + + } else { + SourceList srclist; + boost::shared_ptr afs; + bool old_sbp = AudioSource::get_build_peakfiles (); + + /* don't even think of building peakfiles for these files */ + + SoundFileInfo info; + std::string error_msg; + if (!AudioFileSource::get_soundfile_info (path, info, error_msg)) { + error << string_compose (_("Cannot get info from audio file %1 (%2)"), path, error_msg) << endmsg; + return; + } + + AudioSource::set_build_peakfiles (false); + + for (uint16_t n = 0; n < info.channels; ++n) { + try { + afs = boost::dynamic_pointer_cast (SourceFactory::createExternal (DataType::AUDIO, *_session, path, n, Source::Flag (ARDOUR::AudioFileSource::NoPeakFile), false)); + if (afs->sample_rate () != _session->nominal_sample_rate ()) { + boost::shared_ptr sfs (new SrcFileSource (*_session, afs, ARDOUR::SrcGood)); + srclist.push_back (sfs); + } else { + srclist.push_back (afs); + } + } catch (failed_constructor& err) { + error << _("Could not access soundfile: ") << path << endmsg; + AudioSource::set_build_peakfiles (old_sbp); + return; + } + } + + AudioSource::set_build_peakfiles (old_sbp); + + if (srclist.empty ()) { + return; + } + + afs = boost::dynamic_pointer_cast (srclist[0]); + std::string rname = region_name_from_path (afs->path (), false); + + PropertyList plist; + plist.add (ARDOUR::Properties::start, timepos_t (0)); + plist.add (ARDOUR::Properties::length, srclist[0]->length ()); + plist.add (ARDOUR::Properties::name, rname); + plist.add (ARDOUR::Properties::layer, 0); + + r = boost::dynamic_pointer_cast (RegionFactory::create (srclist, plist, false)); + } + + r->set_position (timepos_t ()); + + _session->audition_region (r); +} diff --git a/gtk2_ardour/trigger_clip_picker.h b/gtk2_ardour/trigger_clip_picker.h index fb68b54fe5..e2240305f1 100644 --- a/gtk2_ardour/trigger_clip_picker.h +++ b/gtk2_ardour/trigger_clip_picker.h @@ -23,19 +23,25 @@ #include #include +#include #include +#include #include #include #include +#include "ardour/session_handle.h" + #include "widgets/ardour_dropdown.h" -class TriggerClipPicker : public Gtk::VBox +class TriggerClipPicker : public Gtk::VBox, public ARDOUR::SessionHandlePtr { public: TriggerClipPicker (); ~TriggerClipPicker (); + void set_session (ARDOUR::Session*); + private: void list_dir (std::string const&); void open_dir (); @@ -43,6 +49,13 @@ private: void row_activated (Gtk::TreeModel::Path const&, Gtk::TreeViewColumn*); void drag_data_get (Glib::RefPtr const&, Gtk::SelectionData&, guint, guint); void maybe_add_dir (std::string const&); + void audition_selected (); + void audition (std::string const&); + void audition_active (bool); + void audition_progress (ARDOUR::samplecnt_t, ARDOUR::samplecnt_t); + void stop_audition (); + bool seek_button_press (GdkEventButton*); + bool seek_button_release (GdkEventButton*); ArdourWidgets::ArdourDropdown _dir; Gtk::FileChooserDialog _fcd; @@ -61,6 +74,13 @@ private: Glib::RefPtr _model; Gtk::TreeView _view; Gtk::ScrolledWindow _scroller; + Gtk::Table _auditable; + Gtk::Button _play_btn; + Gtk::Button _stop_btn; + Gtk::HScale _seek_slider; + + bool _seeking; + PBD::ScopedConnectionList _auditioner_connections; }; #endif diff --git a/gtk2_ardour/trigger_page.cc b/gtk2_ardour/trigger_page.cc index b11f60fda3..9a88f65b1b 100644 --- a/gtk2_ardour/trigger_page.cc +++ b/gtk2_ardour/trigger_page.cc @@ -219,6 +219,7 @@ TriggerPage::set_session (Session* s) { SessionHandlePtr::set_session (s); + _trigger_clip_picker.set_session (s); if (!_session) { return; }