#include "ardour/vca.h"
#include "canvas/debug.h"
+#include "canvas/note.h"
#include "canvas/text.h"
#include "widgets/ardour_spacer.h"
, _stepping_axis_view (0)
, quantize_dialog (0)
, _main_menu_disabler (0)
- , myactions (X_("editor"))
{
/* we are a singleton */
VBox* summary_arrows_right = manage (new VBox);
summary_arrows_right->pack_start (*summary_arrow_right);
- Frame* summary_sample = manage (new Frame);
- summary_sample->set_shadow_type (Gtk::SHADOW_ETCHED_IN);
+ Frame* summary_frame = manage (new Frame);
+ summary_frame->set_shadow_type (Gtk::SHADOW_ETCHED_IN);
- summary_sample->add (*_summary);
- summary_sample->show ();
+ summary_frame->add (*_summary);
+ summary_frame->show ();
_summary_hbox.pack_start (*summary_arrows_left, false, false);
- _summary_hbox.pack_start (*summary_sample, true, true);
+ _summary_hbox.pack_start (*summary_frame, true, true);
_summary_hbox.pack_start (*summary_arrows_right, false, false);
if (!ARDOUR::Profile->get_trx()) {
/* register actions now so that set_state() can find them and set toggles/checks etc */
- register_actions ();
load_bindings ();
+ register_actions ();
setup_toolbar ();
setup_fade_images ();
set_grid_to (GridTypeNone);
-
- instant_save ();
}
Editor::~Editor()
void
Editor::instant_save ()
{
- if (!constructed || !ARDOUR_UI::instance()->session_loaded || no_save_instant) {
+ if (!constructed || !_session || no_save_instant) {
return;
}
- if (_session) {
- _session->add_instant_xml(get_state());
- } else {
- Config->add_instant_xml(get_state());
- }
+ _session->add_instant_xml(get_state());
}
void
return _snap_mode;
}
+void
+Editor::show_rulers_for_grid ()
+{
+ /* show appropriate rulers for this grid setting. */
+ if (grid_musical()) {
+ ruler_tempo_action->set_active(true);
+ ruler_meter_action->set_active(true);
+ ruler_bbt_action->set_active(true);
+
+ if (UIConfiguration::instance().get_rulers_follow_grid()) {
+ ruler_timecode_action->set_active(false);
+ ruler_minsec_action->set_active(false);
+ ruler_samples_action->set_active(false);
+ }
+ } else if (_grid_type == GridTypeTimecode) {
+ ruler_timecode_action->set_active(true);
+
+ if (UIConfiguration::instance().get_rulers_follow_grid()) {
+ ruler_tempo_action->set_active(false);
+ ruler_meter_action->set_active(false);
+ ruler_bbt_action->set_active(false);
+ ruler_minsec_action->set_active(false);
+ ruler_samples_action->set_active(false);
+ }
+ } else if (_grid_type == GridTypeMinSec) {
+ ruler_minsec_action->set_active(true);
+
+ if (UIConfiguration::instance().get_rulers_follow_grid()) {
+ ruler_tempo_action->set_active(false);
+ ruler_meter_action->set_active(false);
+ ruler_bbt_action->set_active(false);
+ ruler_timecode_action->set_active(false);
+ ruler_samples_action->set_active(false);
+ }
+ } else if (_grid_type == GridTypeCDFrame) {
+ ruler_cd_marker_action->set_active(true);
+ ruler_minsec_action->set_active(true);
+
+ if (UIConfiguration::instance().get_rulers_follow_grid()) {
+ ruler_tempo_action->set_active(false);
+ ruler_meter_action->set_active(false);
+ ruler_bbt_action->set_active(false);
+ ruler_timecode_action->set_active(false);
+ ruler_samples_action->set_active(false);
+ }
+ }
+}
+
void
Editor::set_grid_to (GridType gt)
{
unsigned int grid_ind = (unsigned int)gt;
- if (internal_editing()) {
+ if (internal_editing() && UIConfiguration::instance().get_grid_follows_internal()) {
internal_grid_type = gt;
} else {
pre_internal_grid_type = gt;
grid_type_selector.set_text (str);
}
- /* show appropriate rulers for this grid setting.
- * TODO: perhaps make this optional.
- * Currently this is 'required' because the RULER calculates the grid_marks which will be used by grid_lines
- */
- if (grid_musical()) {
- ruler_tempo_action->set_active(true);
- ruler_meter_action->set_active(true);
-
- ruler_bbt_action->set_active(true);
- ruler_timecode_action->set_active(false);
- ruler_minsec_action->set_active(false);
- ruler_samples_action->set_active(false);
- } else if (_grid_type == GridTypeTimecode) {
- ruler_tempo_action->set_active(false);
- ruler_meter_action->set_active(false);
-
- ruler_bbt_action->set_active(false);
- ruler_timecode_action->set_active(true);
- ruler_minsec_action->set_active(false);
- ruler_samples_action->set_active(false);
- } else if (_grid_type == GridTypeMinSec) {
- ruler_tempo_action->set_active(false);
- ruler_meter_action->set_active(false);
-
- ruler_bbt_action->set_active(false);
- ruler_timecode_action->set_active(false);
- ruler_minsec_action->set_active(true);
- ruler_samples_action->set_active(false);
- } else if (_grid_type == GridTypeCDFrame) {
- ruler_tempo_action->set_active(false);
- ruler_meter_action->set_active(false);
-
- ruler_bbt_action->set_active(false);
- ruler_timecode_action->set_active(false);
- ruler_minsec_action->set_active(true);
-
- ruler_cd_marker_action->set_active(true);
- ruler_samples_action->set_active(false);
+ if (UIConfiguration::instance().get_show_grids_ruler()) {
+ show_rulers_for_grid ();
}
instant_save ();
}
}
- return LuaInstance::instance()->set_state(node);
+ return 0;
}
XMLNode&
node->set_property ("nudge-clock-value", nudge_clock->current_duration());
- node->add_child_nocopy (LuaInstance::instance()->get_action_state());
- node->add_child_nocopy (LuaInstance::instance()->get_hook_state());
node->add_child_nocopy (_locations->get_state ());
return *node;
* @param event Event to get current key modifier information from, or 0.
*/
void
-Editor::snap_to_with_modifier (MusicSample& start, GdkEvent const * event, RoundMode direction, SnapPref pref, bool for_mark)
+Editor::snap_to_with_modifier (MusicSample& start, GdkEvent const * event, RoundMode direction, SnapPref pref)
{
if (!_session || !event) {
return;
if (ArdourKeyboard::indicates_snap (event->button.state)) {
if (_snap_mode == SnapOff) {
- snap_to_internal (start, direction, pref, for_mark);
+ snap_to_internal (start, direction, pref);
} else {
start.set (start.sample, 0);
}
} else {
if (_snap_mode != SnapOff) {
- snap_to_internal (start, direction, pref, for_mark);
+ snap_to_internal (start, direction, pref);
} else if (ArdourKeyboard::indicates_snap_delta (event->button.state)) {
/* SnapOff, but we pressed the snap_delta modifier */
- snap_to_internal (start, direction, pref, for_mark);
+ snap_to_internal (start, direction, pref);
} else {
start.set (start.sample, 0);
}
}
void
-Editor::snap_to (MusicSample& start, RoundMode direction, SnapPref pref, bool for_mark, bool ensure_snap)
+Editor::snap_to (MusicSample& start, RoundMode direction, SnapPref pref, bool ensure_snap)
{
if (!_session || (_snap_mode == SnapOff && !ensure_snap)) {
start.set (start.sample, 0);
return;
}
- snap_to_internal (start, direction, pref, for_mark, ensure_snap);
+ snap_to_internal (start, direction, pref, ensure_snap);
}
void
test = max_samplepos; // reset this so it doesn't get accidentally reused
}
-samplepos_t
-Editor::snap_to_grid (vector<ArdourCanvas::Ruler::Mark> marks, samplepos_t presnap, RoundMode direction)
+MusicSample
+Editor::snap_to_timecode (MusicSample presnap, RoundMode direction, SnapPref gpref)
{
- if (marks.empty()) return presnap;
+ samplepos_t start = presnap.sample;
+ const samplepos_t one_timecode_second = (samplepos_t)(rint(_session->timecode_frames_per_second()) * _session->samples_per_timecode_frame());
+ samplepos_t one_timecode_minute = (samplepos_t)(rint(_session->timecode_frames_per_second()) * _session->samples_per_timecode_frame() * 60);
- samplepos_t before;
- samplepos_t after;
- samplepos_t test = presnap;
+ TimecodeRulerScale scale = (gpref != SnapToGrid_Unscaled) ? timecode_ruler_scale : timecode_show_samples;
- before = after = max_samplepos;
+ switch (scale) {
+ case timecode_show_bits:
+ case timecode_show_samples:
+ if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
+ fmod((double)start, (double)_session->samples_per_timecode_frame()) == 0) {
+ /* start is already on a whole timecode frame, do nothing */
+ } else if (((direction == 0) && (fmod((double)start, (double)_session->samples_per_timecode_frame()) > (_session->samples_per_timecode_frame() / 2))) || (direction > 0)) {
+ start = (samplepos_t) (ceil ((double) start / _session->samples_per_timecode_frame()) * _session->samples_per_timecode_frame());
+ } else {
+ start = (samplepos_t) (floor ((double) start / _session->samples_per_timecode_frame()) * _session->samples_per_timecode_frame());
+ }
+ break;
- /* get marks to either side of presnap */
- vector<ArdourCanvas::Ruler::Mark>::const_iterator m = marks.begin();
- while (m != marks.end() && (m->position < presnap)) {
- ++m;
- }
+ case timecode_show_seconds:
+ if (_session->config.get_timecode_offset_negative()) {
+ start += _session->config.get_timecode_offset ();
+ } else {
+ start -= _session->config.get_timecode_offset ();
+ }
+ if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
+ (start % one_timecode_second == 0)) {
+ /* start is already on a whole second, do nothing */
+ } else if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) {
+ start = (samplepos_t) ceil ((double) start / one_timecode_second) * one_timecode_second;
+ } else {
+ start = (samplepos_t) floor ((double) start / one_timecode_second) * one_timecode_second;
+ }
- if (m == marks.end ()) {
- /* ran out of marks */
- before = marks.back().position;
+ if (_session->config.get_timecode_offset_negative()) {
+ start -= _session->config.get_timecode_offset ();
+ } else {
+ start += _session->config.get_timecode_offset ();
+ }
+ break;
+
+ case timecode_show_minutes:
+ case timecode_show_hours:
+ case timecode_show_many_hours:
+ if (_session->config.get_timecode_offset_negative()) {
+ start += _session->config.get_timecode_offset ();
+ } else {
+ start -= _session->config.get_timecode_offset ();
+ }
+ if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
+ (start % one_timecode_minute == 0)) {
+ /* start is already on a whole minute, do nothing */
+ } else if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) {
+ start = (samplepos_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute;
+ } else {
+ start = (samplepos_t) floor ((double) start / one_timecode_minute) * one_timecode_minute;
+ }
+ if (_session->config.get_timecode_offset_negative()) {
+ start -= _session->config.get_timecode_offset ();
+ } else {
+ start += _session->config.get_timecode_offset ();
+ }
+ break;
+ default:
+ fatal << "Editor::smpte_snap_to_internal() called with non-timecode snap type!" << endmsg;
}
- after = m->position;
+ MusicSample ret(start,0);
+ return ret;
+}
- if (m != marks.begin ()) {
- --m;
- before = m->position;
- }
+MusicSample
+Editor::snap_to_minsec (MusicSample presnap, RoundMode direction, SnapPref gpref)
+{
+ MusicSample ret(presnap);
+
+ const samplepos_t one_second = _session->sample_rate();
+ const samplepos_t one_minute = one_second * 60;
+ const samplepos_t one_hour = one_minute * 60;
- if (before == max_samplepos && after == max_samplepos) {
- /* No grid to snap to, so just don't snap */
- return presnap;
- } else if (before == max_samplepos) {
- test = after;
- } else if (after == max_samplepos) {
- test = before;
- } else {
- if ((direction == RoundUpMaybe || direction == RoundUpAlways))
- test = after;
- else if ((direction == RoundDownMaybe || direction == RoundDownAlways))
- test = before;
- else if (direction == 0) {
- if ((presnap - before) < (after - presnap)) {
- test = before;
+ MinsecRulerScale scale = (gpref != SnapToGrid_Unscaled) ? minsec_ruler_scale : minsec_show_seconds;
+
+ switch (scale) {
+ case minsec_show_msecs:
+ case minsec_show_seconds: {
+ if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
+ presnap.sample % one_second == 0) {
+ /* start is already on a whole second, do nothing */
+ } else if (((direction == 0) && (presnap.sample % one_second > one_second / 2)) || (direction > 0)) {
+ ret.sample = (samplepos_t) ceil ((double) presnap.sample / one_second) * one_second;
} else {
- test = after;
+ ret.sample = (samplepos_t) floor ((double) presnap.sample / one_second) * one_second;
+ }
+ } break;
+
+ case minsec_show_minutes: {
+ if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
+ presnap.sample % one_minute == 0) {
+ /* start is already on a whole minute, do nothing */
+ } else if (((direction == 0) && (presnap.sample % one_minute > one_minute / 2)) || (direction > 0)) {
+ ret.sample = (samplepos_t) ceil ((double) presnap.sample / one_minute) * one_minute;
+ } else {
+ ret.sample = (samplepos_t) floor ((double) presnap.sample / one_minute) * one_minute;
}
+ } break;
+
+ default: {
+ if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
+ presnap.sample % one_hour == 0) {
+ /* start is already on a whole hour, do nothing */
+ } else if (((direction == 0) && (presnap.sample % one_hour > one_hour / 2)) || (direction > 0)) {
+ ret.sample = (samplepos_t) ceil ((double) presnap.sample / one_hour) * one_hour;
+ } else {
+ ret.sample = (samplepos_t) floor ((double) presnap.sample / one_hour) * one_hour;
+ }
+ } break;
+ }
+
+ return ret;
+}
+
+MusicSample
+Editor::snap_to_cd_frames (MusicSample presnap, RoundMode direction, SnapPref gpref)
+{
+ if ((gpref != SnapToGrid_Unscaled) && (minsec_ruler_scale != minsec_show_msecs)) {
+ return snap_to_minsec (presnap, direction, gpref);
+ }
+
+ const samplepos_t one_second = _session->sample_rate();
+
+ MusicSample ret(presnap);
+
+ if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
+ presnap.sample % (one_second/75) == 0) {
+ /* start is already on a whole CD sample, do nothing */
+ } else if (((direction == 0) && (presnap.sample % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
+ ret.sample = (samplepos_t) ceil ((double) presnap.sample / (one_second / 75)) * (one_second / 75);
+ } else {
+ ret.sample = (samplepos_t) floor ((double) presnap.sample / (one_second / 75)) * (one_second / 75);
+ }
+
+ return ret;
+}
+
+MusicSample
+Editor::snap_to_bbt (MusicSample presnap, RoundMode direction, SnapPref gpref)
+{
+ MusicSample ret(presnap);
+
+ if (gpref != SnapToGrid_Unscaled) { // use the visual grid lines which are limited by the zoom scale that the user selected
+
+ int divisor = 2;
+ switch (_grid_type) {
+ case GridTypeBeatDiv3:
+ case GridTypeBeatDiv6:
+ case GridTypeBeatDiv12:
+ case GridTypeBeatDiv24:
+ divisor = 3;
+ break;
+ case GridTypeBeatDiv5:
+ case GridTypeBeatDiv10:
+ case GridTypeBeatDiv20:
+ divisor = 5;
+ break;
+ case GridTypeBeatDiv7:
+ case GridTypeBeatDiv14:
+ case GridTypeBeatDiv28:
+ divisor = 7;
+ break;
+ default:
+ divisor = 2;
+ };
+
+ BBTRulerScale scale = bbt_ruler_scale;
+ switch (scale) {
+ case bbt_show_many:
+ case bbt_show_64:
+ case bbt_show_16:
+ case bbt_show_4:
+ case bbt_show_1:
+ ret = _session->tempo_map().round_to_bar (presnap.sample, direction);
+ break;
+ case bbt_show_quarters:
+ ret = _session->tempo_map().round_to_beat (presnap.sample, direction);
+ break;
+ case bbt_show_eighths:
+ ret = _session->tempo_map().round_to_quarter_note_subdivision (presnap.sample, 1 * divisor, direction);
+ break;
+ case bbt_show_sixteenths:
+ ret = _session->tempo_map().round_to_quarter_note_subdivision (presnap.sample, 2 * divisor, direction);
+ break;
+ case bbt_show_thirtyseconds:
+ ret = _session->tempo_map().round_to_quarter_note_subdivision (presnap.sample, 4 * divisor, direction);
+ break;
}
+ } else {
+ ret = _session->tempo_map().round_to_quarter_note_subdivision (presnap.sample, get_grid_beat_divisions(_grid_type), direction);
}
+
+ return ret;
+}
- return test;
+ARDOUR::MusicSample
+Editor::snap_to_grid (MusicSample presnap, RoundMode direction, SnapPref gpref)
+{
+ MusicSample ret(presnap);
+
+ if (grid_musical()) {
+ ret = snap_to_bbt (presnap, direction, gpref);
+ }
+
+ switch (_grid_type) {
+ case GridTypeTimecode:
+ ret = snap_to_timecode(presnap, direction, gpref);
+ break;
+ case GridTypeMinSec:
+ ret = snap_to_minsec(presnap, direction, gpref);
+ break;
+ case GridTypeCDFrame:
+ ret = snap_to_cd_frames(presnap, direction, gpref);
+ break;
+ default:
+ {}
+ };
+
+ return ret;
}
samplepos_t
-Editor::marker_snap_to_internal (samplepos_t presnap, RoundMode direction)
+Editor::snap_to_marker (samplepos_t presnap, RoundMode direction)
{
samplepos_t before;
samplepos_t after;
}
void
-Editor::snap_to_internal (MusicSample& start, RoundMode direction, SnapPref pref, bool for_mark, bool ensure_snap)
+Editor::snap_to_internal (MusicSample& start, RoundMode direction, SnapPref pref, bool ensure_snap)
{
const samplepos_t presnap = start.sample;
samplepos_t best = max_samplepos; // this records the best snap-result we've found so far
/* check snap-to-marker */
- if ( (pref == SnapToAny) && UIConfiguration::instance().get_snap_to_marks()) {
- if (for_mark) {
- return;
- }
-
- test = marker_snap_to_internal (presnap, direction);
+ if ((pref == SnapToAny_Visual) && UIConfiguration::instance().get_snap_to_marks()) {
+ test = snap_to_marker (presnap, direction);
check_best_snap(presnap, test, dist, best);
}
/* check snap-to-region-{start/end/sync} */
- if (
- (pref == SnapToAny) &&
+ if (
+ (pref == SnapToAny_Visual) &&
(UIConfiguration::instance().get_snap_to_region_start() || UIConfiguration::instance().get_snap_to_region_end() || UIConfiguration::instance().get_snap_to_region_sync())
) {
if (!region_boundary_cache.empty()) {
test = *next;
}
}
-
+
}
check_best_snap(presnap, test, dist, best);
/* check Grid */
if (UIConfiguration::instance().get_snap_to_grid() && (_grid_type != GridTypeNone)) {
- test = snap_to_grid (grid_marks, presnap, direction);
- check_best_snap(presnap, test, dist, best);
+ MusicSample pre(presnap, 0);
+ MusicSample post = snap_to_grid (pre, direction, pref);
+ check_best_snap(presnap, post.sample, dist, best);
}
/* now check "magnetic" state: is the grid within reasonable on-screen distance to trigger a snap?
grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeTimecode], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeTimecode)));
grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeMinSec], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeMinSec)));
grid_type_selector.AddMenuElem (MenuElem (grid_type_strings[(int)GridTypeCDFrame], sigc::bind (sigc::mem_fun(*this, &Editor::grid_type_selection_done), (GridType) GridTypeCDFrame)));
-
- set_size_request_to_display_given_text (grid_type_selector, _("Long Grid"), COMBO_TRIANGLE_WIDTH, 2); // problem: some of the rarely-used grid names are very long. Just do something arbitary, translators: rename this if needed
}
void
case GridTypeBeatDiv4: return 4;
case GridTypeBeatDiv3: return 3;
case GridTypeBeatDiv2: return 2;
+ case GridTypeBeat: return 1;
+ case GridTypeBar: return 1;
case GridTypeNone: return 0;
case GridTypeTimecode: return 0;
_last_update_time = 0;
}
- if (!_session->transport_rolling ()) {
+ if (!_session->transport_rolling () || _session->is_auditioning ()) {
/* Do not interpolate the playhead position; just set it */
_last_update_time = 0;
}
if (_verbose_cursor) {
playhead_cursor->set_sensitive (UIConfiguration::instance().get_draggable_playhead());
}
+ } else if (parameter == "use-note-bars-for-velocity") {
+ ArdourCanvas::Note::set_show_velocity_bars (UIConfiguration::instance().get_use_note_bars_for_velocity());
+ _track_canvas->request_redraw (_track_canvas->visible_area());
+ } else if (parameter == "use-note-color-for-velocity") {
+ /* handled individually by each MidiRegionView */
}
}