mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-06 06:44:57 +01:00
Tempo ramps - add visualtempo curve, dragging bbt or music rulers with constraint modifier dilates previous tempo.
This commit is contained in:
parent
652a59b317
commit
86b0268e8b
15 changed files with 576 additions and 14 deletions
|
|
@ -60,6 +60,7 @@
|
|||
#include "editor_items.h"
|
||||
#include "region_selection.h"
|
||||
#include "selection_memento.h"
|
||||
#include "tempo_curve.h"
|
||||
|
||||
namespace Gtkmm2ext {
|
||||
class Bindings;
|
||||
|
|
@ -1565,6 +1566,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
|
|||
bool canvas_stream_view_event (GdkEvent* event,ArdourCanvas::Item*, RouteTimeAxisView*);
|
||||
bool canvas_marker_event (GdkEvent* event,ArdourCanvas::Item*, ArdourMarker*);
|
||||
bool canvas_tempo_marker_event (GdkEvent* event,ArdourCanvas::Item*, TempoMarker*);
|
||||
bool canvas_tempo_curve_event (GdkEvent* event,ArdourCanvas::Item*, TempoCurve*);
|
||||
bool canvas_meter_marker_event (GdkEvent* event,ArdourCanvas::Item*, MeterMarker*);
|
||||
bool canvas_automation_track_event(GdkEvent* event, ArdourCanvas::Item*, AutomationTimeAxisView*);
|
||||
bool canvas_note_event (GdkEvent* event, ArdourCanvas::Item *);
|
||||
|
|
@ -1696,6 +1698,9 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
|
|||
typedef std::list<ArdourMarker*> Marks;
|
||||
Marks metric_marks;
|
||||
|
||||
typedef std::list<TempoCurve*> Curves;
|
||||
Curves tempo_curves;
|
||||
|
||||
void remove_metric_marks ();
|
||||
void draw_metric_marks (const ARDOUR::Metrics& metrics);
|
||||
|
||||
|
|
|
|||
|
|
@ -1013,6 +1013,12 @@ Editor::canvas_tempo_marker_event (GdkEvent *event, ArdourCanvas::Item* item, Te
|
|||
return typed_event (item, event, TempoMarkerItem);
|
||||
}
|
||||
|
||||
bool
|
||||
Editor::canvas_tempo_curve_event (GdkEvent *event, ArdourCanvas::Item* item, TempoCurve* /*marker*/)
|
||||
{
|
||||
return typed_event (item, event, TempoCurveItem);
|
||||
}
|
||||
|
||||
bool
|
||||
Editor::canvas_meter_marker_event (GdkEvent *event, ArdourCanvas::Item* item, MeterMarker* /*marker*/)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3476,6 +3476,69 @@ TempoMarkerDrag::aborted (bool moved)
|
|||
}
|
||||
}
|
||||
|
||||
BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i)
|
||||
: Drag (e, i)
|
||||
, before_state (0)
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::Drags, "New BBTRulerDrag\n");
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
BBTRulerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
|
||||
{
|
||||
Drag::start_grab (event, cursor);
|
||||
show_verbose_cursor_time (adjusted_current_frame (event));
|
||||
}
|
||||
|
||||
void
|
||||
BBTRulerDrag::setup_pointer_frame_offset ()
|
||||
{
|
||||
_pointer_frame_offset = 0;
|
||||
}
|
||||
|
||||
void
|
||||
BBTRulerDrag::motion (GdkEvent* event, bool first_move)
|
||||
{
|
||||
if (first_move) {
|
||||
TempoMap& map (_editor->session()->tempo_map());
|
||||
/* get current state */
|
||||
before_state = &map.get_state();
|
||||
_editor->begin_reversible_command (_("dilate tempo"));
|
||||
}
|
||||
|
||||
framepos_t const pf = adjusted_current_frame (event, false);
|
||||
|
||||
if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::constraint_modifier())) {
|
||||
/* adjust previous tempo to match pointer frame */
|
||||
_editor->session()->tempo_map().gui_dilate_tempo (last_pointer_frame(), pf);
|
||||
}
|
||||
show_verbose_cursor_time (pf);
|
||||
}
|
||||
|
||||
void
|
||||
BBTRulerDrag::finished (GdkEvent* event, bool movement_occurred)
|
||||
{
|
||||
if (!movement_occurred) {
|
||||
return;
|
||||
}
|
||||
|
||||
TempoMap& map (_editor->session()->tempo_map());
|
||||
|
||||
XMLNode &after = map.get_state();
|
||||
_editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
|
||||
_editor->commit_reversible_command ();
|
||||
}
|
||||
|
||||
void
|
||||
BBTRulerDrag::aborted (bool moved)
|
||||
{
|
||||
if (moved) {
|
||||
_editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
|
||||
: Drag (e, &c.track_canvas_item(), false)
|
||||
, _cursor (c)
|
||||
|
|
|
|||
|
|
@ -741,6 +741,31 @@ private:
|
|||
XMLNode* before_state;
|
||||
};
|
||||
|
||||
/** BBT Ruler drag */
|
||||
class BBTRulerDrag : public Drag
|
||||
{
|
||||
public:
|
||||
BBTRulerDrag (Editor *, ArdourCanvas::Item *);
|
||||
|
||||
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
|
||||
void motion (GdkEvent *, bool);
|
||||
void finished (GdkEvent *, bool);
|
||||
void aborted (bool);
|
||||
|
||||
bool allow_vertical_autoscroll () const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool y_movement_matters () const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void setup_pointer_frame_offset ();
|
||||
|
||||
private:
|
||||
|
||||
XMLNode* before_state;
|
||||
};
|
||||
|
||||
/** Drag of the playhead cursor */
|
||||
class CursorDrag : public Drag
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ enum ItemType {
|
|||
GainLineItem,
|
||||
AutomationLineItem,
|
||||
MeterMarkerItem,
|
||||
TempoCurveItem,
|
||||
TempoMarkerItem,
|
||||
MeterBarItem,
|
||||
TempoBarItem,
|
||||
|
|
|
|||
|
|
@ -711,13 +711,17 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
|
|||
|
||||
case MarkerBarItem:
|
||||
case TempoBarItem:
|
||||
case TempoCurveItem:
|
||||
case MeterBarItem:
|
||||
case TimecodeRulerItem:
|
||||
case SamplesRulerItem:
|
||||
case MinsecRulerItem:
|
||||
case BBTRulerItem:
|
||||
if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
|
||||
if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)
|
||||
&& !Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier())) {
|
||||
_drags->set (new CursorDrag (this, *playhead_cursor, false), event);
|
||||
} else if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier())) {
|
||||
_drags->set (new BBTRulerDrag (this, item), event);
|
||||
}
|
||||
return true;
|
||||
break;
|
||||
|
|
@ -1333,7 +1337,6 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
|
|||
case RegionItem:
|
||||
show_region_properties ();
|
||||
break;
|
||||
|
||||
case TempoMarkerItem: {
|
||||
ArdourMarker* marker;
|
||||
TempoMarker* tempo_marker;
|
||||
|
|
@ -1438,6 +1441,7 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
|
|||
case TransportMarkerBarItem:
|
||||
case CdMarkerBarItem:
|
||||
case TempoBarItem:
|
||||
case TempoCurveItem:
|
||||
case MeterBarItem:
|
||||
case VideoBarItem:
|
||||
case TimecodeRulerItem:
|
||||
|
|
@ -1547,8 +1551,8 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
|
|||
mouse_add_new_marker (where, true);
|
||||
}
|
||||
return true;
|
||||
|
||||
case TempoBarItem:
|
||||
case TempoCurveItem:
|
||||
if (!_dragging_playhead) {
|
||||
snap_to_with_modifier (where, event);
|
||||
mouse_add_new_tempo_event (where);
|
||||
|
|
|
|||
|
|
@ -69,6 +69,11 @@ Editor::remove_metric_marks ()
|
|||
delete_when_idle (*x);
|
||||
}
|
||||
metric_marks.clear ();
|
||||
|
||||
for (Curves::iterator x = tempo_curves.begin(); x != tempo_curves.end(); ++x) {
|
||||
delete (*x);
|
||||
}
|
||||
tempo_curves.clear ();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -78,6 +83,8 @@ Editor::draw_metric_marks (const Metrics& metrics)
|
|||
const MeterSection *ms;
|
||||
const TempoSection *ts;
|
||||
char buf[64];
|
||||
double max_tempo = 0.0;
|
||||
double min_tempo = DBL_MAX;
|
||||
|
||||
remove_metric_marks ();
|
||||
|
||||
|
|
@ -89,15 +96,36 @@ Editor::draw_metric_marks (const Metrics& metrics)
|
|||
*(const_cast<MeterSection*>(ms))));
|
||||
} else if ((ts = dynamic_cast<const TempoSection*>(*i)) != 0) {
|
||||
if (UIConfiguration::instance().get_allow_non_quarter_pulse()) {
|
||||
snprintf (buf, sizeof (buf), "%.2f/%.0f", ts->beats_per_minute(), ts->note_type());
|
||||
snprintf (buf, sizeof (buf), "%.3f/%.0f", ts->beats_per_minute(), ts->note_type());
|
||||
} else {
|
||||
snprintf (buf, sizeof (buf), "%.2f", ts->beats_per_minute());
|
||||
snprintf (buf, sizeof (buf), "%.3f", ts->beats_per_minute());
|
||||
}
|
||||
if (ts->beats_per_minute() > max_tempo) {
|
||||
max_tempo = ts->beats_per_minute();
|
||||
}
|
||||
if (ts->beats_per_minute() < min_tempo) {
|
||||
min_tempo = ts->beats_per_minute();
|
||||
}
|
||||
tempo_curves.push_back (new TempoCurve (*this, *tempo_group, UIConfiguration::instance().color ("range drag rect"),
|
||||
*(const_cast<TempoSection*>(ts)), ts->frame(), false));
|
||||
metric_marks.push_back (new TempoMarker (*this, *tempo_group, UIConfiguration::instance().color ("tempo marker"), buf,
|
||||
*(const_cast<TempoSection*>(ts))));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
for (Curves::iterator x = tempo_curves.begin(); x != tempo_curves.end(); ) {
|
||||
Curves::iterator tmp = x;
|
||||
(*x)->set_max_tempo (max_tempo);
|
||||
(*x)->set_min_tempo (min_tempo);
|
||||
++tmp;
|
||||
if (tmp != tempo_curves.end()) {
|
||||
(*x)->set_position ((*x)->tempo().frame(), (*tmp)->tempo().frame());
|
||||
} else {
|
||||
(*x)->set_position ((*x)->tempo().frame(), UINT32_MAX);
|
||||
}
|
||||
++x;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -122,6 +150,12 @@ Editor::tempo_map_changed (const PropertyChange& /*ignored*/)
|
|||
update_tempo_based_rulers (grid);
|
||||
}
|
||||
|
||||
struct CurveComparator {
|
||||
bool operator() (TempoCurve const * a, TempoCurve const * b) {
|
||||
return a->position() < b->position();
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
Editor::marker_position_changed ()
|
||||
{
|
||||
|
|
@ -138,14 +172,21 @@ Editor::marker_position_changed ()
|
|||
MeterMarker* meter_marker;
|
||||
const TempoSection *ts;
|
||||
const MeterSection *ms;
|
||||
|
||||
double max_tempo = 0.0;
|
||||
double min_tempo = DBL_MAX;
|
||||
for (Marks::iterator x = metric_marks.begin(); x != metric_marks.end(); ++x) {
|
||||
if ((tempo_marker = dynamic_cast<TempoMarker*> (*x)) != 0) {
|
||||
if ((ts = &tempo_marker->tempo()) != 0) {
|
||||
tempo_marker->set_position (ts->frame ());
|
||||
char buf[64];
|
||||
snprintf (buf, sizeof (buf), "%.2f", ts->beats_per_minute());
|
||||
snprintf (buf, sizeof (buf), "%.3f", ts->beats_per_minute());
|
||||
tempo_marker->set_name (buf);
|
||||
if (ts->beats_per_minute() > max_tempo) {
|
||||
max_tempo = ts->beats_per_minute();
|
||||
}
|
||||
if (ts->beats_per_minute() < min_tempo) {
|
||||
min_tempo = ts->beats_per_minute();
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((meter_marker = dynamic_cast<MeterMarker*> (*x)) != 0) {
|
||||
|
|
@ -154,6 +195,20 @@ Editor::marker_position_changed ()
|
|||
}
|
||||
}
|
||||
}
|
||||
tempo_curves.sort (CurveComparator());
|
||||
for (Curves::iterator x = tempo_curves.begin(); x != tempo_curves.end(); ) {
|
||||
Curves::iterator tmp = x;
|
||||
(*x)->set_max_tempo (max_tempo);
|
||||
(*x)->set_min_tempo (min_tempo);
|
||||
++tmp;
|
||||
if (tmp != tempo_curves.end()) {
|
||||
(*x)->set_position ((*x)->tempo().frame(), (*tmp)->tempo().frame());
|
||||
} else {
|
||||
(*x)->set_position ((*x)->tempo().frame(), UINT32_MAX);
|
||||
}
|
||||
++x;
|
||||
}
|
||||
|
||||
std::vector<TempoMap::BBTPoint> grid;
|
||||
compute_current_bbt_points (grid, leftmost_frame, leftmost_frame + current_page_samples());
|
||||
draw_measures (grid);
|
||||
|
|
@ -251,9 +306,6 @@ Editor::mouse_add_new_meter_event (framepos_t frame)
|
|||
TempoMap& map(_session->tempo_map());
|
||||
MeterDialog meter_dialog (map, frame, _("add"));
|
||||
|
||||
//this causes compiz to display no border..
|
||||
//meter_dialog.signal_realize().connect (sigc::bind (sigc::ptr_fun (set_decoration), &meter_dialog, Gdk::WMDecoration (Gdk::DECOR_BORDER|Gdk::DECOR_RESIZEH)));
|
||||
|
||||
switch (meter_dialog.run ()) {
|
||||
case RESPONSE_ACCEPT:
|
||||
break;
|
||||
|
|
@ -271,11 +323,13 @@ Editor::mouse_add_new_meter_event (framepos_t frame)
|
|||
|
||||
begin_reversible_command (_("add meter mark"));
|
||||
XMLNode &before = map.get_state();
|
||||
|
||||
if (meter_dialog.get_lock_style() == MusicTime) {
|
||||
map.add_meter (Meter (bpb, note_type), map.bbt_to_beats (requested), requested);
|
||||
} else {
|
||||
map.add_meter (Meter (bpb, note_type), frame, meter_dialog.get_lock_style());
|
||||
map.add_meter (Meter (bpb, note_type), map.frame_time (requested), map.bbt_to_beats (requested), requested);
|
||||
}
|
||||
|
||||
_session->add_command(new MementoCommand<TempoMap>(map, &before, &map.get_state()));
|
||||
commit_reversible_command ();
|
||||
|
||||
|
|
|
|||
|
|
@ -150,6 +150,7 @@ setup_gtk_ardour_enums ()
|
|||
REGISTER_ENUM (GainLineItem);
|
||||
REGISTER_ENUM (AutomationLineItem);
|
||||
REGISTER_ENUM (MeterMarkerItem);
|
||||
REGISTER_ENUM (TempoCurveItem);
|
||||
REGISTER_ENUM (TempoMarkerItem);
|
||||
REGISTER_ENUM (MeterBarItem);
|
||||
REGISTER_ENUM (TempoBarItem);
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ class PluginUIWindow;
|
|||
class RegionView;
|
||||
class RouteTimeAxisView;
|
||||
class Selection;
|
||||
class TempoCurve;
|
||||
class TempoMarker;
|
||||
class TimeAxisView;
|
||||
class TimeAxisViewItem;
|
||||
|
|
@ -346,6 +347,7 @@ class PublicEditor : public Gtkmm2ext::Tabbable {
|
|||
virtual bool canvas_marker_event (GdkEvent* event, ArdourCanvas::Item*, ArdourMarker*) = 0;
|
||||
virtual bool canvas_videotl_bar_event (GdkEvent* event, ArdourCanvas::Item*) = 0;
|
||||
virtual bool canvas_tempo_marker_event (GdkEvent* event, ArdourCanvas::Item*, TempoMarker*) = 0;
|
||||
virtual bool canvas_tempo_curve_event (GdkEvent* event, ArdourCanvas::Item*, TempoCurve*) = 0;
|
||||
virtual bool canvas_meter_marker_event (GdkEvent* event, ArdourCanvas::Item*, MeterMarker*) = 0;
|
||||
virtual bool canvas_automation_track_event(GdkEvent* event, ArdourCanvas::Item*, AutomationTimeAxisView*) = 0;
|
||||
|
||||
|
|
|
|||
194
gtk2_ardour/tempo_curve.cc
Normal file
194
gtk2_ardour/tempo_curve.cc
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
#include <sigc++/bind.h>
|
||||
#include "ardour/tempo.h"
|
||||
|
||||
#include "canvas/rectangle.h"
|
||||
#include "canvas/container.h"
|
||||
#include "canvas/curve.h"
|
||||
#include "canvas/polygon.h"
|
||||
#include "canvas/canvas.h"
|
||||
#include "canvas/debug.h"
|
||||
|
||||
#include "ui_config.h"
|
||||
|
||||
#include "tempo_curve.h"
|
||||
#include "public_editor.h"
|
||||
#include "utils.h"
|
||||
#include "rgb_macros.h"
|
||||
|
||||
#include <gtkmm2ext/utils.h>
|
||||
|
||||
#include "i18n.h"
|
||||
|
||||
PBD::Signal1<void,TempoCurve*> TempoCurve::CatchDeletion;
|
||||
|
||||
TempoCurve::TempoCurve (PublicEditor& ed, ArdourCanvas::Container& parent, guint32 rgba, ARDOUR::TempoSection& temp, framepos_t frame, bool handle_events)
|
||||
|
||||
: editor (ed)
|
||||
, _parent (&parent)
|
||||
, _shown (false)
|
||||
, _curve (0)
|
||||
, _color (rgba)
|
||||
, _max_tempo (temp.beats_per_minute())
|
||||
, _tempo (temp)
|
||||
|
||||
|
||||
{
|
||||
const double MH = 12.0;
|
||||
const double M3 = std::max(1.f, rintf(3.f * UIConfiguration::instance().get_ui_scale()));
|
||||
const double M6 = std::max(2.f, rintf(6.f * UIConfiguration::instance().get_ui_scale()));
|
||||
|
||||
points = new ArdourCanvas::Points ();
|
||||
points->push_back (ArdourCanvas::Duple (0.0, 0.0));
|
||||
points->push_back (ArdourCanvas::Duple (1.0, 0.0));
|
||||
points->push_back (ArdourCanvas::Duple (1.0, MH));
|
||||
points->push_back (ArdourCanvas::Duple (0.0, MH));
|
||||
|
||||
frame_position = frame;
|
||||
unit_position = editor.sample_to_pixel (frame);
|
||||
|
||||
group = new ArdourCanvas::Container (&parent, ArdourCanvas::Duple (unit_position, 0));
|
||||
#ifdef CANVAS_DEBUG
|
||||
group->name = string_compose ("Marker::group for %1", _tempo.beats_per_minute());
|
||||
#endif
|
||||
|
||||
_background = new ArdourCanvas::Rectangle (group);
|
||||
#ifdef CANVAS_DEBUG
|
||||
_background->name = string_compose ("Marker::_background for %1", _tempo.beats_per_minute());
|
||||
#endif
|
||||
_background->set_x0 (0.0);
|
||||
_background->set_x1 (ArdourCanvas::COORD_MAX);
|
||||
_background->set_outline_what (ArdourCanvas::Rectangle::What(0));
|
||||
_curve = new ArdourCanvas::Curve (group);
|
||||
#ifdef CANVAS_DEBUG
|
||||
_curve->name = string_compose ("Marker::_background for %1", _tempo.beats_per_minute());
|
||||
#endif
|
||||
_curve->set_fill_mode (ArdourCanvas::Curve::Inside);
|
||||
_curve->set_points_per_segment (32);
|
||||
_curve->set (*points);
|
||||
|
||||
set_color_rgba (rgba);
|
||||
|
||||
editor.ZoomChanged.connect (sigc::mem_fun (*this, &TempoCurve::reposition));
|
||||
|
||||
/* events will be handled by both the group and the mark itself, so
|
||||
* make sure they can both be used to lookup this object.
|
||||
*/
|
||||
|
||||
group->set_data ("marker", this);
|
||||
|
||||
if (handle_events) {
|
||||
//group->Event.connect (sigc::bind (sigc::mem_fun (editor, &PublicEditor::canvas_marker_event), group, this));
|
||||
}
|
||||
set_position (_tempo.frame(), UINT32_MAX);
|
||||
_curve->Event.connect (sigc::bind (sigc::mem_fun (editor, &PublicEditor::canvas_tempo_curve_event), group, this));
|
||||
_background->Event.connect (sigc::bind (sigc::mem_fun (editor, &PublicEditor::canvas_tempo_curve_event), group, this));
|
||||
|
||||
}
|
||||
|
||||
TempoCurve::~TempoCurve ()
|
||||
{
|
||||
CatchDeletion (this); /* EMIT SIGNAL */
|
||||
|
||||
/* destroying the parent group destroys its contents, namely any polygons etc. that we added */
|
||||
delete group;
|
||||
}
|
||||
|
||||
void TempoCurve::reparent(ArdourCanvas::Container & parent)
|
||||
{
|
||||
group->reparent (&parent);
|
||||
_parent = &parent;
|
||||
}
|
||||
|
||||
void
|
||||
TempoCurve::canvas_height_set (double h)
|
||||
{
|
||||
_canvas_height = h;
|
||||
}
|
||||
|
||||
ArdourCanvas::Item&
|
||||
TempoCurve::the_item() const
|
||||
{
|
||||
return *group;
|
||||
}
|
||||
|
||||
void
|
||||
TempoCurve::set_position (framepos_t frame, framepos_t end_frame)
|
||||
{
|
||||
const double height = 12.0;
|
||||
points->clear();
|
||||
unit_position = editor.sample_to_pixel (frame);
|
||||
group->set_x_position (unit_position);
|
||||
frame_position = frame;
|
||||
_end_frame = end_frame;
|
||||
_background->set_x0 (0.0);
|
||||
_background->set_x1 (editor.sample_to_pixel (end_frame - frame));
|
||||
const double tempo_delta = max (10.0, _max_tempo - _min_tempo);
|
||||
double max_y = 0.0;
|
||||
points = new ArdourCanvas::Points ();
|
||||
if (end_frame == UINT32_MAX) {
|
||||
_curve->set_fill_mode (ArdourCanvas::Curve::None);
|
||||
const double tempo_at = _tempo.tempo_at_frame (frame, editor.session()->frame_rate()) * _tempo.note_type();
|
||||
const double y2_pos = (height + 2.0) - (((tempo_at - _min_tempo) / (tempo_delta)) * height);
|
||||
max_y = y2_pos;
|
||||
points->push_back (ArdourCanvas::Duple (0.0, y2_pos));
|
||||
points->push_back (ArdourCanvas::Duple (ArdourCanvas::COORD_MAX - 5.0, y2_pos));
|
||||
|
||||
} else {
|
||||
_curve->set_fill_mode (ArdourCanvas::Curve::Inside);
|
||||
const framepos_t frame_step = (end_frame - frame) / 32;
|
||||
framepos_t current_frame = frame;
|
||||
while (current_frame < end_frame) {
|
||||
const double tempo_at = _tempo.tempo_at_frame (current_frame, editor.session()->frame_rate()) * _tempo.note_type();
|
||||
const double y2_pos = (height + 2.0) - (((tempo_at - _min_tempo) / (tempo_delta)) * height);
|
||||
|
||||
points->push_back (ArdourCanvas::Duple (editor.sample_to_pixel (current_frame - frame), y2_pos));
|
||||
max_y = max (y2_pos, max_y);
|
||||
current_frame += frame_step;
|
||||
}
|
||||
}
|
||||
|
||||
/* the background fills the gap between the bottom of the curve and the time bar */
|
||||
_background->set_y0 (max_y + 1.0);
|
||||
_background->set_y1 (height + 2.0);
|
||||
|
||||
if (max_y == height + 2.0) {
|
||||
_background->hide();
|
||||
} else {
|
||||
_background->show();
|
||||
}
|
||||
|
||||
_curve->set (*points);
|
||||
}
|
||||
|
||||
void
|
||||
TempoCurve::reposition ()
|
||||
{
|
||||
set_position (frame_position, _end_frame);
|
||||
}
|
||||
|
||||
void
|
||||
TempoCurve::show ()
|
||||
{
|
||||
_shown = true;
|
||||
|
||||
group->show ();
|
||||
}
|
||||
|
||||
void
|
||||
TempoCurve::hide ()
|
||||
{
|
||||
_shown = false;
|
||||
|
||||
group->hide ();
|
||||
}
|
||||
|
||||
void
|
||||
TempoCurve::set_color_rgba (uint32_t c)
|
||||
{
|
||||
_color = c;
|
||||
_curve->set_fill_color (UIConfiguration::instance().color_mod ("selection rect", "selection rect"));
|
||||
_curve->set_outline_color (_color);
|
||||
|
||||
_background->set_fill (true);
|
||||
_background->set_fill_color (UIConfiguration::instance().color_mod ("selection rect", "selection rect"));
|
||||
}
|
||||
74
gtk2_ardour/tempo_curve.h
Normal file
74
gtk2_ardour/tempo_curve.h
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
#ifndef __gtk_ardour_tempo_curve_h__
|
||||
#define __gtk_ardour_tempo_curve_h__
|
||||
|
||||
#include <string>
|
||||
#include <glib.h>
|
||||
|
||||
#include <sigc++/signal.h>
|
||||
|
||||
#include "ardour/ardour.h"
|
||||
#include "pbd/signals.h"
|
||||
|
||||
#include "canvas/fwd.h"
|
||||
#include "canvas/types.h"
|
||||
|
||||
namespace ARDOUR {
|
||||
class TempoSection;
|
||||
}
|
||||
class PublicEditor;
|
||||
|
||||
class TempoCurve : public sigc::trackable
|
||||
{
|
||||
public:
|
||||
TempoCurve (PublicEditor& editor, ArdourCanvas::Container &, guint32 rgba, ARDOUR::TempoSection& temp, framepos_t frame, bool handle_events);
|
||||
~TempoCurve ();
|
||||
|
||||
static PBD::Signal1<void,TempoCurve*> CatchDeletion;
|
||||
|
||||
static void setup_sizes (const double timebar_height);
|
||||
|
||||
ArdourCanvas::Item& the_item() const;
|
||||
void canvas_height_set (double);
|
||||
|
||||
void set_position (framepos_t lower, framepos_t upper);
|
||||
void set_color_rgba (uint32_t rgba);
|
||||
framepos_t position() const { return frame_position; }
|
||||
|
||||
ArdourCanvas::Container * get_parent() { return _parent; }
|
||||
void reparent (ArdourCanvas::Container & parent);
|
||||
|
||||
void hide ();
|
||||
void show ();
|
||||
|
||||
ARDOUR::TempoSection& tempo () { return _tempo; }
|
||||
|
||||
void set_max_tempo (const double& max) { _max_tempo = max; }
|
||||
void set_min_tempo (const double& min) { _min_tempo = min; }
|
||||
|
||||
protected:
|
||||
PublicEditor& editor;
|
||||
|
||||
ArdourCanvas::Container* _parent;
|
||||
ArdourCanvas::Container *group;
|
||||
ArdourCanvas::Points *points;
|
||||
ArdourCanvas::Rectangle* _background;
|
||||
ArdourCanvas::Curve* _curve;
|
||||
|
||||
double unit_position;
|
||||
framepos_t frame_position;
|
||||
framepos_t _end_frame;
|
||||
bool _shown;
|
||||
double _canvas_height;
|
||||
uint32_t _color;
|
||||
|
||||
void reposition ();
|
||||
private:
|
||||
double _max_tempo;
|
||||
double _min_tempo;
|
||||
/* disallow copy construction */
|
||||
TempoCurve (TempoCurve const &);
|
||||
TempoCurve & operator= (TempoCurve const &);
|
||||
ARDOUR::TempoSection& _tempo;
|
||||
|
||||
};
|
||||
#endif /* __gtk_ardour_tempo_curve_h__ */
|
||||
|
|
@ -241,6 +241,7 @@ gtk2_ardour_sources = [
|
|||
'strip_silence_dialog.cc',
|
||||
'sys_ex.cc',
|
||||
'tape_region_view.cc',
|
||||
'tempo_curve.cc',
|
||||
'tempo_dialog.cc',
|
||||
'tempo_lines.cc',
|
||||
'theme_manager.cc',
|
||||
|
|
|
|||
|
|
@ -399,6 +399,7 @@ class LIBARDOUR_API TempoMap : public PBD::StatefulDestructible
|
|||
void gui_move_meter (MeterSection*, const Timecode::BBT_Time& bbt);
|
||||
bool gui_change_tempo (TempoSection*, const Tempo& bpm);
|
||||
void gui_dilate_tempo (MeterSection*, const framepos_t& frame);
|
||||
void gui_dilate_tempo (const framepos_t& frame, const framepos_t& end_frame);
|
||||
|
||||
bool can_solve_bbt (TempoSection* section, const Timecode::BBT_Time& bbt);
|
||||
|
||||
|
|
|
|||
|
|
@ -2654,6 +2654,10 @@ TempoMap::gui_dilate_tempo (MeterSection* ms, const framepos_t& frame)
|
|||
Metrics future_map;
|
||||
TempoSection* ts = 0;
|
||||
|
||||
if (frame <= first_meter().frame()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ms->position_lock_style() == AudioTime) {
|
||||
/* disabled for now due to faked tempo locked to meter pulse */
|
||||
return;
|
||||
|
|
@ -2678,8 +2682,8 @@ TempoMap::gui_dilate_tempo (MeterSection* ms, const framepos_t& frame)
|
|||
constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
|
||||
*/
|
||||
double contribution = 0.0;
|
||||
frameoffset_t frame_contribution = 0.0;
|
||||
frameoffset_t prev_t_frame_contribution = 0.0;
|
||||
frameoffset_t frame_contribution = 0;
|
||||
frameoffset_t prev_t_frame_contribution = fr_off;
|
||||
|
||||
if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
|
||||
/* prev to prev_t's position will remain constant in terms of frame and pulse. lets use frames. */
|
||||
|
|
@ -2778,6 +2782,133 @@ TempoMap::gui_dilate_tempo (MeterSection* ms, const framepos_t& frame)
|
|||
MetricPositionChanged (); // Emit Signal
|
||||
}
|
||||
|
||||
void
|
||||
TempoMap::gui_dilate_tempo (const framepos_t& frame, const framepos_t& end_frame)
|
||||
{
|
||||
Metrics future_map;
|
||||
TempoSection* ts = 0;
|
||||
|
||||
|
||||
{
|
||||
Glib::Threads::RWLock::WriterLock lm (lock);
|
||||
ts = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, frame - 1));
|
||||
if (!ts) {
|
||||
return;
|
||||
}
|
||||
TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
|
||||
TempoSection* prev_to_prev_t = 0;
|
||||
const frameoffset_t fr_off = end_frame - frame;
|
||||
double new_bpm = 0.0;
|
||||
|
||||
if (prev_t && prev_t->pulse() > 0.0) {
|
||||
prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (future_map, prev_t->frame() - 1));
|
||||
}
|
||||
|
||||
/* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
|
||||
constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
|
||||
*/
|
||||
double contribution = 0.0;
|
||||
frameoffset_t frame_contribution = 0;
|
||||
frameoffset_t prev_t_frame_contribution = fr_off;
|
||||
|
||||
if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
|
||||
/* prev to prev_t's position will remain constant in terms of frame and pulse. lets use frames. */
|
||||
contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (frame - prev_to_prev_t->frame());
|
||||
frame_contribution = contribution * (double) fr_off;
|
||||
prev_t_frame_contribution -= frame_contribution;
|
||||
}
|
||||
|
||||
if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
|
||||
|
||||
if (prev_t->position_lock_style() == MusicTime) {
|
||||
if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
|
||||
new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame())
|
||||
/ (double) (frame + prev_t_frame_contribution - prev_t->frame()));
|
||||
|
||||
} else {
|
||||
/* prev to prev is irrelevant */
|
||||
const double meter_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
|
||||
const double frame_pulse = prev_t->pulse_at_frame (end_frame, _frame_rate);
|
||||
|
||||
if (frame_pulse != prev_t->pulse()) {
|
||||
new_bpm = prev_t->beats_per_minute() * ((meter_pulse - prev_t->pulse()) / (frame_pulse - prev_t->pulse()));
|
||||
} else {
|
||||
new_bpm = prev_t->beats_per_minute();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* AudioTime */
|
||||
if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
|
||||
new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame())
|
||||
/ (double) (frame + prev_t_frame_contribution - prev_t->frame()));
|
||||
} else {
|
||||
/* prev_to_prev_t is irrelevant */
|
||||
|
||||
if (end_frame != prev_t->frame()) {
|
||||
new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
|
||||
} else {
|
||||
new_bpm = prev_t->beats_per_minute();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (prev_t->c_func() < 0.0) {
|
||||
if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
|
||||
new_bpm = prev_t->tempo_at_frame (prev_t->frame() + frame_contribution, _frame_rate) * (double) prev_t->note_type();
|
||||
} else {
|
||||
/* prev_to_prev_t is irrelevant */
|
||||
new_bpm = prev_t->tempo_at_frame (prev_t->frame() + fr_off, _frame_rate) * (double) prev_t->note_type();
|
||||
|
||||
}
|
||||
const double end_minute = ((end_frame - prev_t->frame()) / (double) _frame_rate) / 60.0;
|
||||
const double end_tempo = prev_t->tempo_at_frame (end_frame, _frame_rate);
|
||||
const double future_c = log (end_tempo / (new_bpm / (double) prev_t->note_type())) / end_minute;
|
||||
|
||||
/* limits - a bit clunky, but meh */
|
||||
if (future_c > -20.1 && future_c < 20.1) {
|
||||
new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame())
|
||||
/ (double) ((frame + prev_t_frame_contribution) - prev_t->frame()));
|
||||
}
|
||||
|
||||
} else if (prev_t->c_func() > 0.0) {
|
||||
if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
|
||||
new_bpm = prev_t->tempo_at_frame (prev_t->frame() - frame_contribution, _frame_rate) * (double) prev_t->note_type();
|
||||
} else {
|
||||
/* prev_to_prev_t is irrelevant */
|
||||
new_bpm = prev_t->tempo_at_frame (prev_t->frame() - fr_off, _frame_rate) * (double) prev_t->note_type();
|
||||
}
|
||||
const double end_minute = ((end_frame - prev_t->frame()) / (double) _frame_rate) / 60.0;
|
||||
const double end_tempo = prev_t->tempo_at_frame (end_frame, _frame_rate);
|
||||
const double future_c = log (end_tempo / (new_bpm / (double) prev_t->note_type())) / end_minute;
|
||||
|
||||
/* limits - a bit clunky, but meh */
|
||||
if (future_c > -20.1 && future_c < 20.1) {
|
||||
new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame())
|
||||
/ (double) ((frame + prev_t_frame_contribution) - prev_t->frame()));
|
||||
}
|
||||
}
|
||||
|
||||
prev_t->set_beats_per_minute (new_bpm);
|
||||
recompute_tempos (future_map);
|
||||
recompute_meters (future_map);
|
||||
|
||||
if (check_solved (future_map, true)) {
|
||||
|
||||
prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, frame - 1));
|
||||
prev_t->set_beats_per_minute (new_bpm);
|
||||
recompute_tempos (_metrics);
|
||||
recompute_meters (_metrics);
|
||||
}
|
||||
}
|
||||
|
||||
Metrics::const_iterator d = future_map.begin();
|
||||
while (d != future_map.end()) {
|
||||
delete (*d);
|
||||
++d;
|
||||
}
|
||||
|
||||
MetricPositionChanged (); // Emit Signal
|
||||
}
|
||||
|
||||
framecnt_t
|
||||
TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -194,7 +194,7 @@ Curve::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
|
|||
window_space = item_to_window (Duple (samples[left].x, samples[left].y));
|
||||
context->move_to (window_space.x, window_space.y);
|
||||
for (uint32_t idx = left + 1; idx < right; ++idx) {
|
||||
window_space = item_to_window (Duple (samples[idx].x, samples[idx].y));
|
||||
window_space = item_to_window (Duple (samples[idx].x, samples[idx].y), false);
|
||||
context->line_to (window_space.x, window_space.y);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue