2021-12-12 08:25:49 -05:00
/*
* Copyright ( C ) 2019 - 2021 Robin Gareus < robin @ gareus . org >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License along
* with this program ; if not , write to the Free Software Foundation , Inc . ,
* 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA .
*/
2021-12-15 15:07:59 -05:00
# include <cassert>
# include <vector>
# include <gtkmm/stock.h>
# include "pbd/basename.h"
# include "pbd/file_utils.h"
2022-01-30 16:42:13 -05:00
# include "pbd/openuri.h"
2021-12-19 20:39:15 -05:00
# include "pbd/pathexpand.h"
2021-12-15 15:07:59 -05:00
# include "pbd/search_path.h"
2022-01-29 10:21:49 -05:00
# include "pbd/unwind.h"
2021-12-15 15:07:59 -05:00
# include "ardour/audiofilesource.h"
2021-12-16 18:36:06 -05:00
# include "ardour/audioregion.h"
# include "ardour/auditioner.h"
2022-01-29 10:21:49 -05:00
# include "ardour/clip_library.h"
2021-12-15 15:07:59 -05:00
# include "ardour/directory_names.h"
# include "ardour/filesystem_paths.h"
2021-12-16 18:36:06 -05:00
# include "ardour/midi_region.h"
2022-02-02 16:09:47 -05:00
# include "ardour/plugin_insert.h"
2021-12-16 18:36:06 -05:00
# include "ardour/region_factory.h"
2021-12-15 15:07:59 -05:00
# include "ardour/smf_source.h"
2021-12-16 18:36:06 -05:00
# include "ardour/source_factory.h"
# include "ardour/srcfilesource.h"
2021-12-15 15:07:59 -05:00
2021-12-16 18:36:06 -05:00
# include "gtkmm2ext/gui_thread.h"
2021-12-15 15:07:59 -05:00
# include "gtkmm2ext/menu_elems.h"
2021-12-12 08:25:49 -05:00
# include "gtkmm2ext/utils.h"
2021-12-19 20:39:15 -05:00
# include "widgets/paths_dialog.h"
2022-01-30 16:42:13 -05:00
# include "widgets/tooltips.h"
# include "widgets/ardour_icon.h"
2021-12-19 20:39:15 -05:00
2022-02-04 07:57:33 -05:00
# include "ardour_ui.h"
2022-02-02 16:09:47 -05:00
# include "plugin_ui.h"
2022-02-04 07:57:33 -05:00
# include "timers.h"
2021-12-12 08:25:49 -05:00
# include "trigger_clip_picker.h"
2022-01-10 18:43:39 -05:00
# include "ui_config.h"
2021-12-12 08:25:49 -05:00
2021-12-15 15:07:59 -05:00
# include "pbd/i18n.h"
using namespace Gtk ;
2021-12-16 18:36:06 -05:00
using namespace PBD ;
2021-12-15 15:07:59 -05:00
using namespace ARDOUR ;
2023-03-28 19:48:25 -04:00
# define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale()))
2021-12-12 08:25:49 -05:00
TriggerClipPicker : : TriggerClipPicker ( )
2022-07-13 18:06:48 -04:00
: _fcd ( _ ( " Select clip folder " ) , FILE_CHOOSER_ACTION_SELECT_FOLDER )
2021-12-16 18:36:06 -05:00
, _seek_slider ( 0 , 1000 , 1 )
2022-01-10 18:43:39 -05:00
, _autoplay_btn ( _ ( " Auto-play " ) )
2022-02-04 21:37:23 -05:00
, _auditioner_combo ( InstrumentSelector : : ForAuditioner )
2022-01-29 10:21:49 -05:00
, _clip_library_listed ( false )
, _ignore_list_dir ( false )
2021-12-16 18:36:06 -05:00
, _seeking ( false )
2022-02-02 16:09:47 -05:00
, _audition_plugnui ( 0 )
2021-12-12 08:25:49 -05:00
{
2021-12-16 18:36:06 -05:00
/* Setup Dropdown / File Browser */
2021-12-30 15:23:05 -05:00
# ifdef __APPLE__
try {
/* add_shortcut_folder throws an exception if the folder being added already has a shortcut */
_fcd . add_shortcut_folder_uri ( " file:///Library/GarageBand/Apple Loops " ) ;
_fcd . add_shortcut_folder_uri ( " file:///Library/Audio/Apple Loops " ) ;
_fcd . add_shortcut_folder_uri ( " file:///Library/Application Support/GarageBand/Instrument Library/Sampler/Sampler Files " ) ;
}
catch ( Glib : : Error & e ) { }
# endif
2021-12-15 15:07:59 -05:00
Gtkmm2ext : : add_volume_shortcuts ( _fcd ) ;
2021-12-16 18:36:06 -05:00
2021-12-15 15:07:59 -05:00
_fcd . add_button ( Stock : : CANCEL , RESPONSE_CANCEL ) ;
2021-12-19 20:39:15 -05:00
_fcd . add_button ( Stock : : ADD , RESPONSE_ACCEPT ) ;
_fcd . add_button ( Stock : : OPEN , RESPONSE_OK ) ;
2021-12-15 15:07:59 -05:00
2021-12-19 20:39:15 -05:00
refill_dropdown ( ) ;
2022-10-12 22:01:09 -04:00
Glib : : signal_idle ( ) . connect ( sigc : : mem_fun ( * this , & TriggerClipPicker : : refill_dropdown ) ) ;
2021-12-15 15:07:59 -05:00
2021-12-16 18:36:06 -05:00
/* Audition */
2022-01-10 18:43:39 -05:00
_autoplay_btn . set_active ( UIConfiguration : : instance ( ) . get_autoplay_clips ( ) ) ;
2021-12-16 18:36:06 -05:00
_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 ) ;
2022-01-11 13:26:14 -05:00
_play_btn . set_name ( " generic button " ) ;
_play_btn . set_icon ( ArdourWidgets : : ArdourIcon : : TransportPlay ) ;
_play_btn . signal_clicked . connect ( sigc : : mem_fun ( * this , & TriggerClipPicker : : audition_selected ) ) ;
2021-12-16 18:36:06 -05:00
2022-01-11 13:26:14 -05:00
_stop_btn . set_name ( " generic button " ) ;
_stop_btn . set_icon ( ArdourWidgets : : ArdourIcon : : TransportStop ) ;
_stop_btn . signal_clicked . connect ( sigc : : mem_fun ( * this , & TriggerClipPicker : : stop_audition ) ) ;
2023-03-28 19:48:25 -04:00
_gain_control . set_name ( " monitor section knob " ) ;
_gain_control . set_size_request ( PX_SCALE ( 24 ) , PX_SCALE ( 24 ) ) ;
2022-01-30 16:42:13 -05:00
_open_library_btn . set_name ( " generic button " ) ;
_open_library_btn . set_icon ( ArdourWidgets : : ArdourIcon : : Folder ) ;
_open_library_btn . signal_clicked . connect ( sigc : : mem_fun ( * this , & TriggerClipPicker : : open_library ) ) ;
_open_library_btn . set_no_show_all ( ) ;
2022-09-16 15:07:39 -04:00
_refresh_btn . set_name ( " generic button " ) ;
_refresh_btn . set_icon ( ArdourWidgets : : ArdourIcon : : TransportLoop ) ;
_refresh_btn . signal_clicked . connect ( sigc : : mem_fun ( * this , & TriggerClipPicker : : refresh_library ) ) ;
2022-02-02 16:09:47 -05:00
_show_plugin_btn . set_name ( " generic button " ) ;
_show_plugin_btn . set_icon ( ArdourWidgets : : ArdourIcon : : PsetBrowse ) ;
_show_plugin_btn . signal_clicked . connect ( sigc : : mem_fun ( * this , & TriggerClipPicker : : audition_show_plugin_ui ) ) ;
_show_plugin_btn . set_sensitive ( false ) ;
2022-01-12 18:08:30 -05:00
_play_btn . set_sensitive ( false ) ;
_stop_btn . set_sensitive ( false ) ;
2022-01-11 13:26:14 -05:00
_autoplay_btn . set_can_focus ( false ) ;
2022-01-10 18:43:39 -05:00
_autoplay_btn . signal_toggled ( ) . connect ( sigc : : mem_fun ( * this , & TriggerClipPicker : : autoplay_toggled ) ) ;
2021-12-16 18:36:06 -05:00
2022-02-04 21:37:23 -05:00
auditioner_combo_changed ( ) ;
_auditioner_combo . signal_changed ( ) . connect ( sigc : : mem_fun ( * this , & TriggerClipPicker : : auditioner_combo_changed ) ) ;
2022-01-30 16:42:13 -05:00
ArdourWidgets : : set_tooltip ( _play_btn , _ ( " Audition selected clip " ) ) ;
ArdourWidgets : : set_tooltip ( _stop_btn , _ ( " Stop the audition " ) ) ;
2023-03-28 19:48:25 -04:00
ArdourWidgets : : set_tooltip ( _gain_control , _ ( " Audition Volume " ) ) ;
2022-01-30 16:42:13 -05:00
ArdourWidgets : : set_tooltip ( _open_library_btn , _ ( " Open clip library folder " ) ) ;
2022-09-16 15:07:39 -04:00
ArdourWidgets : : set_tooltip ( _refresh_btn , _ ( " Refresh clip list " ) ) ;
2022-02-19 13:12:43 -05:00
ArdourWidgets : : set_tooltip ( _auditioner_combo , _ ( " Select the Synth used for auditioning " ) ) ;
ArdourWidgets : : set_tooltip ( _show_plugin_btn , _ ( " Show the GUI for the Auditioner Synth " ) ) ;
ArdourWidgets : : set_tooltip ( _clip_dir_menu , _ ( " Click to select a clip folder and edit your available clip folders " ) ) ;
2022-01-30 16:42:13 -05:00
2022-01-26 16:19:32 -05:00
format_text . set_alignment ( Gtk : : ALIGN_START , Gtk : : ALIGN_CENTER ) ;
channels_value . set_alignment ( Gtk : : ALIGN_START , Gtk : : ALIGN_CENTER ) ;
2022-02-07 20:33:28 -05:00
_midi_prop_table . attach ( format_text , 0 , 1 , 0 , 1 , EXPAND | FILL , SHRINK ) ;
_midi_prop_table . attach ( channels_value , 0 , 1 , 1 , 2 , EXPAND | FILL , SHRINK ) ;
_midi_prop_table . attach ( _auditioner_combo , 0 , 3 , 2 , 3 , EXPAND | FILL , SHRINK ) ;
_midi_prop_table . attach ( _show_plugin_btn , 3 , 4 , 2 , 3 , SHRINK , SHRINK ) ;
_midi_prop_table . set_border_width ( 4 ) ;
_midi_prop_table . set_spacings ( 4 ) ;
2021-12-16 18:36:06 -05:00
/* Layout */
2022-02-07 20:33:28 -05:00
int r = 0 ;
2022-01-11 13:26:14 -05:00
_auditable . set_homogeneous ( false ) ;
2022-02-07 20:33:28 -05:00
_auditable . attach ( _play_btn , 0 , 1 , r , r + 1 , SHRINK , SHRINK ) ;
_auditable . attach ( _stop_btn , 1 , 2 , r , r + 1 , SHRINK , SHRINK ) ;
2023-03-28 19:48:25 -04:00
_auditable . attach ( _gain_control , 2 , 3 , r , r + 1 , SHRINK , SHRINK ) ;
_auditable . attach ( _autoplay_btn , 3 , 4 , r , r + 1 , EXPAND | FILL , SHRINK ) ; r + + ;
_auditable . attach ( _seek_slider , 0 , 5 , r , r + 1 , EXPAND | FILL , SHRINK ) ; r + + ;
_auditable . attach ( _midi_prop_table , 0 , 5 , r , r + 1 , EXPAND | FILL , SHRINK ) ;
2022-01-11 13:26:14 -05:00
_auditable . set_border_width ( 4 ) ;
_auditable . set_spacings ( 4 ) ;
2021-12-16 18:36:06 -05:00
2021-12-16 21:57:04 -05:00
_scroller . set_policy ( POLICY_AUTOMATIC , POLICY_AUTOMATIC ) ;
2021-12-16 18:36:06 -05:00
_scroller . add ( _view ) ;
2022-02-07 10:59:22 -05:00
Gtk : : Table * dir_table = manage ( new Gtk : : Table ( ) ) ;
2022-02-07 20:33:28 -05:00
dir_table - > set_border_width ( 4 ) ;
dir_table - > set_spacings ( 4 ) ;
2022-02-07 10:59:22 -05:00
dir_table - > attach ( _clip_dir_menu , 0 , 1 , 0 , 1 , EXPAND | FILL , SHRINK ) ;
dir_table - > attach ( _open_library_btn , 1 , 2 , 0 , 1 , SHRINK , SHRINK ) ;
2022-09-16 15:07:39 -04:00
dir_table - > attach ( _refresh_btn , 2 , 3 , 0 , 1 , SHRINK , SHRINK ) ;
2022-02-07 10:59:22 -05:00
2022-02-07 20:33:28 -05:00
pack_start ( * dir_table , false , false ) ;
2021-12-15 15:07:59 -05:00
pack_start ( _scroller ) ;
2021-12-16 18:36:06 -05:00
pack_start ( _auditable , false , false ) ;
2021-12-15 15:07:59 -05:00
2021-12-16 18:36:06 -05:00
/* TreeView */
2021-12-15 15:07:59 -05:00
_model = TreeStore : : create ( _columns ) ;
_view . set_model ( _model ) ;
_view . append_column ( _ ( " File Name " ) , _columns . name ) ;
2022-01-11 13:26:14 -05:00
_view . set_headers_visible ( false ) ; //TODO: show headers when we have size/tags/etc
2021-12-15 15:07:59 -05:00
_view . set_reorderable ( false ) ;
_view . get_selection ( ) - > set_mode ( SELECTION_MULTIPLE ) ;
2022-10-12 22:01:09 -04:00
_view . signal_realize ( ) . connect ( mem_fun ( this , & TriggerClipPicker : : on_theme_changed ) ) ;
_view . ensure_style ( ) ;
on_theme_changed ( ) ;
2021-12-15 15:07:59 -05:00
2022-10-02 19:02:00 -04:00
Gtk : : TreeViewColumn * name_col = _view . get_column ( 0 ) ;
Gtk : : CellRendererText * renderer = dynamic_cast < Gtk : : CellRendererText * > ( _view . get_column_cell_renderer ( 0 ) ) ;
name_col - > add_attribute ( renderer - > property_foreground_gdk ( ) , _columns . color ) ;
2022-01-28 20:57:25 -05:00
/* DnD source */
2021-12-15 15:07:59 -05:00
std : : vector < TargetEntry > dnd ;
dnd . push_back ( TargetEntry ( " text/uri-list " ) ) ;
2022-01-28 20:57:25 -05:00
_view . drag_source_set ( dnd , Gdk : : MODIFIER_MASK , Gdk : : ACTION_COPY ) ;
2021-12-15 15:07:59 -05:00
_view . get_selection ( ) - > signal_changed ( ) . connect ( sigc : : mem_fun ( * this , & TriggerClipPicker : : row_selected ) ) ;
_view . signal_row_activated ( ) . connect ( sigc : : mem_fun ( * this , & TriggerClipPicker : : row_activated ) ) ;
2021-12-17 17:40:12 -05:00
_view . signal_test_expand_row ( ) . connect ( sigc : : mem_fun ( * this , & TriggerClipPicker : : test_expand ) ) ;
_view . signal_row_collapsed ( ) . connect ( sigc : : mem_fun ( * this , & TriggerClipPicker : : row_collapsed ) ) ;
2021-12-15 15:07:59 -05:00
_view . signal_drag_data_get ( ) . connect ( sigc : : mem_fun ( * this , & TriggerClipPicker : : drag_data_get ) ) ;
2022-01-10 18:43:39 -05:00
_view . signal_cursor_changed ( ) . connect ( sigc : : mem_fun ( * this , & TriggerClipPicker : : cursor_changed ) ) ;
2022-01-17 20:47:49 -05:00
_view . signal_drag_end ( ) . connect ( sigc : : mem_fun ( * this , & TriggerClipPicker : : drag_end ) ) ;
2021-12-15 15:07:59 -05:00
2022-01-28 20:57:25 -05:00
/* DnD target */
std : : vector < Gtk : : TargetEntry > target_table ;
target_table . push_back ( Gtk : : TargetEntry ( " x-ardour/region.pbdid " , Gtk : : TARGET_SAME_APP ) ) ;
2022-02-10 12:08:46 -05:00
target_table . push_back ( TargetEntry ( " text/uri-list " ) ) ;
2022-01-28 20:57:25 -05:00
_view . drag_dest_set ( target_table , DEST_DEFAULT_ALL , Gdk : : ACTION_COPY ) ;
_view . signal_drag_begin ( ) . connect ( sigc : : mem_fun ( * this , & TriggerClipPicker : : drag_begin ) ) ;
_view . signal_drag_motion ( ) . connect ( sigc : : mem_fun ( * this , & TriggerClipPicker : : drag_motion ) ) ;
_view . signal_drag_data_received ( ) . connect ( sigc : : mem_fun ( * this , & TriggerClipPicker : : drag_data_received ) ) ;
2022-10-02 19:02:00 -04:00
UIConfiguration : : instance ( ) . ColorsChanged . connect ( sigc : : mem_fun ( * this , & TriggerClipPicker : : on_theme_changed ) ) ;
UIConfiguration : : instance ( ) . ParameterChanged . connect ( sigc : : mem_fun ( * this , & TriggerClipPicker : : parameter_changed ) ) ;
2024-10-18 19:51:44 -04:00
Config - > ParameterChanged . connect ( _config_connection , invalidator ( * this ) , std : : bind ( & TriggerClipPicker : : parameter_changed , this , _1 ) , gui_context ( ) ) ;
LibraryClipAdded . connect ( _clip_added_connection , invalidator ( * this ) , std : : bind ( & TriggerClipPicker : : clip_added , this , _1 , _2 ) , gui_context ( ) ) ;
2022-01-29 10:21:49 -05:00
/* cache value */
_clip_library_dir = clip_library_dir ( ) ;
2021-12-19 20:39:15 -05:00
2021-12-16 18:36:06 -05:00
/* show off */
2021-12-15 15:07:59 -05:00
_scroller . show ( ) ;
_view . show ( ) ;
2022-02-07 10:59:22 -05:00
_clip_dir_menu . show ( ) ;
2022-02-07 20:33:28 -05:00
_auditable . show ( ) ;
2021-12-15 15:07:59 -05:00
2021-12-16 18:36:06 -05:00
/* fill treeview with data */
2022-02-07 10:59:22 -05:00
_clip_dir_menu . items ( ) . front ( ) . activate ( ) ;
2021-12-12 08:25:49 -05:00
}
TriggerClipPicker : : ~ TriggerClipPicker ( )
{
2022-02-04 07:57:33 -05:00
_idle_connection . disconnect ( ) ;
2021-12-12 08:25:49 -05:00
}
2021-12-15 15:07:59 -05:00
2022-02-04 21:37:23 -05:00
void
TriggerClipPicker : : auditioner_combo_changed ( )
{
if ( _session ) {
_session - > the_auditioner ( ) - > set_audition_synth_info ( _auditioner_combo . selected_instrument ( ) ) ;
}
}
2021-12-16 18:36:06 -05:00
void
2021-12-19 20:39:15 -05:00
TriggerClipPicker : : parameter_changed ( std : : string const & p )
2021-12-16 18:36:06 -05:00
{
2021-12-19 20:39:15 -05:00
if ( p = = " sample-lib-path " ) {
refill_dropdown ( ) ;
2022-01-29 10:21:49 -05:00
} else if ( p = = " clip-library-dir " ) {
_clip_library_dir = clip_library_dir ( ) ;
refill_dropdown ( ) ;
2022-10-02 19:02:00 -04:00
} else if ( p = = " highlight-auditioned-clips " ) {
reset_audition_marks ( true ) ;
2022-01-29 10:21:49 -05:00
}
}
void
TriggerClipPicker : : clip_added ( std : : string const & , void * src )
{
if ( ! _clip_library_listed ) {
_clip_library_dir = clip_library_dir ( ) ;
refill_dropdown ( ) ;
}
if ( src = = this ) {
list_dir ( clip_library_dir ( ) ) ;
} else {
list_dir ( _current_path ) ;
2021-12-19 20:39:15 -05:00
}
}
2021-12-16 18:36:06 -05:00
2021-12-19 20:39:15 -05:00
/* ****************************************************************************
* Paths Dropdown Callbacks
*/
void
TriggerClipPicker : : edit_path ( )
{
Gtk : : Window * tlw = dynamic_cast < Gtk : : Window * > ( get_toplevel ( ) ) ;
assert ( tlw ) ;
ArdourWidgets : : PathsDialog pd ( * tlw , _ ( " Edit Sample Library Path " ) , Config - > get_sample_lib_path ( ) , " " ) ;
if ( pd . run ( ) ! = Gtk : : RESPONSE_ACCEPT ) {
return ;
}
Config - > set_sample_lib_path ( pd . get_serialized_paths ( ) ) ;
}
2022-10-12 22:01:09 -04:00
bool
2021-12-19 20:39:15 -05:00
TriggerClipPicker : : refill_dropdown ( )
{
2022-02-07 10:59:22 -05:00
_clip_dir_menu . clear_items ( ) ;
2021-12-30 15:22:41 -05:00
_root_paths . clear ( ) ;
2021-12-19 20:39:15 -05:00
/* Bundled Content */
Searchpath spath ( ardour_data_search_path ( ) ) ;
spath . add_subdirectory_to_paths ( media_dir_name ) ;
for ( auto const & f : spath ) {
maybe_add_dir ( f ) ;
}
/* User config folder */
maybe_add_dir ( Glib : : build_filename ( user_config_directory ( ) , media_dir_name ) ) ;
2022-10-20 14:48:33 -04:00
/* Freesuund dir, in case they use it */
maybe_add_dir ( UIConfiguration : : instance ( ) . get_freesound_dir ( ) ) ;
2021-12-19 20:39:15 -05:00
/* Anything added by Gtkmm2ext::add_volume_shortcuts */
for ( auto const & f : _fcd . list_shortcut_folders ( ) ) {
maybe_add_dir ( f ) ;
2021-12-16 18:36:06 -05:00
}
2021-12-19 20:39:15 -05:00
/* Custom Paths */
2022-02-07 10:59:22 -05:00
assert ( _clip_dir_menu . items ( ) . size ( ) > 0 ) ;
2021-12-19 20:39:15 -05:00
if ( ! Config - > get_sample_lib_path ( ) . empty ( ) ) {
2022-02-07 10:59:22 -05:00
_clip_dir_menu . AddMenuElem ( Menu_Helpers : : SeparatorElem ( ) ) ;
2021-12-19 20:39:15 -05:00
Searchpath cpath ( Config - > get_sample_lib_path ( ) ) ;
for ( auto const & f : cpath ) {
maybe_add_dir ( f ) ;
}
}
2022-01-29 10:21:49 -05:00
_clip_library_listed = maybe_add_dir ( clip_library_dir ( false ) ) ;
2022-02-07 10:59:22 -05:00
_clip_dir_menu . AddMenuElem ( Menu_Helpers : : SeparatorElem ( ) ) ;
_clip_dir_menu . AddMenuElem ( Menu_Helpers : : MenuElem ( _ ( " Edit... " ) , sigc : : mem_fun ( * this , & TriggerClipPicker : : edit_path ) ) ) ;
_clip_dir_menu . AddMenuElem ( Menu_Helpers : : MenuElem ( _ ( " Other... " ) , sigc : : mem_fun ( * this , & TriggerClipPicker : : open_dir ) ) ) ;
2022-09-26 13:15:44 -04:00
_clip_dir_menu . AddMenuElem ( Menu_Helpers : : MenuElem ( _ ( " Download... " ) , sigc : : mem_fun ( * this , & TriggerClipPicker : : open_downloader ) ) ) ;
2022-10-12 22:01:09 -04:00
return false ;
2021-12-16 18:36:06 -05:00
}
2021-12-30 15:22:41 -05:00
static bool
is_subfolder ( std : : string const & parent , std : : string dir )
{
assert ( Glib : : file_test ( dir , Glib : : FILE_TEST_IS_DIR | Glib : : FILE_TEST_EXISTS ) ) ;
assert ( Glib : : file_test ( parent , Glib : : FILE_TEST_IS_DIR | Glib : : FILE_TEST_EXISTS ) ) ;
if ( parent . size ( ) > dir . size ( ) ) {
return false ;
}
if ( parent = = dir ) {
return false ;
}
if ( dir = = Glib : : path_get_dirname ( dir ) ) {
/* dir must be root */
return false ;
}
while ( parent . size ( ) < dir . size ( ) ) {
/* step up, compare with parent */
dir = Glib : : path_get_dirname ( dir ) ;
if ( parent = = dir ) {
return true ;
}
}
return false ;
}
2022-01-12 18:06:33 -05:00
static std : : string
display_name ( std : : string const & dir ) {
std : : string metadata = Glib : : build_filename ( dir , " .daw-meta.xml " ) ;
if ( Glib : : file_test ( metadata , Glib : : FILE_TEST_IS_REGULAR | Glib : : FILE_TEST_EXISTS ) ) {
XMLTree tree ;
if ( tree . read ( metadata ) & & tree . root ( ) - > name ( ) = = " DAWDirectory " ) {
XMLNode * root = tree . root ( ) ;
std : : string type ;
if ( root - > get_property ( " type " , type ) ) {
if ( type = = " bundled " ) {
return string_compose ( _ ( " %1 Bundled Content " ) , PROGRAM_NAME ) ;
}
}
# if ENABLE_NLS
if ( ARDOUR : : translations_are_enabled ( ) ) {
for ( XMLNodeList : : const_iterator n = root - > children ( " title " ) . begin ( ) ; n ! = root - > children ( " title " ) . end ( ) ; + + n ) {
std : : string lang ;
if ( ! ( * n ) - > get_property ( " lang " , lang ) ) {
continue ;
}
if ( lang ! = " en_US " ) { // TODO: get current lang
continue ;
}
return ( * n ) - > child_content ( ) ;
}
}
# endif
/* pick first title, if any */
XMLNode * child = root - > child ( " title " ) ;
if ( child ) {
return child - > child_content ( ) ;
}
}
}
return Glib : : path_get_basename ( dir ) ;
}
2022-01-29 10:21:49 -05:00
bool
2021-12-15 15:07:59 -05:00
TriggerClipPicker : : maybe_add_dir ( std : : string const & dir )
{
2022-10-12 22:01:09 -04:00
if ( dir . empty ( ) | | dir = = " . " | | ! Glib : : file_test ( dir , Glib : : FILE_TEST_IS_DIR | Glib : : FILE_TEST_EXISTS ) ) {
2022-01-29 10:21:49 -05:00
return false ;
2022-01-12 18:06:33 -05:00
}
2022-02-07 10:59:22 -05:00
_clip_dir_menu . AddMenuElem ( Gtkmm2ext : : MenuElemNoMnemonic ( display_name ( dir ) , sigc : : bind ( sigc : : mem_fun ( * this , & TriggerClipPicker : : list_dir ) , dir , ( Gtk : : TreeNodeChildren * ) 0 ) ) ) ;
2022-01-12 18:06:33 -05:00
/* check if a parent path of the given dir already exists,
Fix typos in gtk2_ardour/ directory
Found via `codespell -q 3 -S *.po,./share/patchfiles,./libs -L ba,buss,busses,doubleclick,hsi,ontop,ro,seh,siz,sur,te,trough,ue`
2022-01-26 12:35:38 -05:00
* or if this new path is parent to any existing ones .
2022-01-12 18:06:33 -05:00
*/
bool insert = true ;
auto it = _root_paths . begin ( ) ;
while ( it ! = _root_paths . end ( ) ) {
bool erase = false ;
if ( it - > size ( ) > dir . size ( ) ) {
if ( is_subfolder ( dir , * it ) ) {
erase = true ;
2021-12-30 15:22:41 -05:00
}
2022-01-12 18:06:33 -05:00
} else if ( is_subfolder ( * it , dir ) ) {
insert = false ;
break ;
2021-12-30 15:22:41 -05:00
}
2022-01-12 18:06:33 -05:00
if ( erase ) {
auto it2 = it ;
+ + it ;
_root_paths . erase ( it2 ) ;
} else {
+ + it ;
2021-12-30 15:22:41 -05:00
}
2021-12-15 15:07:59 -05:00
}
2022-01-12 18:06:33 -05:00
if ( insert ) {
_root_paths . insert ( dir ) ;
}
2022-01-29 10:21:49 -05:00
return true ;
2021-12-15 15:07:59 -05:00
}
2021-12-19 20:39:15 -05:00
/* ****************************************************************************
* Treeview Callbacks
*/
2022-01-28 20:57:25 -05:00
void
TriggerClipPicker : : drag_begin ( Glib : : RefPtr < Gdk : : DragContext > const & context )
{
TreeView : : Selection : : ListHandle_Path rows = _view . get_selection ( ) - > get_selected_rows ( ) ;
if ( ! rows . empty ( ) ) {
Glib : : RefPtr < Gdk : : Pixmap > pix = _view . create_row_drag_icon ( * rows . begin ( ) ) ;
int w , h ;
pix - > get_size ( w , h ) ;
context - > set_icon ( pix - > get_colormap ( ) , pix , Glib : : RefPtr < Gdk : : Bitmap > ( ) , 4 , h / 2 ) ;
}
}
2022-01-17 20:47:49 -05:00
void
TriggerClipPicker : : drag_end ( Glib : : RefPtr < Gdk : : DragContext > const & )
{
_session - > cancel_audition ( ) ;
}
2022-01-10 18:43:39 -05:00
void
TriggerClipPicker : : cursor_changed ( )
{
if ( ! _session | | ! _autoplay_btn . get_active ( ) ) {
return ;
}
_session - > cancel_audition ( ) ;
TreeModel : : Path p ;
TreeViewColumn * col = NULL ;
_view . get_cursor ( p , col ) ;
TreeModel : : iterator i = _model - > get_iter ( p ) ;
/* This also plays the file if the cursor change deselects the row.
* However , checking if ` i ` is _view . get_selection ( ) does not reliably work from this context .
*/
if ( i & & ( * i ) [ _columns . file ] ) {
2022-10-02 19:02:00 -04:00
mark_auditioned ( i ) ;
2022-01-10 18:43:39 -05:00
audition ( ( * i ) [ _columns . path ] ) ;
}
}
2021-12-15 15:07:59 -05:00
void
TriggerClipPicker : : row_selected ( )
{
2021-12-16 18:36:06 -05:00
if ( ! _session ) {
2021-12-15 15:07:59 -05:00
return ;
}
2021-12-17 19:27:47 -05:00
2022-01-10 18:43:39 -05:00
if ( ! _autoplay_btn . get_active ( ) ) {
_session - > cancel_audition ( ) ;
}
2022-02-12 10:35:24 -05:00
if ( _view . get_selection ( ) - > count_selected_rows ( ) < 1 ) {
2021-12-16 18:36:06 -05:00
_play_btn . set_sensitive ( false ) ;
2022-02-12 10:35:24 -05:00
_midi_prop_table . hide ( ) ;
2021-12-16 18:36:06 -05:00
} else {
2021-12-17 19:27:47 -05:00
TreeView : : Selection : : ListHandle_Path rows = _view . get_selection ( ) - > get_selected_rows ( ) ;
TreeIter i = _model - > get_iter ( * rows . begin ( ) ) ;
2022-02-12 10:35:24 -05:00
_play_btn . set_sensitive ( ( * i ) [ _columns . file ] & & ! _autoplay_btn . get_active ( ) ) ;
2022-02-07 20:33:28 -05:00
std : : string path = ( * i ) [ _columns . path ] ;
if ( SMFSource : : valid_midi_file ( path ) ) {
/* TODO: if it's a really big file, we could skip this check */
2023-02-16 18:33:28 -05:00
std : : shared_ptr < SMFSource > ms ;
2022-02-07 20:33:28 -05:00
try {
2023-02-16 18:33:28 -05:00
ms = std : : dynamic_pointer_cast < SMFSource > (
2022-02-07 20:33:28 -05:00
SourceFactory : : createExternal ( DataType : : MIDI , * _session ,
path , 0 , Source : : Flag ( 0 ) , false ) ) ;
} catch ( const std : : exception & e ) {
error < < string_compose ( _ ( " Could not read file: %1 (%2). " ) ,
path , e . what ( ) ) < < endmsg ;
}
if ( ms ) {
if ( ms - > smf_format ( ) = = 0 ) {
format_text . set_text ( " MIDI Type 0 " ) ;
} else {
2022-02-24 15:33:28 -05:00
format_text . set_text ( string_compose ( _ ( " %1 (%2 Tracks, only the first track will be used) " ) , ms - > smf_format ( ) = = 2 ? X_ ( " MIDI Type 2 " ) : X_ ( " MIDI Type 1 " ) , ms - > num_tracks ( ) ) ) ;
2022-02-07 20:33:28 -05:00
}
channels_value . set_text ( string_compose (
2022-02-24 15:33:28 -05:00
_ ( " %1 notes on channel: %2%3 " ) ,
ms - > n_note_on_events ( ) ,
2022-02-07 20:33:28 -05:00
ARDOUR_UI_UTILS : : midi_channels_as_string ( ms - > used_channels ( ) ) ,
2022-02-24 15:33:28 -05:00
ms - > has_pgm_change ( ) ? _ ( " , with pgms " ) : X_ ( " " )
2022-02-07 20:33:28 -05:00
) ) ;
_midi_prop_table . show ( ) ;
}
2022-02-12 10:35:24 -05:00
} else {
_midi_prop_table . hide ( ) ;
2022-02-07 20:33:28 -05:00
}
2021-12-15 15:07:59 -05:00
}
}
void
2021-12-16 18:36:06 -05:00
TriggerClipPicker : : row_activated ( TreeModel : : Path const & p , TreeViewColumn * )
2021-12-15 15:07:59 -05:00
{
2021-12-16 18:36:06 -05:00
TreeModel : : iterator i = _model - > get_iter ( p ) ;
2021-12-17 19:27:47 -05:00
if ( i & & ( * i ) [ _columns . file ] ) {
2022-10-02 19:02:00 -04:00
mark_auditioned ( i ) ;
2021-12-16 18:36:06 -05:00
audition ( ( * i ) [ _columns . path ] ) ;
2021-12-19 20:39:15 -05:00
} else if ( i ) {
list_dir ( ( * i ) [ _columns . path ] ) ;
2021-12-16 18:36:06 -05:00
}
2021-12-15 15:07:59 -05:00
}
2021-12-17 17:40:12 -05:00
bool
TriggerClipPicker : : test_expand ( TreeModel : : iterator const & i , Gtk : : TreeModel : : Path const & )
{
TreeModel : : Row row = * i ;
if ( row [ _columns . read ] ) {
/* already expanded */
return false ; /* OK */
}
row [ _columns . read ] = true ;
/* remove stub */
_model - > erase ( row . children ( ) . begin ( ) ) ;
list_dir ( row [ _columns . path ] , & row . children ( ) ) ;
return row . children ( ) . size ( ) = = 0 ;
}
void
TriggerClipPicker : : row_collapsed ( TreeModel : : iterator const & i , Gtk : : TreeModel : : Path const & )
{
2021-12-19 20:39:15 -05:00
/* forget about expanded sub-view, refresh when expanded again */
2021-12-17 17:40:12 -05:00
TreeModel : : Row row = * i ;
row [ _columns . read ] = false ;
Gtk : : TreeIter ti ;
while ( ( ti = row . children ( ) . begin ( ) ) ! = row . children ( ) . end ( ) ) {
_model - > erase ( ti ) ;
}
/* add stub child */
row = * ( _model - > append ( row . children ( ) ) ) ;
row [ _columns . read ] = false ;
}
2021-12-15 15:07:59 -05:00
void
TriggerClipPicker : : drag_data_get ( Glib : : RefPtr < Gdk : : DragContext > const & , SelectionData & data , guint , guint time )
{
if ( data . get_target ( ) ! = " text/uri-list " ) {
return ;
}
std : : vector < std : : string > uris ;
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 ) ) ) {
2021-12-17 19:27:47 -05:00
if ( ( * iter ) [ _columns . file ] ) {
uris . push_back ( Glib : : filename_to_uri ( ( * iter ) [ _columns . path ] ) ) ;
}
2021-12-15 15:07:59 -05:00
}
}
data . set_uris ( uris ) ;
2022-10-02 19:02:00 -04:00
reset_audition_marks ( ) ;
2021-12-15 15:07:59 -05:00
}
2022-01-28 20:57:25 -05:00
bool
2022-03-01 10:33:39 -05:00
TriggerClipPicker : : drag_motion ( Glib : : RefPtr < Gdk : : DragContext > const & context , int , int , guint time )
2022-01-28 20:57:25 -05:00
{
2022-02-10 15:59:53 -05:00
for ( auto i : context - > get_targets ( ) ) {
2022-03-01 10:33:39 -05:00
if ( i = = " x-ardour/region.pbdid " ) {
/* prepare for export to local clip library */
if ( ! _clip_library_dir . empty ( ) & & _current_path ! = _clip_library_dir ) {
list_dir ( _clip_library_dir ) ;
}
context - > drag_status ( Gdk : : ACTION_COPY , time ) ;
2022-02-10 15:59:53 -05:00
return true ;
}
}
2022-03-01 10:33:39 -05:00
/* drag from clip-picker (to slots), or
* drag of an external folder to the clip - picker ( add to sample_lib_path )
*/
context - > drag_status ( Gdk : : ACTION_LINK , time ) ;
2022-01-28 20:57:25 -05:00
return true ;
}
void
TriggerClipPicker : : drag_data_received ( Glib : : RefPtr < Gdk : : DragContext > const & context , int /*x*/ , int y , Gtk : : SelectionData const & data , guint /*info*/ , guint time )
{
2022-02-10 12:08:46 -05:00
if ( data . get_target ( ) = = " x-ardour/region.pbdid " ) {
2022-01-28 20:57:25 -05:00
PBD : : ID rid ( data . get_data_as_string ( ) ) ;
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Region > region = RegionFactory : : region_by_id ( rid ) ;
2022-01-29 10:21:49 -05:00
if ( export_to_clip_library ( region , this ) ) {
2022-01-28 20:57:25 -05:00
context - > drag_finish ( true , false , time ) ;
2022-01-29 10:21:49 -05:00
} else {
2022-01-28 20:57:25 -05:00
context - > drag_finish ( true , false , time ) ;
}
2022-02-10 12:08:46 -05:00
} else {
bool changed = false ;
std : : string path ;
2022-02-10 12:20:18 -05:00
std : : string path_to_list ;
2022-02-10 12:08:46 -05:00
std : : vector < std : : string > paths ;
std : : vector < std : : string > a = PBD : : parse_path ( Config - > get_sample_lib_path ( ) ) ;
if ( ARDOUR_UI_UTILS : : convert_drop_to_paths ( paths , data ) ) {
for ( std : : vector < std : : string > : : const_iterator s = paths . begin ( ) ; s ! = paths . end ( ) ; + + s ) {
2022-02-10 16:04:40 -05:00
if ( Glib : : file_test ( * s , Glib : : FILE_TEST_IS_DIR ) ) {
2022-02-10 12:20:18 -05:00
if ( std : : find ( a . begin ( ) , a . end ( ) , * s ) = = a . end ( ) ) {
a . push_back ( * s ) ;
changed = true ;
}
path_to_list = * s ;
2022-02-10 12:08:46 -05:00
}
}
if ( changed ) {
size_t j = 0 ;
for ( std : : vector < std : : string > : : const_iterator i = a . begin ( ) ; i ! = a . end ( ) ; + + i , + + j ) {
if ( j > 0 )
path + = G_SEARCHPATH_SEPARATOR ;
path + = * i ;
}
Config - > set_sample_lib_path ( path ) ;
2022-02-10 12:20:18 -05:00
}
if ( ! path_to_list . empty ( ) ) {
list_dir ( path_to_list ) ;
2022-02-10 12:08:46 -05:00
}
}
}
2022-01-28 20:57:25 -05:00
}
2021-12-19 20:39:15 -05:00
/* ****************************************************************************
* Dir Listing
*/
2021-12-15 15:07:59 -05:00
static bool
2021-12-17 17:40:12 -05:00
audio_midi_suffix ( const std : : string & str )
2021-12-15 15:07:59 -05:00
{
if ( AudioFileSource : : safe_audio_file_extension ( str ) ) {
return true ;
}
if ( SMFSource : : safe_midi_file_extension ( str ) ) {
return true ;
}
return false ;
}
2022-09-26 13:15:44 -04:00
void
TriggerClipPicker : : open_downloader ( )
{
2022-09-27 13:22:56 -04:00
ARDOUR_UI : : instance ( ) - > show_library_download_window ( ) ;
2022-09-26 13:15:44 -04:00
}
2021-12-15 15:07:59 -05:00
void
TriggerClipPicker : : open_dir ( )
{
Gtk : : Window * tlw = dynamic_cast < Gtk : : Window * > ( get_toplevel ( ) ) ;
2021-12-19 20:39:15 -05:00
assert ( tlw ) ;
2022-11-14 21:07:43 -05:00
# ifndef __APPLE__
2023-04-03 19:38:33 -04:00
if ( tlw ) {
_fcd . set_transient_for ( * tlw ) ;
}
2022-11-14 21:07:43 -05:00
# endif
2021-12-19 20:39:15 -05:00
2021-12-16 18:36:06 -05:00
int result = _fcd . run ( ) ;
2021-12-15 15:07:59 -05:00
_fcd . hide ( ) ;
2021-12-19 20:39:15 -05:00
2021-12-15 15:07:59 -05:00
switch ( result ) {
2021-12-19 20:39:15 -05:00
case RESPONSE_OK :
2021-12-15 15:07:59 -05:00
list_dir ( _fcd . get_filename ( ) ) ;
break ;
2021-12-19 20:39:15 -05:00
case RESPONSE_ACCEPT :
if ( Glib : : file_test ( _fcd . get_filename ( ) , Glib : : FILE_TEST_IS_DIR ) ) {
size_t j = 0 ;
std : : string path ;
std : : vector < std : : string > a = PBD : : parse_path ( Config - > get_sample_lib_path ( ) ) ;
2022-02-10 12:20:18 -05:00
if ( std : : find ( a . begin ( ) , a . end ( ) , _fcd . get_filename ( ) ) ! = a . end ( ) ) {
list_dir ( _fcd . get_filename ( ) ) ;
break ;
}
2021-12-19 20:39:15 -05:00
a . push_back ( _fcd . get_filename ( ) ) ;
for ( std : : vector < std : : string > : : const_iterator i = a . begin ( ) ; i ! = a . end ( ) ; + + i , + + j ) {
if ( j > 0 )
path + = G_SEARCHPATH_SEPARATOR ;
path + = * i ;
}
Config - > set_sample_lib_path ( path ) ;
list_dir ( _fcd . get_filename ( ) ) ;
}
break ;
2021-12-15 15:07:59 -05:00
default :
break ;
}
}
void
2021-12-17 17:40:12 -05:00
TriggerClipPicker : : list_dir ( std : : string const & path , Gtk : : TreeNodeChildren const * pc )
2021-12-15 15:07:59 -05:00
{
2022-01-29 10:21:49 -05:00
if ( _ignore_list_dir ) {
return ;
}
2022-02-07 10:59:22 -05:00
/* do not recurse when calling _clip_dir_menu.set_active() */
2022-01-29 10:21:49 -05:00
PBD : : Unwinder < bool > uw ( _ignore_list_dir , true ) ;
2021-12-19 20:39:15 -05:00
if ( ! Glib : : file_test ( path , Glib : : FILE_TEST_IS_DIR ) ) {
assert ( 0 ) ;
return ;
}
2021-12-17 17:40:12 -05:00
if ( ! pc ) {
2022-01-29 10:21:49 -05:00
_view . set_model ( Glib : : RefPtr < Gtk : : TreeStore > ( 0 ) ) ;
2021-12-17 17:40:12 -05:00
_model - > clear ( ) ;
2022-02-07 10:59:22 -05:00
_clip_dir_menu . set_active ( display_name ( path ) ) ;
2021-12-17 17:40:12 -05:00
}
2021-12-19 20:39:15 -05:00
_current_path = path ;
2021-12-17 17:40:12 -05:00
2022-01-30 16:42:13 -05:00
if ( _clip_library_dir = = path ) {
_open_library_btn . show ( ) ;
} else {
_open_library_btn . hide ( ) ;
}
2021-12-17 17:40:12 -05:00
std : : vector < std : : string > dirs ;
std : : vector < std : : string > files ;
try {
Glib : : Dir dir ( path ) ;
for ( Glib : : DirIterator i = dir . begin ( ) ; i ! = dir . end ( ) ; + + i ) {
std : : string fullpath = Glib : : build_filename ( path , * i ) ;
std : : string basename = * i ;
2021-12-19 20:39:15 -05:00
if ( basename . size ( ) = = 0 | | basename [ 0 ] = = ' . ' ) {
2021-12-17 19:27:47 -05:00
continue ;
}
2021-12-17 17:40:12 -05:00
if ( Glib : : file_test ( fullpath , Glib : : FILE_TEST_IS_DIR ) ) {
dirs . push_back ( * i ) ;
continue ;
}
if ( audio_midi_suffix ( fullpath ) ) {
files . push_back ( * i ) ;
}
}
} catch ( Glib : : FileError const & err ) {
}
std : : sort ( dirs . begin ( ) , dirs . end ( ) ) ;
std : : sort ( files . begin ( ) , files . end ( ) ) ;
2021-12-19 20:39:15 -05:00
if ( ! pc ) {
2021-12-30 15:22:41 -05:00
if ( _root_paths . find ( _current_path ) = = _root_paths . end ( ) ) {
2022-10-02 19:02:00 -04:00
TreeModel : : Row row = * ( _model - > append ( ) ) ;
row [ _columns . name ] = " .. " ;
row [ _columns . path ] = Glib : : path_get_dirname ( _current_path ) ;
row [ _columns . read ] = false ;
row [ _columns . file ] = false ;
row [ _columns . color ] = _color_foreground ;
2021-12-19 20:39:15 -05:00
}
}
2021-12-17 17:40:12 -05:00
for ( auto & f : dirs ) {
TreeModel : : Row row ;
if ( pc ) {
row = * ( _model - > append ( * pc ) ) ;
} else {
row = * ( _model - > append ( ) ) ;
}
row [ _columns . name ] = f ;
row [ _columns . path ] = Glib : : build_filename ( path , f ) ;
row [ _columns . read ] = false ;
2021-12-17 19:27:47 -05:00
row [ _columns . file ] = false ;
2021-12-17 17:40:12 -05:00
/* add stub child */
2022-10-02 19:02:00 -04:00
row = * ( _model - > append ( row . children ( ) ) ) ;
row [ _columns . read ] = false ;
row [ _columns . color ] = _color_foreground ;
2021-12-17 17:40:12 -05:00
}
for ( auto & f : files ) {
TreeModel : : Row row ;
if ( pc ) {
row = * ( _model - > append ( * pc ) ) ;
} else {
row = * ( _model - > append ( ) ) ;
}
row [ _columns . name ] = f ;
row [ _columns . path ] = Glib : : build_filename ( path , f ) ;
row [ _columns . read ] = false ;
2021-12-17 19:27:47 -05:00
row [ _columns . file ] = true ;
2021-12-15 15:07:59 -05:00
}
2022-01-29 10:21:49 -05:00
if ( ! pc ) {
_view . set_model ( _model ) ;
}
2021-12-15 15:07:59 -05:00
}
2021-12-16 18:36:06 -05:00
2022-09-16 15:07:39 -04:00
void
TriggerClipPicker : : refresh_library ( )
{
list_dir ( _current_path ) ;
}
2022-01-30 16:42:13 -05:00
void
TriggerClipPicker : : open_library ( )
{
PBD : : open_folder ( _clip_library_dir ) ;
}
2021-12-19 20:39:15 -05:00
/* ****************************************************************************
* Auditioner
*/
void
TriggerClipPicker : : set_session ( Session * s )
{
SessionHandlePtr : : set_session ( s ) ;
2022-01-11 13:26:14 -05:00
_play_btn . set_sensitive ( false ) ;
_stop_btn . set_sensitive ( false ) ;
2022-02-07 20:33:28 -05:00
_midi_prop_table . hide ( ) ; //only shown when a valid smf is chosen
2022-01-11 13:26:14 -05:00
2021-12-19 20:39:15 -05:00
if ( ! _session ) {
_seek_slider . set_sensitive ( false ) ;
_auditioner_connections . drop_connections ( ) ;
2022-02-02 16:09:47 -05:00
_processor_connections . drop_connections ( ) ;
audition_processor_going_away ( ) ;
2023-03-28 19:48:25 -04:00
std : : shared_ptr < Controllable > none ;
_gain_control . set_controllable ( none ) ;
2021-12-19 20:39:15 -05:00
} else {
_auditioner_connections . drop_connections ( ) ;
2024-10-18 19:51:44 -04:00
_session - > AuditionActive . connect ( _auditioner_connections , invalidator ( * this ) , std : : bind ( & TriggerClipPicker : : audition_active , this , _1 ) , gui_context ( ) ) ;
_session - > the_auditioner ( ) - > AuditionProgress . connect ( _auditioner_connections , invalidator ( * this ) , std : : bind ( & TriggerClipPicker : : audition_progress , this , _1 , _2 ) , gui_context ( ) ) ;
_session - > the_auditioner ( ) - > processors_changed . connect ( _auditioner_connections , invalidator ( * this ) , std : : bind ( & TriggerClipPicker : : audition_processors_changed , this ) , gui_context ( ) ) ;
2022-02-02 16:09:47 -05:00
audition_processors_changed ( ) ; /* set sensitivity */
2023-03-28 19:48:25 -04:00
_gain_control . set_controllable ( _session - > the_auditioner ( ) - > gain_control ( ) ) ;
2021-12-19 20:39:15 -05:00
}
}
2022-01-10 18:43:39 -05:00
void
TriggerClipPicker : : autoplay_toggled ( )
{
UIConfiguration : : instance ( ) . set_autoplay_clips ( _autoplay_btn . get_active ( ) ) ;
row_selected ( ) ; /* maybe cancel audition, update sensitivity */
}
2021-12-16 18:36:06 -05:00
void
TriggerClipPicker : : stop_audition ( )
{
if ( _session ) {
_session - > cancel_audition ( ) ;
}
}
void
TriggerClipPicker : : audition_active ( bool active )
{
2022-01-11 13:26:14 -05:00
_play_btn . set_sensitive ( ! active & & ! _autoplay_btn . get_active ( ) ) ;
2021-12-16 18:36:06 -05:00
_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 ( ) ) ;
2022-10-02 19:02:00 -04:00
mark_auditioned ( i ) ;
2021-12-16 18:36:06 -05:00
audition ( ( * i ) [ _columns . path ] ) ;
}
2022-10-02 19:02:00 -04:00
void
TriggerClipPicker : : mark_auditioned ( TreeModel : : iterator i )
{
if ( ! UIConfiguration : : instance ( ) . get_highlight_auditioned_clips ( ) ) {
return ;
}
2022-10-12 22:01:09 -04:00
if ( ! ( * i ) [ _columns . file ] ) {
return ;
}
2022-10-02 19:02:00 -04:00
( * i ) [ _columns . color ] = _color_auditioned ;
( * i ) [ _columns . auditioned ] = true ;
}
void
TriggerClipPicker : : reset_audition_marks ( bool force )
{
for ( auto const & i : _model - > children ( ) ) {
if ( i [ _columns . auditioned ] | | force ) {
i [ _columns . color ] = _color_foreground ;
}
}
}
void
TriggerClipPicker : : on_theme_changed ( )
{
2022-10-13 19:39:00 -04:00
_color_foreground = _view . get_style ( ) - > get_text ( STATE_NORMAL ) ;
_color_auditioned = _view . get_style ( ) - > get_text ( STATE_INSENSITIVE ) ;
2022-10-02 19:02:00 -04:00
for ( auto const & i : _model - > children ( ) ) {
if ( i [ _columns . auditioned ] ) {
i [ _columns . color ] = _color_auditioned ;
} else {
i [ _columns . color ] = _color_foreground ;
}
}
}
2021-12-16 18:36:06 -05:00
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 ;
}
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Region > r ;
2021-12-16 18:36:06 -05:00
if ( SMFSource : : valid_midi_file ( path ) ) {
2023-02-16 18:33:28 -05:00
std : : shared_ptr < SMFSource > ms = std : : dynamic_pointer_cast < SMFSource > ( SourceFactory : : createExternal ( DataType : : MIDI , * _session , path , 0 , Source : : Flag ( 0 ) , false ) ) ;
2021-12-16 18:36:06 -05:00
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 ) ;
2023-02-16 18:33:28 -05:00
r = std : : dynamic_pointer_cast < MidiRegion > ( RegionFactory : : create ( std : : dynamic_pointer_cast < Source > ( ms ) , plist , false ) ) ;
2021-12-16 18:36:06 -05:00
assert ( r ) ;
} else {
SourceList srclist ;
2023-02-16 18:33:28 -05:00
std : : shared_ptr < AudioFileSource > afs ;
2021-12-16 18:36:06 -05:00
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 {
2023-02-16 18:33:28 -05:00
afs = std : : dynamic_pointer_cast < AudioFileSource > ( SourceFactory : : createExternal ( DataType : : AUDIO , * _session , path , n , Source : : Flag ( ARDOUR : : AudioFileSource : : NoPeakFile ) , false ) ) ;
2021-12-16 18:36:06 -05:00
if ( afs - > sample_rate ( ) ! = _session - > nominal_sample_rate ( ) ) {
2023-02-16 18:33:28 -05:00
std : : shared_ptr < SrcFileSource > sfs ( new SrcFileSource ( * _session , afs , ARDOUR : : SrcGood ) ) ;
2021-12-16 18:36:06 -05:00
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 ;
}
2023-02-16 18:33:28 -05:00
afs = std : : dynamic_pointer_cast < AudioFileSource > ( srclist [ 0 ] ) ;
2021-12-16 18:36:06 -05:00
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 ) ;
2023-02-16 18:33:28 -05:00
r = std : : dynamic_pointer_cast < AudioRegion > ( RegionFactory : : create ( srclist , plist , false ) ) ;
2021-12-16 18:36:06 -05:00
}
r - > set_position ( timepos_t ( ) ) ;
_session - > audition_region ( r ) ;
}
2022-02-02 16:09:47 -05:00
2022-02-04 07:57:33 -05:00
void
TriggerClipPicker : : audition_processor_idle ( )
{
if ( ! _session | | _session - > deletion_in_progress ( ) | | ! _session - > the_auditioner ( ) ) {
return ;
}
assert ( _session & & _session - > the_auditioner ( ) ) ;
ARDOUR_UI : : instance ( ) - > get_process_buffers ( ) ;
_session - > the_auditioner ( ) - > idle_synth_update ( ) ;
ARDOUR_UI : : instance ( ) - > drop_process_buffers ( ) ;
}
bool
TriggerClipPicker : : audition_processor_viz ( bool show )
{
if ( show ) {
_idle_connection = Timers : : fps_connect ( sigc : : mem_fun ( * this , & TriggerClipPicker : : audition_processor_idle ) ) ;
} else {
_idle_connection . disconnect ( ) ;
}
return false ;
}
2022-02-02 16:09:47 -05:00
void
TriggerClipPicker : : audition_show_plugin_ui ( )
{
if ( ! _audition_plugnui ) {
2023-02-16 18:33:28 -05:00
std : : shared_ptr < PluginInsert > plugin_insert = std : : dynamic_pointer_cast < PluginInsert > ( _session - > the_auditioner ( ) - > the_instrument ( ) ) ;
2022-02-02 16:09:47 -05:00
if ( plugin_insert ) {
_audition_plugnui = new PluginUIWindow ( plugin_insert ) ;
_audition_plugnui - > set_session ( _session ) ;
_audition_plugnui - > show_all ( ) ;
_audition_plugnui - > set_title ( /* generate_processor_title (plugin_insert)*/ _ ( " Audition Synth " ) ) ;
2024-10-18 19:51:44 -04:00
plugin_insert - > DropReferences . connect ( _processor_connections , invalidator ( * this ) , std : : bind ( & TriggerClipPicker : : audition_processor_going_away , this ) , gui_context ( ) ) ;
2022-02-04 07:57:33 -05:00
_audition_plugnui - > signal_map_event ( ) . connect ( sigc : : hide ( sigc : : bind ( sigc : : mem_fun ( * this , & TriggerClipPicker : : audition_processor_viz ) , true ) ) ) ;
_audition_plugnui - > signal_unmap_event ( ) . connect ( sigc : : hide ( sigc : : bind ( sigc : : mem_fun ( * this , & TriggerClipPicker : : audition_processor_viz ) , false ) ) ) ;
2022-02-02 16:09:47 -05:00
}
}
if ( _audition_plugnui ) {
_audition_plugnui - > present ( ) ;
}
}
void
TriggerClipPicker : : audition_processor_going_away ( )
{
if ( _audition_plugnui ) {
2022-02-04 07:57:33 -05:00
_idle_connection . disconnect ( ) ;
2022-02-02 16:09:47 -05:00
delete _audition_plugnui ;
}
_audition_plugnui = 0 ;
}
void
TriggerClipPicker : : audition_processors_changed ( )
{
2022-02-04 07:58:22 -05:00
if ( ! _session | | _session - > deletion_in_progress ( ) | | ! _session - > the_auditioner ( ) ) {
2022-02-02 17:08:10 -05:00
_show_plugin_btn . set_sensitive ( false ) ;
2022-02-04 21:37:23 -05:00
set_tooltip ( _show_plugin_btn , " You must first play one midi file to show the plugin's GUI " ) ;
2022-02-04 07:58:22 -05:00
return ;
2022-02-02 17:08:10 -05:00
}
2022-02-04 21:37:23 -05:00
if ( _session & & _session - > the_auditioner ( ) - > get_audition_synth_info ( ) ) {
2023-02-16 18:33:28 -05:00
std : : shared_ptr < PluginInsert > plugin_insert = std : : dynamic_pointer_cast < PluginInsert > ( _session - > the_auditioner ( ) - > the_instrument ( ) ) ;
2022-02-04 21:37:23 -05:00
if ( plugin_insert ) {
set_tooltip ( _show_plugin_btn , " Show the selected audition-instrument's GUI " ) ;
_show_plugin_btn . set_sensitive ( true ) ;
}
}
2022-02-02 16:09:47 -05:00
}