Merged with trunk R1327.

git-svn-id: svn://localhost/ardour2/branches/midi@1328 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
David Robillard 2007-01-15 18:33:54 +00:00
parent 32f3a4ae3e
commit 7701c52adb
21 changed files with 464 additions and 383 deletions

View file

@ -68,8 +68,8 @@ cEnteredAutomationLine 0.87 0.39 0.39 1.00
cEnteredMarker 0.87 0.39 0.39 1.00 cEnteredMarker 0.87 0.39 0.39 1.00
cMeterMarker 0.95 0.26 0.36 1.00 cMeterMarker 0.95 0.26 0.36 1.00
cTempoMarker 0.95 0.26 0.36 1.00 cTempoMarker 0.95 0.26 0.36 1.00
cMeasureLineBeat 0.40 0.40 0.40 0.50 cMeasureLineBeat 0.45 0.45 0.45 0.40
cMeasureLineBar 0.55 0.55 0.60 0.70 cMeasureLineBar 0.55 0.55 0.60 0.55
cGhostTrackBaseOutline 0.00 0.00 0.00 1.00 cGhostTrackBaseOutline 0.00 0.00 0.00 1.00
cGhostTrackBaseFill 0.27 0.00 0.49 0.50 cGhostTrackBaseFill 0.27 0.00 0.49 0.50
cImageTrackBase 0.87 0.87 0.85 1.00 cImageTrackBase 0.87 0.87 0.85 1.00

View file

@ -676,27 +676,44 @@ style "edit_group_3"
bg[SELECTED] = { 0.93, 0.34, 0.08 } bg[SELECTED] = { 0.93, 0.34, 0.08 }
} }
style "region_list_display" = "small_bold_text" style "treeview_parent_node"
{ {
fg[NORMAL] = { 0.80, 0.80, 0.80 } # specifies *just* the color used for whole file rows when not selected
fg[ACTIVE] = { 0.80, 0.80, 0.80 } fg[NORMAL] = { 0.0, 0.6, 0.85 }
fg[SELECTED] = { 0.70, 0.70, 0.70 } }
bg[NORMAL] = { 0, 0, 0 }
bg[ACTIVE] = { 0, 0, 0 } style "treeview_display" = "small_bold_text"
bg[SELECTED] = { 0, 0, 0 } {
base[NORMAL] = { 0, 0, 0 } # expander arrow border and DnD "icon" text
base[ACTIVE] = { 0, 1, 0 } fg[NORMAL] = { 0.8, 0.8, 0.8 }
base[INSENSITIVE] = { 0, 0, 0 }
base[SELECTED] = { 0.80, 0.80, 0.80 } # background with no rows or no selection, plus
# expander arrow core and DnD "icon" background
base[NORMAL] = { 0.20, 0.20, 0.25 }
# selected row bg when window does not have focus (including during DnD)
base[ACTIVE] = { 0.0, 0.60, 0.60 }
# selected row bg when window has focus
base[SELECTED] = { 0, 0.75, 0.75 }
# row text when in normal state and not a parent
text[NORMAL] = { 0.80, 0.80, 0.80 }
# selected row text with window focus
text[SELECTED] = { 0, 1.0, 1.0 }
# selected row text without window focus (including during DnD)
text[ACTIVE] = { 0, 1.0, 1.0 }
} }
style "main_canvas_area" style "main_canvas_area"
{ {
bg[NORMAL] = { 0.20, 0.20, 0.25 } bg[NORMAL] = { 0.30, 0.30, 0.34 }
bg[ACTIVE] = { 0.20, 0.20, 0.25 } bg[ACTIVE] = { 0.30, 0.30, 0.34 }
bg[INSENSITIVE] = { 0.20, 0.20, 0.25 } bg[INSENSITIVE] = { 0.30, 0.30, 0.34 }
bg[SELECTED] = { 0.20, 0.20, 0.25 } bg[SELECTED] = { 0.30, 0.30, 0.34 }
bg[PRELIGHT] = { 0.20, 0.20, 0.25 } bg[PRELIGHT] = { 0.30, 0.30, 0.34 }
} }
style "track_controls_inactive" style "track_controls_inactive"
@ -999,11 +1016,6 @@ style "pan_slider"
} }
style "region_list_whole_file"
{
fg[NORMAL] = { 0.4, 0.4, 0.9 }
}
style "ardour_button" ="default_buttons_menus" style "ardour_button" ="default_buttons_menus"
{ {
xthickness = 1 xthickness = 1
@ -1189,7 +1201,6 @@ widget "*EditorTrackNameDisplay" style "track_name_display"
widget "*EditorTrackNameDisplay*" style "track_name_display" widget "*EditorTrackNameDisplay*" style "track_name_display"
widget "*EditorActiveTrackNameDisplay" style "active_track_name_display" widget "*EditorActiveTrackNameDisplay" style "active_track_name_display"
widget "*EditorActiveTrackNameDisplay*" style "active_track_name_display" widget "*EditorActiveTrackNameDisplay*" style "active_track_name_display"
widget "*EditorRegionList" style "region_list_display"
widget "*CrossfadeEditAuditionButton" style "red_when_active" widget "*CrossfadeEditAuditionButton" style "red_when_active"
widget "*CrossfadeEditAuditionButton*" style "red_when_active" widget "*CrossfadeEditAuditionButton*" style "red_when_active"
widget "*CrossfadeEditCurveButton" style "red_when_active" widget "*CrossfadeEditCurveButton" style "red_when_active"
@ -1221,19 +1232,19 @@ widget "*ParameterValueDisplay" style "medium_bold_entry"
widget "*PluginUIClickBox" style "medium_bold_entry" widget "*PluginUIClickBox" style "medium_bold_entry"
widget "*PluginUIClickBox*" style "medium_bold_entry" widget "*PluginUIClickBox*" style "medium_bold_entry"
widget "*PluginSlider" style "plugin_slider" widget "*PluginSlider" style "plugin_slider"
widget "*TrackListDisplay" style "track_list_display"
widget "*TrackListDisplay.*" style "small_bold_text"
widget "*EditGroupList" style "track_list_display"
widget "*RegionListDisplay" style "small_bold_entry"
widget "*RegionListDisplay.*" style "small_bold_text"
widget "*RedirectSelector" style "redirect_list_display" widget "*RedirectSelector" style "redirect_list_display"
widget "*RedirectSelector.*" style "redirect_list_display" widget "*RedirectSelector.*" style "redirect_list_display"
widget "*EditGroupDisplay" style "treeview_display"
widget "*TrackListDisplay" style "treeview_display"
widget "*RegionListDisplay" style "treeview_display"
widget "*NamedSelectionDisplay" style "treeview_display"
widget "*SnapshotDisplay" style "treeview_display"
widget "*MixerTrackCommentArea" style "option_entry" widget "*MixerTrackCommentArea" style "option_entry"
widget "*MixerPanZone" style "pan_zone" widget "*MixerPanZone" style "pan_zone"
widget "*MixerTrackDisplayList" style "track_list_display" widget "*MixerTrackDisplayList" style "treeview_display"
widget "*MixerSnapshotDisplayList" style "track_list_display" widget "*MixerSnapshotDisplayList" style "treeview_display"
widget "*MixerAuxDisplayList" style "track_list_display" widget "*MixerAuxDisplayList" style "treeview_display"
widget "*MixerGroupList" style "track_list_display" widget "*MixerGroupList" style "treeview_display"
widget "*RegionEditorLabel" style "medium_text" widget "*RegionEditorLabel" style "medium_text"
widget "*RegionEditorSmallLabel" style "small_text" widget "*RegionEditorSmallLabel" style "small_text"
widget "*RegionEditorEntry" style "medium_entry" widget "*RegionEditorEntry" style "medium_entry"
@ -1360,7 +1371,7 @@ widget "*PanningLinkDirectionButton" style "very_small_button"
widget "*PanningLinkDirectionButton.*" style "very_small_button" widget "*PanningLinkDirectionButton.*" style "very_small_button"
widget "*ChannelCountSelector" style "medium_bold_entry" widget "*ChannelCountSelector" style "medium_bold_entry"
widget "*ChannelCountSelector.GtkArrow" style "default_buttons_menus" widget "*ChannelCountSelector.GtkArrow" style "default_buttons_menus"
widget "*RegionListWholeFile" style "region_list_whole_file" widget "*RegionListWholeFile" style "treeview_parent_node"
class "GtkWidget" style "default_base" class "GtkWidget" style "default_base"
class "GtkScrollbar" style "ardour_adjusters" class "GtkScrollbar" style "ardour_adjusters"

View file

@ -504,12 +504,11 @@ Editor::Editor (AudioEngine& eng)
active_cell->property_activatable() = true; active_cell->property_activatable() = true;
active_cell->property_radio() = false; active_cell->property_radio() = false;
edit_group_display.set_name ("EditGroupList");
group_model->signal_row_changed().connect (mem_fun (*this, &Editor::edit_group_row_change)); group_model->signal_row_changed().connect (mem_fun (*this, &Editor::edit_group_row_change));
edit_group_display.set_name ("EditGroupList"); edit_group_display.set_name ("EditGroupList");
edit_group_display.get_selection()->set_mode (SELECTION_SINGLE); edit_group_display.get_selection()->set_mode (SELECTION_SINGLE);
edit_group_display.set_headers_visible (false);
edit_group_display.set_reorderable (false); edit_group_display.set_reorderable (false);
edit_group_display.set_rules_hint (true); edit_group_display.set_rules_hint (true);
edit_group_display.set_size_request (75, -1); edit_group_display.set_size_request (75, -1);
@ -595,11 +594,12 @@ Editor::Editor (AudioEngine& eng)
named_selection_display.append_column (_("Chunks"), named_selection_columns.text); named_selection_display.append_column (_("Chunks"), named_selection_columns.text);
named_selection_display.set_headers_visible (false); named_selection_display.set_headers_visible (false);
named_selection_display.set_size_request (100, -1); named_selection_display.set_size_request (100, -1);
named_selection_display.set_name ("RegionListDisplay"); named_selection_display.set_name ("NamedSelectionDisplay");
named_selection_display.get_selection()->set_mode (SELECTION_SINGLE); named_selection_display.get_selection()->set_mode (SELECTION_SINGLE);
named_selection_display.set_size_request (100, -1); named_selection_display.set_size_request (100, -1);
named_selection_display.signal_button_release_event().connect (mem_fun(*this, &Editor::named_selection_display_button_press), false); named_selection_display.signal_button_release_event().connect (mem_fun(*this, &Editor::named_selection_display_button_release), false);
named_selection_display.signal_key_release_event().connect (mem_fun(*this, &Editor::named_selection_display_key_release), false);
named_selection_display.get_selection()->signal_changed().connect (mem_fun (*this, &Editor::named_selection_display_selection_changed)); named_selection_display.get_selection()->signal_changed().connect (mem_fun (*this, &Editor::named_selection_display_selection_changed));
/* SNAPSHOTS */ /* SNAPSHOTS */
@ -607,7 +607,7 @@ Editor::Editor (AudioEngine& eng)
snapshot_display_model = ListStore::create (snapshot_display_columns); snapshot_display_model = ListStore::create (snapshot_display_columns);
snapshot_display.set_model (snapshot_display_model); snapshot_display.set_model (snapshot_display_model);
snapshot_display.append_column (X_("snapshot"), snapshot_display_columns.visible_name); snapshot_display.append_column (X_("snapshot"), snapshot_display_columns.visible_name);
snapshot_display.set_name ("SnapshotDisplayList"); snapshot_display.set_name ("SnapshotDisplay");
snapshot_display.set_size_request (75, -1); snapshot_display.set_size_request (75, -1);
snapshot_display.set_headers_visible (false); snapshot_display.set_headers_visible (false);
snapshot_display.set_reorderable (false); snapshot_display.set_reorderable (false);
@ -1773,7 +1773,7 @@ Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
items.push_back (MenuElem (_("Crop region to range"), mem_fun(*this, &Editor::crop_region_to_selection))); items.push_back (MenuElem (_("Crop region to range"), mem_fun(*this, &Editor::crop_region_to_selection)));
items.push_back (MenuElem (_("Fill range with region"), mem_fun(*this, &Editor::region_fill_selection))); items.push_back (MenuElem (_("Fill range with region"), mem_fun(*this, &Editor::region_fill_selection)));
items.push_back (MenuElem (_("Duplicate range"), bind (mem_fun(*this, &Editor::duplicate_dialog), false))); items.push_back (MenuElem (_("Duplicate range"), bind (mem_fun(*this, &Editor::duplicate_dialog), false)));
items.push_back (MenuElem (_("Create chunk from range"), mem_fun(*this, &Editor::name_selection))); items.push_back (MenuElem (_("Create chunk from range"), mem_fun(*this, &Editor::create_named_selection)));
items.push_back (SeparatorElem()); items.push_back (SeparatorElem());
items.push_back (MenuElem (_("Bounce range"), mem_fun(*this, &Editor::bounce_range_selection))); items.push_back (MenuElem (_("Bounce range"), mem_fun(*this, &Editor::bounce_range_selection)));
items.push_back (MenuElem (_("Export range"), mem_fun(*this, &Editor::export_selection))); items.push_back (MenuElem (_("Export range"), mem_fun(*this, &Editor::export_selection)));
@ -4020,15 +4020,16 @@ Editor::redisplay_snapshots ()
string statename = *(*i); string statename = *(*i);
TreeModel::Row row = *(snapshot_display_model->append()); TreeModel::Row row = *(snapshot_display_model->append());
// we don't have a way of changing the rendering in just one TreeView /* this lingers on in case we ever want to change the visible
// cell so just put an asterisk on each side of the name for now. name of the snapshot.
*/
string display_name; string display_name;
display_name = statename;
if (statename == session->snap_name()) { if (statename == session->snap_name()) {
display_name = "*"+statename+"*";
snapshot_display.get_selection()->select(row); snapshot_display.get_selection()->select(row);
} else { }
display_name = statename;
}
row[snapshot_display_columns.visible_name] = display_name; row[snapshot_display_columns.visible_name] = display_name;
row[snapshot_display_columns.real_name] = statename; row[snapshot_display_columns.real_name] = statename;

View file

@ -788,16 +788,16 @@ class Editor : public PublicEditor
Gtkmm2ext::DnDTreeView<ARDOUR::NamedSelection*> named_selection_display; Gtkmm2ext::DnDTreeView<ARDOUR::NamedSelection*> named_selection_display;
Gtk::ScrolledWindow named_selection_scroller; Gtk::ScrolledWindow named_selection_scroller;
void name_selection(); void create_named_selection ();
void named_selection_name_chosen ();
void create_named_selection (const string &);
void paste_named_selection (float times); void paste_named_selection (float times);
void remove_selected_named_selections ();
void handle_new_named_selection (); void handle_new_named_selection ();
void add_named_selection_to_named_selection_display (ARDOUR::NamedSelection&); void add_named_selection_to_named_selection_display (ARDOUR::NamedSelection&);
void redisplay_named_selections (); void redisplay_named_selections ();
gint named_selection_display_button_press (GdkEventButton *ev); bool named_selection_display_button_release (GdkEventButton *ev);
bool named_selection_display_key_release (GdkEventKey *ev);
void named_selection_display_selection_changed (); void named_selection_display_selection_changed ();
/* track views */ /* track views */

View file

@ -282,11 +282,21 @@ Editor::session_going_away ()
/* rip everything out of the list displays */ /* rip everything out of the list displays */
region_list_clear (); // no clear() method in gtkmm 1.2 region_list_display.set_model (Glib::RefPtr<Gtk::TreeStore>(0));
route_list_display.set_model (Glib::RefPtr<Gtk::TreeStore>(0));
named_selection_display.set_model (Glib::RefPtr<Gtk::TreeStore>(0));
edit_group_display.set_model (Glib::RefPtr<Gtk::TreeStore>(0));
region_list_model->clear ();
route_display_model->clear (); route_display_model->clear ();
named_selection_model->clear (); named_selection_model->clear ();
group_model->clear (); group_model->clear ();
region_list_display.set_model (region_list_model);
route_list_display.set_model (route_display_model);
named_selection_display.set_model (named_selection_model);
edit_group_display.set_model (group_model);
edit_cursor_clock.set_session (0); edit_cursor_clock.set_session (0);
zoom_range_clock.set_session (0); zoom_range_clock.set_session (0);
nudge_clock.set_session (0); nudge_clock.set_session (0);

View file

@ -2800,7 +2800,6 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
set<boost::shared_ptr<Playlist> > affected_playlists; set<boost::shared_ptr<Playlist> > affected_playlists;
pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result; pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
// TODO: Crossfades need to be copied!
for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) { for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
RegionView* rv; RegionView* rv;
@ -2808,7 +2807,7 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist(); boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view()); RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
insert_result = affected_playlists.insert (to_playlist); insert_result = affected_playlists.insert (to_playlist);
if (insert_result.second) { if (insert_result.second) {
session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0)); session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
@ -3223,7 +3222,7 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
/* hide any dependent views */ /* hide any dependent views */
// rv->get_time_axis_view().hide_dependent_views (*rv); rv->get_time_axis_view().hide_dependent_views (*rv);
/* this is subtle. raising the regionview itself won't help, /* this is subtle. raising the regionview itself won't help,
because raise_to_top() just puts the item on the top of because raise_to_top() just puts the item on the top of

View file

@ -50,7 +50,8 @@ using namespace Editing;
void void
Editor::handle_region_removed (boost::weak_ptr<Region> wregion) Editor::handle_region_removed (boost::weak_ptr<Region> wregion)
{ {
ENSURE_GUI_THREAD (bind (mem_fun (*this, &Editor::handle_region_removed), wregion)); cerr << "removed region\n";
ENSURE_GUI_THREAD (mem_fun (*this, &Editor::redisplay_regions));
redisplay_regions (); redisplay_regions ();
} }
@ -255,12 +256,6 @@ Editor::redisplay_regions ()
} }
} }
void
Editor::region_list_clear ()
{
region_list_model->clear();
}
void void
Editor::build_region_list_menu () Editor::build_region_list_menu ()
{ {
@ -562,6 +557,7 @@ Editor::hide_a_region (boost::shared_ptr<Region> r)
void void
Editor::remove_a_region (boost::shared_ptr<Region> r) Editor::remove_a_region (boost::shared_ptr<Region> r)
{ {
cerr << "remove " << r->name();
session->remove_region_from_region_list (r); session->remove_region_from_region_list (r);
} }
@ -580,6 +576,7 @@ Editor::hide_region_from_region_list ()
void void
Editor::remove_region_from_region_list () Editor::remove_region_from_region_list ()
{ {
cerr << "Mapping remove over region selection\n";
region_list_selection_mapover (mem_fun (*this, &Editor::remove_a_region)); region_list_selection_mapover (mem_fun (*this, &Editor::remove_a_region));
} }

View file

@ -65,10 +65,48 @@ Editor::redisplay_named_selections ()
session->foreach_named_selection (*this, &Editor::add_named_selection_to_named_selection_display); session->foreach_named_selection (*this, &Editor::add_named_selection_to_named_selection_display);
} }
gint bool
Editor::named_selection_display_button_press (GdkEventButton *ev) Editor::named_selection_display_key_release (GdkEventKey* ev)
{ {
if (session == 0) {
return true;
}
switch (ev->keyval) {
case GDK_Delete:
remove_selected_named_selections ();
return true;
break;
default:
return false;
break;
}
}
void
Editor::remove_selected_named_selections ()
{
Glib::RefPtr<TreeSelection> selection = named_selection_display.get_selection();
TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
if (selection->count_selected_rows() == 0) {
return;
}
for (TreeView::Selection::ListHandle_Path::iterator i = rows.begin(); i != rows.end(); ++i) {
TreeIter iter;
if ((iter = named_selection_model->get_iter (*i))) {
session->remove_named_selection ((*iter)[named_selection_columns.selection]);
}
}
}
bool
Editor::named_selection_display_button_release (GdkEventButton *ev)
{
TreeModel::Children rows = named_selection_model->children(); TreeModel::Children rows = named_selection_model->children();
TreeModel::Children::iterator i; TreeModel::Children::iterator i;
Glib::RefPtr<TreeSelection> selection = named_selection_display.get_selection(); Glib::RefPtr<TreeSelection> selection = named_selection_display.get_selection();
@ -91,7 +129,8 @@ Editor::named_selection_display_button_press (GdkEventButton *ev)
} }
} }
} }
return FALSE;
return false;
} }
@ -101,37 +140,10 @@ Editor::named_selection_display_selection_changed ()
} }
void void
Editor::name_selection () Editor::create_named_selection ()
{ {
ArdourPrompter p; string name;
p.set_prompt (_("Name for Chunk:"));
p.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
p.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
p.change_labels (_("Create Chunk"), _("Forget it"));
p.show_all ();
switch (p.run ()) {
case Gtk::RESPONSE_ACCEPT:
string name;
p.get_result (name);
if (name.length()) {
create_named_selection (name);
}
break;
}
}
void
Editor::named_selection_name_chosen ()
{
Gtk::Main::quit ();
}
void
Editor::create_named_selection (const string & name)
{
if (session == 0) { if (session == 0) {
return; return;
} }
@ -141,7 +153,6 @@ Editor::create_named_selection (const string & name)
if (selection->time.empty()) { if (selection->time.empty()) {
return; return;
} }
TrackViewList *views = get_valid_views (selection->time.track, selection->time.group); TrackViewList *views = get_valid_views (selection->time.track, selection->time.group);
@ -157,25 +168,42 @@ Editor::create_named_selection (const string & name)
boost::shared_ptr<Playlist> pl = (*i)->playlist(); boost::shared_ptr<Playlist> pl = (*i)->playlist();
if (pl) { if (pl && (what_we_found = pl->copy (selection->time, false)) != 0) {
thelist.push_back (what_we_found);
if ((what_we_found = pl->copy (selection->time, false)) != 0) {
thelist.push_back (what_we_found);
}
} }
} }
NamedSelection* ns; if (!thelist.empty()) {
TreeModel::Row row = *(named_selection_model->append());
ns = new NamedSelection (name, thelist); ArdourPrompter p;
row[named_selection_columns.selection] = ns;
row[named_selection_columns.text] = name; p.set_prompt (_("Name for Chunk:"));
p.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
p.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
p.change_labels (_("Create Chunk"), _("Forget it"));
p.show_all ();
switch (p.run ()) {
case Gtk::RESPONSE_ACCEPT:
p.get_result (name);
if (name.empty()) {
return;
}
break;
default:
return;
}
/* make the one we just added be selected */ new NamedSelection (name, thelist); // creation will add it to the model
named_selection_display.get_selection()->select (row);
/* make the one we just added be selected */
TreeModel::Children::iterator added = named_selection_model->children().end();
--added;
named_selection_display.get_selection()->select (*added);
} else {
error << _("No selectable material found in the currently selected time range") << endmsg;
}
} }

View file

@ -88,7 +88,6 @@ PlaylistSelector::clear_map ()
bool bool
PlaylistSelector::on_unmap_event (GdkEventAny* ev) PlaylistSelector::on_unmap_event (GdkEventAny* ev)
{ {
cerr << "PLselector unmapped\n";
clear_map (); clear_map ();
if (model) { if (model) {
model->clear (); model->clear ();

View file

@ -367,9 +367,9 @@ void
RegionView::set_frame_color () RegionView::set_frame_color ()
{ {
if (_region->opaque()) { if (_region->opaque()) {
fill_opacity = 230; fill_opacity = 130;
} else { } else {
fill_opacity = 100; fill_opacity = 60;
} }
TimeAxisViewItem::set_frame_color (); TimeAxisViewItem::set_frame_color ();

View file

@ -446,7 +446,7 @@ RouteUI::refresh_remote_control_menu ()
RadioMenuItem::Group rc_group; RadioMenuItem::Group rc_group;
CheckMenuItem* rc_active; CheckMenuItem* rc_active;
uint32_t limit = _session.ntracks(); uint32_t limit = _session.ntracks() + _session.nbusses();
char buf[32]; char buf[32];
MenuList& rc_items = remote_control_menu->items(); MenuList& rc_items = remote_control_menu->items();

View file

@ -102,7 +102,7 @@ TimeAxisViewItem::TimeAxisViewItem(const string & it_name, ArdourCanvas::Group&
frame_position = start ; frame_position = start ;
item_duration = duration ; item_duration = duration ;
name_connected = false; name_connected = false;
fill_opacity = 230; fill_opacity = 130;
position_locked = false ; position_locked = false ;
max_item_duration = ARDOUR::max_frames; max_item_duration = ARDOUR::max_frames;
min_item_duration = 0 ; min_item_duration = 0 ;

View file

@ -157,6 +157,7 @@ class Crossfade : public PBD::StatefulDestructible, public boost::enable_shared_
AnchorPoint _anchor_point; AnchorPoint _anchor_point;
bool _follow_overlap; bool _follow_overlap;
bool _fixed; bool _fixed;
int32_t layer_relation;
Curve _fade_in; Curve _fade_in;
Curve _fade_out; Curve _fade_out;
@ -165,7 +166,7 @@ class Crossfade : public PBD::StatefulDestructible, public boost::enable_shared_
void initialize (); void initialize ();
int compute (boost::shared_ptr<ARDOUR::AudioRegion>, boost::shared_ptr<ARDOUR::AudioRegion>, CrossfadeModel); int compute (boost::shared_ptr<ARDOUR::AudioRegion>, boost::shared_ptr<ARDOUR::AudioRegion>, CrossfadeModel);
bool update (bool force); bool update ();
void member_changed (ARDOUR::Change); void member_changed (ARDOUR::Change);
}; };

View file

@ -277,9 +277,14 @@ AudioPlaylist::refresh_dependents (boost::shared_ptr<Region> r)
if ((*x)->involves (ar)) { if ((*x)->involves (ar)) {
if (find (updated.begin(), updated.end(), *x) == updated.end()) { if (find (updated.begin(), updated.end(), *x) == updated.end()) {
if ((*x)->refresh ()) { try {
/* not invalidated by the refresh */ if ((*x)->refresh ()) {
updated.insert (*x); updated.insert (*x);
}
}
catch (Crossfade::NoCrossfadeHere& err) {
// relax, Invalidated during refresh
} }
} }
} }
@ -353,6 +358,7 @@ AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
refresh_dependents (r); refresh_dependents (r);
} }
if (!Config->get_auto_xfade()) { if (!Config->get_auto_xfade()) {
return; return;
} }

View file

@ -87,13 +87,7 @@ Crossfade::Crossfade (boost::shared_ptr<AudioRegion> in, boost::shared_ptr<Audio
_position = position; _position = position;
_anchor_point = ap; _anchor_point = ap;
switch (Config->get_xfade_model()) { _follow_overlap = false;
case ShortCrossfade:
_follow_overlap = false;
break;
default:
_follow_overlap = true;
}
_active = Config->get_xfades_active (); _active = Config->get_xfades_active ();
_fixed = true; _fixed = true;
@ -115,7 +109,6 @@ Crossfade::Crossfade (boost::shared_ptr<AudioRegion> a, boost::shared_ptr<AudioR
_active = act; _active = act;
initialize (); initialize ();
} }
Crossfade::Crossfade (const Playlist& playlist, XMLNode& node) Crossfade::Crossfade (const Playlist& playlist, XMLNode& node)
@ -192,6 +185,7 @@ Crossfade::Crossfade (const Crossfade &orig, boost::shared_ptr<AudioRegion> newi
_in->suspend_fade_in (); _in->suspend_fade_in ();
overlap_type = _in->coverage (_out->position(), _out->last_frame()); overlap_type = _in->coverage (_out->position(), _out->last_frame());
layer_relation = (int32_t) (_in->layer() - _out->layer());
// Let's make sure the fade isn't too long // Let's make sure the fade isn't too long
set_length(_length); set_length(_length);
@ -200,7 +194,6 @@ Crossfade::Crossfade (const Crossfade &orig, boost::shared_ptr<AudioRegion> newi
Crossfade::~Crossfade () Crossfade::~Crossfade ()
{ {
cerr << "Crossfade deleted\n";
notify_callbacks (); notify_callbacks ();
} }
@ -232,186 +225,13 @@ Crossfade::initialize ()
_fade_in.add (_length, 1.0); _fade_in.add (_length, 1.0);
_fade_in.thaw (); _fade_in.thaw ();
_in->StateChanged.connect (sigc::mem_fun (*this, &Crossfade::member_changed)); // _in->StateChanged.connect (sigc::mem_fun (*this, &Crossfade::member_changed));
_out->StateChanged.connect (sigc::mem_fun (*this, &Crossfade::member_changed)); // _out->StateChanged.connect (sigc::mem_fun (*this, &Crossfade::member_changed));
overlap_type = _in->coverage (_out->position(), _out->last_frame()); overlap_type = _in->coverage (_out->position(), _out->last_frame());
layer_relation = (int32_t) (_in->layer() - _out->layer());
} }
int
Crossfade::compute (boost::shared_ptr<AudioRegion> a, boost::shared_ptr<AudioRegion> b, CrossfadeModel model)
{
boost::shared_ptr<AudioRegion> top;
boost::shared_ptr<AudioRegion> bottom;
nframes_t short_xfade_length;
short_xfade_length = _short_xfade_length;
if (a->layer() < b->layer()) {
top = b;
bottom = a;
} else {
top = a;
bottom = b;
}
/* first check for matching ends */
if (top->first_frame() == bottom->first_frame()) {
cerr << "same start\n";
/* Both regions start at the same point */
if (top->last_frame() < bottom->last_frame()) {
/* top ends before bottom, so put an xfade
in at the end of top.
*/
/* [-------- top ---------- ]
* {====== bottom =====================}
*/
_in = bottom;
_out = top;
if (top->last_frame() < short_xfade_length) {
_position = 0;
} else {
_position = top->last_frame() - short_xfade_length;
}
_length = min (short_xfade_length, top->length());
_follow_overlap = false;
_anchor_point = EndOfIn;
_active = true;
_fixed = true;
} else {
/* top ends after (or same time) as bottom - no xfade
*/
/* [-------- top ------------------------ ]
* {====== bottom =====================}
*/
throw NoCrossfadeHere();
}
} else if (top->last_frame() == bottom->last_frame()) {
cerr << "same end\n";
/* Both regions end at the same point */
if (top->first_frame() > bottom->first_frame()) {
/* top starts after bottom, put an xfade in at the
start of top
*/
/* [-------- top ---------- ]
* {====== bottom =====================}
*/
_in = top;
_out = bottom;
_position = top->first_frame();
_length = min (short_xfade_length, top->length());
_follow_overlap = false;
_anchor_point = StartOfIn;
_active = true;
_fixed = true;
} else {
/* top starts before bottom - no xfade
*/
/* [-------- top ------------------------ ]
* {====== bottom =====================}
*/
throw NoCrossfadeHere();
}
} else {
/* OK, time to do more regular overlapping */
OverlapType ot = top->coverage (bottom->first_frame(), bottom->last_frame());
cerr << "ot = " << ot << endl;
switch (ot) {
case OverlapNone:
/* should be NOTREACHED as a precondition of creating
a new crossfade, but we need to handle it here.
*/
cerr << "no sir\n";
throw NoCrossfadeHere();
break;
case OverlapInternal:
case OverlapExternal:
/* should be NOTREACHED because of tests above */
cerr << "nu-uh\n";
throw NoCrossfadeHere();
break;
case OverlapEnd: /* top covers start of bottom but ends within it */
/* [---- top ------------------------]
* { ==== bottom ============ }
*/
_in = bottom;
_out = top;
_anchor_point = StartOfIn;
if (model == FullCrossfade) {
_position = bottom->first_frame(); // "{"
_length = _out->first_frame() + _out->length() - _in->first_frame();
/* leave active alone */
_follow_overlap = true;
} else {
_length = min (short_xfade_length, top->length());
_position = top->last_frame() - _length; // "]" - length
_active = true;
_follow_overlap = false;
}
break;
case OverlapStart: /* top starts within bottom but covers bottom's end */
/* { ==== top ============ }
* [---- bottom -------------------]
*/
_in = top;
_out = bottom;
_position = top->first_frame();
_anchor_point = StartOfIn;
if (model == FullCrossfade) {
_length = _out->first_frame() + _out->length() - _in->first_frame();
/* leave active alone */
_follow_overlap = true;
} else {
_length = min (short_xfade_length, top->length());
_active = true;
_follow_overlap = false;
}
break;
}
}
return 0;
}
nframes_t nframes_t
Crossfade::read_at (Sample *buf, Sample *mixdown_buffer, Crossfade::read_at (Sample *buf, Sample *mixdown_buffer,
float *gain_buffer, nframes_t start, nframes_t cnt, uint32_t chan_n, float *gain_buffer, nframes_t start, nframes_t cnt, uint32_t chan_n,
@ -514,105 +334,269 @@ Crossfade::refresh ()
return false; return false;
} }
if (_in->layer() < _out->layer()) { /* layer ordering cannot change */
cerr << "layer change, invalidated\n";
int32_t new_layer_relation = (int32_t) (_in->layer() - _out->layer());
if (new_layer_relation * layer_relation < 0) { // different sign, layers rotated
Invalidated (shared_from_this()); Invalidated (shared_from_this());
return false; return false;
} }
/* overlap type must be Start, End or External */ OverlapType ot = _in->coverage (_out->first_frame(), _out->last_frame());
OverlapType ot; if (ot == OverlapNone) {
ot = _in->coverage (_out->first_frame(), _out->last_frame());
switch (ot) {
case OverlapNone:
case OverlapInternal:
Invalidated (shared_from_this());
return false;
default:
break;
}
/* overlap type must not have altered */
if (ot != overlap_type) {
Invalidated (shared_from_this()); Invalidated (shared_from_this());
return false; return false;
} }
/* time to update */ bool send_signal;
return update (false); if (ot != overlap_type) {
if (_follow_overlap) {
try {
compute (_in, _out, Config->get_xfade_model());
}
catch (NoCrossfadeHere& err) {
Invalidated (shared_from_this());
return false;
}
send_signal = true;
} else {
Invalidated (shared_from_this());
return false;
}
} else {
send_signal = update ();
}
if (send_signal) {
StateChanged (BoundsChanged); /* EMIT SIGNAL */
}
_in_update = false;
return true;
} }
bool bool
Crossfade::update (bool force) Crossfade::update ()
{ {
nframes_t newlen; nframes_t newlen;
if (_follow_overlap) { if (_follow_overlap) {
newlen = _out->first_frame() + _out->length() - _in->first_frame(); newlen = _out->first_frame() + _out->length() - _in->first_frame();
} else { } else {
newlen = _length; newlen = _length;
} }
if (newlen == 0) { if (newlen == 0) {
Invalidated (shared_from_this()); Invalidated (shared_from_this());
return false; return false;
} }
_in_update = true; _in_update = true;
if (force || (_follow_overlap && newlen != _length) || (_length > newlen)) { if ((_follow_overlap && newlen != _length) || (_length > newlen)) {
double factor = newlen / (double) _length; double factor = newlen / (double) _length;
_fade_out.x_scale (factor); _fade_out.x_scale (factor);
_fade_in.x_scale (factor); _fade_in.x_scale (factor);
_length = newlen; _length = newlen;
} }
switch (_anchor_point) { switch (_anchor_point) {
case StartOfIn: case StartOfIn:
if (_position != _in->first_frame()) { _position = _in->first_frame();
if (_length > _short_xfade_length) {
/* assume FullCrossfade */
_position = _in->first_frame();
} else {
/* assume short xfade */
_position = _out->last_frame() - _length;
}
}
break; break;
case EndOfIn: case EndOfIn:
if (_position != _in->last_frame() - _length) { _position = _in->last_frame() - _length;
_position = _in->last_frame() - _length;
}
break; break;
case EndOfOut: case EndOfOut:
if (_position != _out->last_frame() - _length) { _position = _out->last_frame() - _length;
_position = _out->last_frame() - _length;
}
} }
/* UI's may need to know that the overlap changed even
though the xfade length did not.
*/
StateChanged (BoundsChanged); /* EMIT SIGNAL */
_in_update = false;
return true; return true;
} }
int
Crossfade::compute (boost::shared_ptr<AudioRegion> a, boost::shared_ptr<AudioRegion> b, CrossfadeModel model)
{
boost::shared_ptr<AudioRegion> top;
boost::shared_ptr<AudioRegion> bottom;
nframes_t short_xfade_length;
short_xfade_length = _short_xfade_length;
if (a->layer() < b->layer()) {
top = b;
bottom = a;
} else {
top = a;
bottom = b;
}
/* first check for matching ends */
if (top->first_frame() == bottom->first_frame()) {
/* Both regions start at the same point */
if (top->last_frame() < bottom->last_frame()) {
/* top ends before bottom, so put an xfade
in at the end of top.
*/
/* [-------- top ---------- ]
* {====== bottom =====================}
*/
_in = bottom;
_out = top;
if (top->last_frame() < short_xfade_length) {
_position = 0;
} else {
_position = top->last_frame() - short_xfade_length;
}
_length = min (short_xfade_length, top->length());
_follow_overlap = false;
_anchor_point = EndOfIn;
_active = true;
_fixed = true;
} else {
/* top ends after (or same time) as bottom - no xfade
*/
/* [-------- top ------------------------ ]
* {====== bottom =====================}
*/
throw NoCrossfadeHere();
}
} else if (top->last_frame() == bottom->last_frame()) {
/* Both regions end at the same point */
if (top->first_frame() > bottom->first_frame()) {
/* top starts after bottom, put an xfade in at the
start of top
*/
/* [-------- top ---------- ]
* {====== bottom =====================}
*/
_in = top;
_out = bottom;
_position = top->first_frame();
_length = min (short_xfade_length, top->length());
_follow_overlap = false;
_anchor_point = StartOfIn;
_active = true;
_fixed = true;
} else {
/* top starts before bottom - no xfade
*/
/* [-------- top ------------------------ ]
* {====== bottom =====================}
*/
throw NoCrossfadeHere();
}
} else {
/* OK, time to do more regular overlapping */
OverlapType ot = top->coverage (bottom->first_frame(), bottom->last_frame());
switch (ot) {
case OverlapNone:
/* should be NOTREACHED as a precondition of creating
a new crossfade, but we need to handle it here.
*/
throw NoCrossfadeHere();
break;
case OverlapInternal:
case OverlapExternal:
/* should be NOTREACHED because of tests above */
throw NoCrossfadeHere();
break;
case OverlapEnd: /* top covers start of bottom but ends within it */
/* [---- top ------------------------]
* { ==== bottom ============ }
*/
_in = bottom;
_out = top;
_anchor_point = StartOfIn;
if (model == FullCrossfade) {
_position = bottom->first_frame(); // "{"
_length = _out->first_frame() + _out->length() - _in->first_frame();
/* leave active alone */
_follow_overlap = true;
} else {
_length = min (short_xfade_length, top->length());
_position = top->last_frame() - _length; // "]" - length
_active = true;
_follow_overlap = false;
}
break;
case OverlapStart: /* top starts within bottom but covers bottom's end */
/* { ==== top ============ }
* [---- bottom -------------------]
*/
_in = top;
_out = bottom;
_position = top->first_frame();
_anchor_point = StartOfIn;
if (model == FullCrossfade) {
_length = _out->first_frame() + _out->length() - _in->first_frame();
/* leave active alone */
_follow_overlap = true;
} else {
_length = min (short_xfade_length, top->length());
_active = true;
_follow_overlap = false;
}
break;
}
}
return 0;
}
void void
Crossfade::member_changed (Change what_changed) Crossfade::member_changed (Change what_changed)
{ {
@ -621,7 +605,15 @@ Crossfade::member_changed (Change what_changed)
BoundsChanged); BoundsChanged);
if (what_changed & what_we_care_about) { if (what_changed & what_we_care_about) {
refresh (); try {
if (what_changed & what_we_care_about) {
refresh ();
}
}
catch (NoCrossfadeHere& err) {
// relax, Invalidated inside refresh()
}
} }
} }

View file

@ -211,7 +211,7 @@ setup_hardware_optimization (bool try_optimization)
"cpuid\n" "cpuid\n"
"movl %%edx, %0\n" "movl %%edx, %0\n"
"popl %%ebx\n" "popl %%ebx\n"
: "=l" (use_sse) : "=r" (use_sse)
: :
: "%eax", "%ecx", "%edx", "memory"); : "%eax", "%ecx", "%edx", "memory");
@ -223,7 +223,7 @@ setup_hardware_optimization (bool try_optimization)
"cpuid\n" "cpuid\n"
"movq %%rdx, %0\n" "movq %%rdx, %0\n"
"popq %%rbx\n" "popq %%rbx\n"
: "=l" (use_sse) : "=r" (use_sse)
: :
: "%rax", "%rcx", "%rdx", "memory"); : "%rax", "%rcx", "%rdx", "memory");

View file

@ -40,8 +40,18 @@ NamedSelection::NamedSelection (string n, PlaylistList& l)
{ {
playlists = l; playlists = l;
for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) { for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) {
string new_name;
/* rename playlists to reflect our ownership */
new_name = name;
new_name += '/';
new_name += (*i)->name();
(*i)->set_name (new_name);
(*i)->use(); (*i)->use();
} }
NamedSelectionCreated (this); NamedSelectionCreated (this);
} }
@ -90,7 +100,8 @@ NamedSelection::NamedSelection (Session& session, const XMLNode& node)
NamedSelection::~NamedSelection () NamedSelection::~NamedSelection ()
{ {
for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) { for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) {
(*i)->release(); (*i)->release ();
(*i)->GoingAway ();
} }
} }

View file

@ -209,7 +209,6 @@ Playlist::release ()
} }
} }
void void
Playlist::copy_regions (RegionList& newlist) const Playlist::copy_regions (RegionList& newlist) const
{ {
@ -1709,6 +1708,7 @@ Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region>
region->set_layer (target_layer); region->set_layer (target_layer);
#if 0
/* now check all dependents */ /* now check all dependents */
for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) { for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
@ -1716,6 +1716,7 @@ Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region>
} }
check_dependents (region, false); check_dependents (region, false);
#endif
return 0; return 0;
} }

View file

@ -366,23 +366,34 @@ Session::Session (AudioEngine &eng,
} }
} }
if (control_out_channels) { {
shared_ptr<Route> r (new Route (*this, _("monitor"), -1, control_out_channels, -1, control_out_channels, Route::ControlOut)); /* set up Master Out and Control Out if necessary */
RouteList rl; RouteList rl;
rl.push_back (r); int control_id = 1;
add_routes (rl);
_control_out = r; if (control_out_channels) {
} shared_ptr<Route> r (new Route (*this, _("monitor"), -1, control_out_channels, -1, control_out_channels, Route::ControlOut));
r->set_remote_control_id (control_id++);
if (master_out_channels) {
shared_ptr<Route> r (new Route (*this, _("master"), -1, master_out_channels, -1, master_out_channels, Route::MasterOut)); rl.push_back (r);
RouteList rl; }
rl.push_back (r);
add_routes (rl); if (master_out_channels) {
_master_out = r; shared_ptr<Route> r (new Route (*this, _("master"), -1, master_out_channels, -1, master_out_channels, Route::MasterOut));
} else { r->set_remote_control_id (control_id);
/* prohibit auto-connect to master, because there isn't one */ cerr << "master bus has remote control ID " << r->remote_control_id() << endl;
output_ac = AutoConnectOption (output_ac & ~AutoConnectMaster);
rl.push_back (r);
} else {
/* prohibit auto-connect to master, because there isn't one */
output_ac = AutoConnectOption (output_ac & ~AutoConnectMaster);
}
if (!rl.empty()) {
add_routes (rl);
}
} }
Config->set_input_auto_connect (input_ac); Config->set_input_auto_connect (input_ac);
@ -1695,7 +1706,7 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod
_engine.get_physical_outputs (physoutputs); _engine.get_physical_outputs (physoutputs);
_engine.get_physical_inputs (physinputs); _engine.get_physical_inputs (physinputs);
control_id = 0; control_id = ntracks() + nbusses() + 1;
while (how_many) { while (how_many) {
@ -1784,7 +1795,7 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod
} }
track->DiskstreamChanged.connect (mem_fun (this, &Session::resort_routes)); track->DiskstreamChanged.connect (mem_fun (this, &Session::resort_routes));
track->set_remote_control_id (ntracks() + control_id + 1); track->set_remote_control_id (control_id);
++control_id; ++control_id;
new_routes.push_back (track); new_routes.push_back (track);
@ -1817,6 +1828,7 @@ Session::new_audio_route (int input_channels, int output_channels, uint32_t how_
uint32_t n = 0; uint32_t n = 0;
string port; string port;
RouteList ret; RouteList ret;
uint32_t control_id;
/* count existing audio busses */ /* count existing audio busses */
@ -1837,6 +1849,7 @@ Session::new_audio_route (int input_channels, int output_channels, uint32_t how_
_engine.get_physical_outputs (physoutputs); _engine.get_physical_outputs (physoutputs);
_engine.get_physical_inputs (physinputs); _engine.get_physical_inputs (physinputs);
control_id = ntracks() + nbusses() + 1;
while (how_many) { while (how_many) {
@ -1900,6 +1913,9 @@ Session::new_audio_route (int input_channels, int output_channels, uint32_t how_
bus->set_control_outs (cports); bus->set_control_outs (cports);
} }
bus->set_remote_control_id (control_id);
++control_id;
ret.push_back (bus); ret.push_back (bus);
} }
@ -3788,9 +3804,13 @@ Session::add_named_selection (NamedSelection* named_selection)
named_selections.insert (named_selections.begin(), named_selection); named_selections.insert (named_selections.begin(), named_selection);
} }
for (list<boost::shared_ptr<Playlist> >::iterator i = named_selection->playlists.begin(); i != named_selection->playlists.end(); ++i) {
add_playlist (*i);
}
set_dirty(); set_dirty();
NamedSelectionAdded (); /* EMIT SIGNAL */ NamedSelectionAdded (); /* EMIT SIGNAL */
} }
void void

View file

@ -3242,8 +3242,11 @@ Session::config_changed (const char* parameter_name)
} }
first_file_data_format_reset = false; first_file_data_format_reset = false;
} else if (PARAM_IS ("slave-source")) {
set_slave_source (Config->get_slave_source());
} }
set_dirty (); set_dirty ();
#undef PARAM_IS #undef PARAM_IS

View file

@ -4,5 +4,7 @@
# but somehow scons puts leading /'s on INSTALL_PREFIX and that causes # but somehow scons puts leading /'s on INSTALL_PREFIX and that causes
# wine to be unable to find the .exe.so file # wine to be unable to find the .exe.so file
export GTK_PATH=%PREFIX%/lib/ardour2:$GTK_PATH
LD_LIBRARY_PATH=%PREFIX%/lib/ardour2:$LD_LIBRARY_PATH exec wine %PREFIX%/lib/ardour2/ardour_vst.exe.so "$@" LD_LIBRARY_PATH=%PREFIX%/lib/ardour2:$LD_LIBRARY_PATH exec wine %PREFIX%/lib/ardour2/ardour_vst.exe.so "$@"