mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-06 23:05:04 +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 "editor_items.h"
|
||||||
#include "region_selection.h"
|
#include "region_selection.h"
|
||||||
#include "selection_memento.h"
|
#include "selection_memento.h"
|
||||||
|
#include "tempo_curve.h"
|
||||||
|
|
||||||
namespace Gtkmm2ext {
|
namespace Gtkmm2ext {
|
||||||
class Bindings;
|
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_stream_view_event (GdkEvent* event,ArdourCanvas::Item*, RouteTimeAxisView*);
|
||||||
bool canvas_marker_event (GdkEvent* event,ArdourCanvas::Item*, ArdourMarker*);
|
bool canvas_marker_event (GdkEvent* event,ArdourCanvas::Item*, ArdourMarker*);
|
||||||
bool canvas_tempo_marker_event (GdkEvent* event,ArdourCanvas::Item*, TempoMarker*);
|
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_meter_marker_event (GdkEvent* event,ArdourCanvas::Item*, MeterMarker*);
|
||||||
bool canvas_automation_track_event(GdkEvent* event, ArdourCanvas::Item*, AutomationTimeAxisView*);
|
bool canvas_automation_track_event(GdkEvent* event, ArdourCanvas::Item*, AutomationTimeAxisView*);
|
||||||
bool canvas_note_event (GdkEvent* event, ArdourCanvas::Item *);
|
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;
|
typedef std::list<ArdourMarker*> Marks;
|
||||||
Marks metric_marks;
|
Marks metric_marks;
|
||||||
|
|
||||||
|
typedef std::list<TempoCurve*> Curves;
|
||||||
|
Curves tempo_curves;
|
||||||
|
|
||||||
void remove_metric_marks ();
|
void remove_metric_marks ();
|
||||||
void draw_metric_marks (const ARDOUR::Metrics& metrics);
|
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);
|
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
|
bool
|
||||||
Editor::canvas_meter_marker_event (GdkEvent *event, ArdourCanvas::Item* item, MeterMarker* /*marker*/)
|
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)
|
CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
|
||||||
: Drag (e, &c.track_canvas_item(), false)
|
: Drag (e, &c.track_canvas_item(), false)
|
||||||
, _cursor (c)
|
, _cursor (c)
|
||||||
|
|
|
||||||
|
|
@ -741,6 +741,31 @@ private:
|
||||||
XMLNode* before_state;
|
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 */
|
/** Drag of the playhead cursor */
|
||||||
class CursorDrag : public Drag
|
class CursorDrag : public Drag
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ enum ItemType {
|
||||||
GainLineItem,
|
GainLineItem,
|
||||||
AutomationLineItem,
|
AutomationLineItem,
|
||||||
MeterMarkerItem,
|
MeterMarkerItem,
|
||||||
|
TempoCurveItem,
|
||||||
TempoMarkerItem,
|
TempoMarkerItem,
|
||||||
MeterBarItem,
|
MeterBarItem,
|
||||||
TempoBarItem,
|
TempoBarItem,
|
||||||
|
|
|
||||||
|
|
@ -711,13 +711,17 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
|
||||||
|
|
||||||
case MarkerBarItem:
|
case MarkerBarItem:
|
||||||
case TempoBarItem:
|
case TempoBarItem:
|
||||||
|
case TempoCurveItem:
|
||||||
case MeterBarItem:
|
case MeterBarItem:
|
||||||
case TimecodeRulerItem:
|
case TimecodeRulerItem:
|
||||||
case SamplesRulerItem:
|
case SamplesRulerItem:
|
||||||
case MinsecRulerItem:
|
case MinsecRulerItem:
|
||||||
case BBTRulerItem:
|
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);
|
_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;
|
return true;
|
||||||
break;
|
break;
|
||||||
|
|
@ -1333,7 +1337,6 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
|
||||||
case RegionItem:
|
case RegionItem:
|
||||||
show_region_properties ();
|
show_region_properties ();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TempoMarkerItem: {
|
case TempoMarkerItem: {
|
||||||
ArdourMarker* marker;
|
ArdourMarker* marker;
|
||||||
TempoMarker* tempo_marker;
|
TempoMarker* tempo_marker;
|
||||||
|
|
@ -1438,6 +1441,7 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
|
||||||
case TransportMarkerBarItem:
|
case TransportMarkerBarItem:
|
||||||
case CdMarkerBarItem:
|
case CdMarkerBarItem:
|
||||||
case TempoBarItem:
|
case TempoBarItem:
|
||||||
|
case TempoCurveItem:
|
||||||
case MeterBarItem:
|
case MeterBarItem:
|
||||||
case VideoBarItem:
|
case VideoBarItem:
|
||||||
case TimecodeRulerItem:
|
case TimecodeRulerItem:
|
||||||
|
|
@ -1547,8 +1551,8 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
|
||||||
mouse_add_new_marker (where, true);
|
mouse_add_new_marker (where, true);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case TempoBarItem:
|
case TempoBarItem:
|
||||||
|
case TempoCurveItem:
|
||||||
if (!_dragging_playhead) {
|
if (!_dragging_playhead) {
|
||||||
snap_to_with_modifier (where, event);
|
snap_to_with_modifier (where, event);
|
||||||
mouse_add_new_tempo_event (where);
|
mouse_add_new_tempo_event (where);
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,11 @@ Editor::remove_metric_marks ()
|
||||||
delete_when_idle (*x);
|
delete_when_idle (*x);
|
||||||
}
|
}
|
||||||
metric_marks.clear ();
|
metric_marks.clear ();
|
||||||
|
|
||||||
|
for (Curves::iterator x = tempo_curves.begin(); x != tempo_curves.end(); ++x) {
|
||||||
|
delete (*x);
|
||||||
|
}
|
||||||
|
tempo_curves.clear ();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -78,6 +83,8 @@ Editor::draw_metric_marks (const Metrics& metrics)
|
||||||
const MeterSection *ms;
|
const MeterSection *ms;
|
||||||
const TempoSection *ts;
|
const TempoSection *ts;
|
||||||
char buf[64];
|
char buf[64];
|
||||||
|
double max_tempo = 0.0;
|
||||||
|
double min_tempo = DBL_MAX;
|
||||||
|
|
||||||
remove_metric_marks ();
|
remove_metric_marks ();
|
||||||
|
|
||||||
|
|
@ -89,15 +96,36 @@ Editor::draw_metric_marks (const Metrics& metrics)
|
||||||
*(const_cast<MeterSection*>(ms))));
|
*(const_cast<MeterSection*>(ms))));
|
||||||
} else if ((ts = dynamic_cast<const TempoSection*>(*i)) != 0) {
|
} else if ((ts = dynamic_cast<const TempoSection*>(*i)) != 0) {
|
||||||
if (UIConfiguration::instance().get_allow_non_quarter_pulse()) {
|
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 {
|
} 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,
|
metric_marks.push_back (new TempoMarker (*this, *tempo_group, UIConfiguration::instance().color ("tempo marker"), buf,
|
||||||
*(const_cast<TempoSection*>(ts))));
|
*(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);
|
update_tempo_based_rulers (grid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct CurveComparator {
|
||||||
|
bool operator() (TempoCurve const * a, TempoCurve const * b) {
|
||||||
|
return a->position() < b->position();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
void
|
void
|
||||||
Editor::marker_position_changed ()
|
Editor::marker_position_changed ()
|
||||||
{
|
{
|
||||||
|
|
@ -138,14 +172,21 @@ Editor::marker_position_changed ()
|
||||||
MeterMarker* meter_marker;
|
MeterMarker* meter_marker;
|
||||||
const TempoSection *ts;
|
const TempoSection *ts;
|
||||||
const MeterSection *ms;
|
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) {
|
for (Marks::iterator x = metric_marks.begin(); x != metric_marks.end(); ++x) {
|
||||||
if ((tempo_marker = dynamic_cast<TempoMarker*> (*x)) != 0) {
|
if ((tempo_marker = dynamic_cast<TempoMarker*> (*x)) != 0) {
|
||||||
if ((ts = &tempo_marker->tempo()) != 0) {
|
if ((ts = &tempo_marker->tempo()) != 0) {
|
||||||
tempo_marker->set_position (ts->frame ());
|
tempo_marker->set_position (ts->frame ());
|
||||||
char buf[64];
|
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);
|
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) {
|
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;
|
std::vector<TempoMap::BBTPoint> grid;
|
||||||
compute_current_bbt_points (grid, leftmost_frame, leftmost_frame + current_page_samples());
|
compute_current_bbt_points (grid, leftmost_frame, leftmost_frame + current_page_samples());
|
||||||
draw_measures (grid);
|
draw_measures (grid);
|
||||||
|
|
@ -251,9 +306,6 @@ Editor::mouse_add_new_meter_event (framepos_t frame)
|
||||||
TempoMap& map(_session->tempo_map());
|
TempoMap& map(_session->tempo_map());
|
||||||
MeterDialog meter_dialog (map, frame, _("add"));
|
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 ()) {
|
switch (meter_dialog.run ()) {
|
||||||
case RESPONSE_ACCEPT:
|
case RESPONSE_ACCEPT:
|
||||||
break;
|
break;
|
||||||
|
|
@ -271,11 +323,13 @@ Editor::mouse_add_new_meter_event (framepos_t frame)
|
||||||
|
|
||||||
begin_reversible_command (_("add meter mark"));
|
begin_reversible_command (_("add meter mark"));
|
||||||
XMLNode &before = map.get_state();
|
XMLNode &before = map.get_state();
|
||||||
|
|
||||||
if (meter_dialog.get_lock_style() == MusicTime) {
|
if (meter_dialog.get_lock_style() == MusicTime) {
|
||||||
map.add_meter (Meter (bpb, note_type), map.bbt_to_beats (requested), requested);
|
map.add_meter (Meter (bpb, note_type), map.bbt_to_beats (requested), requested);
|
||||||
} else {
|
} 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()));
|
_session->add_command(new MementoCommand<TempoMap>(map, &before, &map.get_state()));
|
||||||
commit_reversible_command ();
|
commit_reversible_command ();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -150,6 +150,7 @@ setup_gtk_ardour_enums ()
|
||||||
REGISTER_ENUM (GainLineItem);
|
REGISTER_ENUM (GainLineItem);
|
||||||
REGISTER_ENUM (AutomationLineItem);
|
REGISTER_ENUM (AutomationLineItem);
|
||||||
REGISTER_ENUM (MeterMarkerItem);
|
REGISTER_ENUM (MeterMarkerItem);
|
||||||
|
REGISTER_ENUM (TempoCurveItem);
|
||||||
REGISTER_ENUM (TempoMarkerItem);
|
REGISTER_ENUM (TempoMarkerItem);
|
||||||
REGISTER_ENUM (MeterBarItem);
|
REGISTER_ENUM (MeterBarItem);
|
||||||
REGISTER_ENUM (TempoBarItem);
|
REGISTER_ENUM (TempoBarItem);
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,7 @@ class PluginUIWindow;
|
||||||
class RegionView;
|
class RegionView;
|
||||||
class RouteTimeAxisView;
|
class RouteTimeAxisView;
|
||||||
class Selection;
|
class Selection;
|
||||||
|
class TempoCurve;
|
||||||
class TempoMarker;
|
class TempoMarker;
|
||||||
class TimeAxisView;
|
class TimeAxisView;
|
||||||
class TimeAxisViewItem;
|
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_marker_event (GdkEvent* event, ArdourCanvas::Item*, ArdourMarker*) = 0;
|
||||||
virtual bool canvas_videotl_bar_event (GdkEvent* event, ArdourCanvas::Item*) = 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_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_meter_marker_event (GdkEvent* event, ArdourCanvas::Item*, MeterMarker*) = 0;
|
||||||
virtual bool canvas_automation_track_event(GdkEvent* event, ArdourCanvas::Item*, AutomationTimeAxisView*) = 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',
|
'strip_silence_dialog.cc',
|
||||||
'sys_ex.cc',
|
'sys_ex.cc',
|
||||||
'tape_region_view.cc',
|
'tape_region_view.cc',
|
||||||
|
'tempo_curve.cc',
|
||||||
'tempo_dialog.cc',
|
'tempo_dialog.cc',
|
||||||
'tempo_lines.cc',
|
'tempo_lines.cc',
|
||||||
'theme_manager.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);
|
void gui_move_meter (MeterSection*, const Timecode::BBT_Time& bbt);
|
||||||
bool gui_change_tempo (TempoSection*, const Tempo& bpm);
|
bool gui_change_tempo (TempoSection*, const Tempo& bpm);
|
||||||
void gui_dilate_tempo (MeterSection*, const framepos_t& frame);
|
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);
|
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;
|
Metrics future_map;
|
||||||
TempoSection* ts = 0;
|
TempoSection* ts = 0;
|
||||||
|
|
||||||
|
if (frame <= first_meter().frame()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (ms->position_lock_style() == AudioTime) {
|
if (ms->position_lock_style() == AudioTime) {
|
||||||
/* disabled for now due to faked tempo locked to meter pulse */
|
/* disabled for now due to faked tempo locked to meter pulse */
|
||||||
return;
|
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.
|
constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
|
||||||
*/
|
*/
|
||||||
double contribution = 0.0;
|
double contribution = 0.0;
|
||||||
frameoffset_t frame_contribution = 0.0;
|
frameoffset_t frame_contribution = 0;
|
||||||
frameoffset_t prev_t_frame_contribution = 0.0;
|
frameoffset_t prev_t_frame_contribution = fr_off;
|
||||||
|
|
||||||
if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
|
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. */
|
/* 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
|
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
|
framecnt_t
|
||||||
TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
|
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));
|
window_space = item_to_window (Duple (samples[left].x, samples[left].y));
|
||||||
context->move_to (window_space.x, window_space.y);
|
context->move_to (window_space.x, window_space.y);
|
||||||
for (uint32_t idx = left + 1; idx < right; ++idx) {
|
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);
|
context->line_to (window_space.x, window_space.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue