Tempo ramps - add visualtempo curve, dragging bbt or music rulers with constraint modifier dilates previous tempo.

This commit is contained in:
nick_m 2016-05-08 03:03:12 +10:00
parent 652a59b317
commit 86b0268e8b
15 changed files with 576 additions and 14 deletions

View file

@ -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);

View file

@ -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*/)
{

View file

@ -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)

View file

@ -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

View file

@ -36,6 +36,7 @@ enum ItemType {
GainLineItem,
AutomationLineItem,
MeterMarkerItem,
TempoCurveItem,
TempoMarkerItem,
MeterBarItem,
TempoBarItem,

View file

@ -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);

View file

@ -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 ();

View file

@ -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);

View file

@ -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
View 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
View 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__ */

View file

@ -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',

View file

@ -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);

View file

@ -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)
{

View file

@ -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);
}