ARDOUR_UI::instance()->tooltips().set_tip (zoom_out_full_button, _("Zoom to Session"));
zoom_focus_selector.set_name ("ZoomFocusSelector");
- Gtkmm2ext::set_size_request_to_display_given_text (zoom_focus_selector, "Playhead", FUDGE, 0);
+ Gtkmm2ext::set_size_request_to_display_given_text (zoom_focus_selector, _("Playhead"), FUDGE, 0);
set_popdown_strings (zoom_focus_selector, zoom_focus_strings);
zoom_focus_selector.signal_changed().connect (mem_fun(*this, &Editor::zoom_focus_selection_done));
ARDOUR_UI::instance()->tooltips().set_tip (zoom_focus_selector, _("Zoom focus"));
snap_box.set_border_width (2);
snap_type_selector.set_name ("SnapTypeSelector");
- Gtkmm2ext::set_size_request_to_display_given_text (snap_type_selector, "SMPTE Seconds", 2+FUDGE, 10);
+ Gtkmm2ext::set_size_request_to_display_given_text (snap_type_selector, _("SMPTE Seconds"), 2+FUDGE, 10);
set_popdown_strings (snap_type_selector, snap_type_strings);
snap_type_selector.signal_changed().connect (mem_fun(*this, &Editor::snap_type_selection_done));
ARDOUR_UI::instance()->tooltips().set_tip (snap_type_selector, _("Snap/Grid Units"));
snap_mode_selector.set_name ("SnapModeSelector");
- Gtkmm2ext::set_size_request_to_display_given_text (snap_mode_selector, "Magnetic Snap", 2+FUDGE, 10);
+ Gtkmm2ext::set_size_request_to_display_given_text (snap_mode_selector, _("Magnetic Snap"), 2+FUDGE, 10);
set_popdown_strings (snap_mode_selector, snap_mode_strings);
snap_mode_selector.signal_changed().connect (mem_fun(*this, &Editor::snap_mode_selection_done));
ARDOUR_UI::instance()->tooltips().set_tip (snap_mode_selector, _("Snap/Grid Mode"));
edit_point_selector.set_name ("SnapModeSelector");
- Gtkmm2ext::set_size_request_to_display_given_text (edit_point_selector, "Playhead", 2+FUDGE, 10);
+ Gtkmm2ext::set_size_request_to_display_given_text (edit_point_selector, _("Playhead"), 2+FUDGE, 10);
set_popdown_strings (edit_point_selector, edit_point_strings);
edit_point_selector.signal_changed().connect (mem_fun(*this, &Editor::edit_point_selection_done));
ARDOUR_UI::instance()->tooltips().set_tip (edit_point_selector, _("Edit point"));
case EditAtSelectedMarker:
if (!selection->markers.empty()) {
- bool whocares;
- Location* loc = find_location_from_marker (selection->markers.front(), whocares);
+ bool is_start;
+ Location* loc = find_location_from_marker (selection->markers.front(), is_start);
if (loc) {
- where = loc->start();
+ if (is_start) {
+ where = loc->start();
+ } else {
+ where = loc->end();
+ }
break;
}
}
/* only update if the playhead is on screen or we are following it */
- if (_follow_playhead) {
+ if (_follow_playhead && session->requested_return_frame() < 0) {
playhead_cursor->canvas_item.show();
BBT_Time requested;
bpm = tempo_dialog.get_bpm ();
+ double nt = tempo_dialog.get_note_type();
bpm = max (0.01, bpm);
tempo_dialog.get_bbt_time (requested);
begin_reversible_command (_("add tempo mark"));
XMLNode &before = map.get_state();
- map.add_tempo (Tempo (bpm), requested);
+ map.add_tempo (Tempo (bpm,nt), requested);
XMLNode &after = map.get_state();
session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
commit_reversible_command ();
}
double bpm = tempo_dialog.get_bpm ();
+ double nt = tempo_dialog.get_note_type ();
BBT_Time when;
tempo_dialog.get_bbt_time(when);
bpm = max (0.01, bpm);
begin_reversible_command (_("replace tempo mark"));
XMLNode &before = session->tempo_map().get_state();
- session->tempo_map().replace_tempo (*section, Tempo (bpm));
+ session->tempo_map().replace_tempo (*section, Tempo (bpm,nt));
session->tempo_map().move_tempo (*section, when);
XMLNode &after = session->tempo_map().get_state();
session->add_command (new MementoCommand<TempoMap>(session->tempo_map(), &before, &after));
bpm_adjustment (60.0, 1.0, 999.9, 0.1, 1.0, 1.0),
bpm_spinner (bpm_adjustment),
bpm_frame (_("Beats per minute")),
+ note_frame (_("BPM denominator")),
ok_button (action),
cancel_button (_("Cancel")),
when_bar_label (_("Bar")),
Tempo tempo (map.tempo_at (frame));
map.bbt_time (frame, when);
- init (when, tempo.beats_per_minute(), true);
+ init (when, tempo.beats_per_minute(), tempo.note_type(), true);
}
TempoDialog::TempoDialog (TempoSection& section, const string & action)
when_table (2, 2),
when_frame (_("Location"))
{
- init (section.start(), section.beats_per_minute(), section.movable());
+ init (section.start(), section.beats_per_minute(), section.note_type(), section.movable());
}
void
-TempoDialog::init (const BBT_Time& when, double bpm, bool movable)
+TempoDialog::init (const BBT_Time& when, double bpm, double note_type, bool movable)
{
bpm_spinner.set_numeric (true);
bpm_spinner.set_digits (2);
bpm_spinner.set_wrap (true);
bpm_spinner.set_value (bpm);
+ strings.push_back (_("whole (1)"));
+ strings.push_back (_("second (2)"));
+ strings.push_back (_("third (3)"));
+ strings.push_back (_("quarter (4)"));
+ strings.push_back (_("eighth (8)"));
+ strings.push_back (_("sixteenth (16)"));
+ strings.push_back (_("thirty-second (32)"));
+
+ /* the string here needs to be the longest one to display */
+ const guint32 FUDGE = 20; // Combo's are stupid - they steal space from the entry for the button
+ // TRANSLATORS: this is not a mis-spelling of "thirty", we're including a vertical
+ // descender to make sure the height gets computed properly.
+ Gtkmm2ext::set_size_request_to_display_given_text (note_types, "thirtq-second (32)", 7+FUDGE, 15);
+
+ set_popdown_strings (note_types, strings);
+
+ if (note_type==1.0f)
+ note_types.set_active_text (_("whole (1)"));
+ else if (note_type==2.0f)
+ note_types.set_active_text (_("second (2)"));
+ else if (note_type==3.0f)
+ note_types.set_active_text (_("third (3)"));
+ else if (note_type==4.0f)
+ note_types.set_active_text (_("quarter (4)"));
+ else if (note_type==8.0f)
+ note_types.set_active_text (_("eighth (8)"));
+ else if (note_type==16.0f)
+ note_types.set_active_text (_("sixteenth (16)"));
+ else if (note_type==32.0f)
+ note_types.set_active_text (_("thirty-second (32)"));
+ else
+ note_types.set_active_text (_("quarter (4)"));
+
hspacer1.set_border_width (5);
hspacer1.pack_start (bpm_spinner, false, false);
vspacer1.set_border_width (5);
vspacer1.pack_start (hspacer1, false, false);
+ hspacer2.set_border_width (5);
+ hspacer2.pack_start (note_types, false, false);
+ vspacer2.set_border_width (5);
+ vspacer2.pack_start (hspacer2, false, false);
+
bpm_frame.add (vspacer1);
+ note_frame.add (vspacer2);
if (movable) {
snprintf (buf, sizeof (buf), "%" PRIu32, when.bars);
bpm_frame.set_name ("MetricDialogFrame");
bpm_spinner.set_name ("MetricEntry");
+ note_frame.set_name ("MetricDialogFrame");
+ get_vbox()->set_border_width (12);
get_vbox()->pack_start (bpm_frame, false, false);
-
+ get_vbox()->pack_start (note_frame, false, false);
+
add_button (Stock::CANCEL, RESPONSE_CANCEL);
add_button (Stock::APPLY, RESPONSE_ACCEPT);
set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
bpm_spinner.signal_activate().connect (bind (mem_fun (*this, &TempoDialog::response), RESPONSE_ACCEPT));
bpm_spinner.signal_button_press_event().connect (mem_fun (*this, &TempoDialog::bpm_button_press), false);
bpm_spinner.signal_button_release_event().connect (mem_fun (*this, &TempoDialog::bpm_button_release), false);
+ note_types.signal_changed().connect (mem_fun (*this, &TempoDialog::note_types_change));
}
bool
return true;
}
+double
+TempoDialog::get_note_type ()
+{
+ double note_type = 0;
+ vector<string>::iterator i;
+ string text = note_types.get_active_text();
+
+ for (i = strings.begin(); i != strings.end(); ++i) {
+ if (text == *i) {
+ if (sscanf (text.c_str(), "%*[^0-9]%lf", ¬e_type) != 1) {
+ error << string_compose(_("garbaged note type entry (%1)"), text) << endmsg;
+ return 0;
+ } else {
+ break;
+ }
+ }
+ }
+
+ if (i == strings.end()) {
+ if (sscanf (text.c_str(), "%lf", ¬e_type) != 1) {
+ error << string_compose(_("incomprehensible note type entry (%1)"), text) << endmsg;
+ return 0;
+ }
+ }
+
+ cerr << "returning " << note_type << " based on " << text << endl;
+ return note_type;
+}
+
+void
+TempoDialog::note_types_change ()
+{
+ set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
+}
+
MeterDialog::MeterDialog (TempoMap& map, nframes_t frame, const string & action)
: ArdourDialog ("meter dialog"),
strings.push_back (_("sixteenth (16)"));
strings.push_back (_("thirty-second (32)"));
+ /* the string here needs to be the longest one to display */
+ const guint32 FUDGE = 20; // Combo's are stupid - they steal space from the entry for the button
+
+ // TRANSLATORS: this is not a mis-spelling of "thirty", we're including a vertical
+ // descender to make sure the height gets computed properly.
+ Gtkmm2ext::set_size_request_to_display_given_text (note_types, _("thirtq-second (32)"), 7+FUDGE, 15);
+
set_popdown_strings (note_types, strings);
if (note_type==1.0f)
else
note_types.set_active_text (_("quarter (4)"));
- /* the string here needs to be the longest one to display */
- const guint32 FUDGE = 20; // Combo's are stupid - they steal space from the entry for the button
- Gtkmm2ext::set_size_request_to_display_given_text (note_types, "thirty-second (32)", 7+FUDGE, 7);
-
hspacer1.set_border_width (5);
hspacer1.pack_start (note_types, false, false);
vspacer1.set_border_width (5);
get_vbox()->pack_start (when_frame, false, false);
}
+
+ get_vbox()->set_border_width (12);
get_vbox()->pack_start (bpb_frame, false, false);
get_vbox()->pack_start (note_frame, false, false);
struct TempoDialog : public ArdourDialog
{
+ Gtk::ComboBoxText note_types;
+ vector<string> strings;
+ Gtk::Frame note_frame;
Gtk::Adjustment bpm_adjustment;
Gtk::SpinButton bpm_spinner;
Gtk::Frame bpm_frame;
Gtk::Button ok_button;
Gtk::Button cancel_button;
Gtk::HBox button_box;
- Gtk::HBox hspacer1;
- Gtk::VBox vspacer1;
+ Gtk::HBox hspacer1, hspacer2;
+ Gtk::VBox vspacer1, vspacer2;
Gtk::Entry when_bar_entry;
Gtk::Entry when_beat_entry;
Gtk::Label when_bar_label;
TempoDialog (ARDOUR::TempoSection&, const string & action);
double get_bpm ();
+ double get_note_type ();
bool get_bbt_time (ARDOUR::BBT_Time&);
private:
- void init (const ARDOUR::BBT_Time& start, double, bool);
+ void init (const ARDOUR::BBT_Time& start, double, double, bool);
void bpm_changed ();
bool bpm_button_press (GdkEventButton* );
bool bpm_button_release (GdkEventButton* );
+ void note_types_change ();
};
struct MeterDialog : public ArdourDialog
typedef void (Session::*process_function_type)(nframes_t);
- AudioEngine &_engine;
- mutable gint processing_prohibited;
+ AudioEngine& _engine;
+ mutable gint processing_prohibited;
process_function_type process_function;
process_function_type last_process_function;
bool waiting_for_sync_offset;
- nframes_t _base_frame_rate;
- nframes_t _current_frame_rate; //this includes video pullup offset
+ nframes_t _base_frame_rate;
+ nframes_t _current_frame_rate; //this includes video pullup offset
int transport_sub_state;
- mutable gint _record_status;
- nframes_t _transport_frame;
+ mutable gint _record_status;
+ volatile nframes_t _transport_frame;
Location* end_location;
Location* start_location;
- Slave *_slave;
+ Slave* _slave;
bool _silent;
volatile float _transport_speed;
volatile float _desired_transport_speed;
float _last_transport_speed;
bool auto_play_legal;
- nframes_t _last_slave_transport_frame;
- nframes_t maximum_output_latency;
- nframes_t last_stop_frame;
- nframes64_t _requested_return_frame;
+ nframes_t _last_slave_transport_frame;
+ nframes_t maximum_output_latency;
+ nframes_t last_stop_frame;
+ volatile nframes64_t _requested_return_frame;
vector<Sample *> _passthru_buffers;
vector<Sample *> _silent_buffers;
vector<Sample *> _send_buffers;
- nframes_t current_block_size;
- nframes_t _worst_output_latency;
- nframes_t _worst_input_latency;
- nframes_t _worst_track_latency;
+ nframes_t current_block_size;
+ nframes_t _worst_output_latency;
+ nframes_t _worst_input_latency;
+ nframes_t _worst_track_latency;
bool _have_captured;
float _meter_hold;
float _meter_falloff;
using std::vector;
namespace ARDOUR {
-
+class Meter;
class Tempo {
public:
- Tempo (double bpm)
- : _beats_per_minute (bpm) {}
+ Tempo (double bpm, double type=4.0) // defaulting to quarter note
+ : _beats_per_minute (bpm), _note_type(type) {}
Tempo (const Tempo& other) {
_beats_per_minute = other._beats_per_minute;
+ _note_type = other._note_type;
}
void operator= (const Tempo& other) {
if (&other != this) {
_beats_per_minute = other._beats_per_minute;
+ _note_type = other._note_type;
}
}
- double beats_per_minute () const { return _beats_per_minute; }
- double frames_per_beat (nframes_t sr) const {
- return ((60.0 * sr) / _beats_per_minute);
- }
+ double beats_per_minute () const { return _beats_per_minute;}
+ double note_type () const { return _note_type;}
+ double frames_per_beat (nframes_t sr, const Meter& meter) const;
protected:
double _beats_per_minute;
+ double _note_type;
};
class Meter {
class TempoSection : public MetricSection, public Tempo {
public:
- TempoSection (const BBT_Time& start, double qpm)
- : MetricSection (start), Tempo (qpm) {}
+ TempoSection (const BBT_Time& start, double qpm, double note_type)
+ : MetricSection (start), Tempo (qpm, note_type) {}
TempoSection (const XMLNode&);
static const string xml_state_node_name;
LadspaPlugin::unique_id() const
{
char buf[32];
- snprintf (buf, sizeof (buf), "%u", descriptor->UniqueID);
+ snprintf (buf, sizeof (buf), "%lu", descriptor->UniqueID);
return string (buf);
}
}
- PositionChanged (_transport_frame); /* EMIT SIGNAL */
+ PositionChanged ((nframes_t) _transport_frame); /* EMIT SIGNAL */
TransportStateChange (); /* EMIT SIGNAL */
/* and start it up again if relevant */
const double Meter::ticks_per_beat = 1920.0;
+double Tempo::frames_per_beat (nframes_t sr, const Meter& meter) const
+{
+ return ((60.0 * sr) / (_beats_per_minute * meter.note_divisor()/_note_type));
+}
+
/***********************************************************************/
double
Meter::frames_per_bar (const Tempo& tempo, nframes_t sr) const
{
- return ((60.0 * sr * _beats_per_bar) / tempo.beats_per_minute());
+ return ((60.0 * sr * _beats_per_bar) / (tempo.beats_per_minute() * _note_type/tempo.note_type()));
}
/***********************************************************************/
error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
throw failed_constructor();
}
+
+ if ((prop = node.property ("note-type")) == 0) {
+ /* older session, make note type be quarter by default */
+ _note_type = 4.0;
+ } else {
+ if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
+ error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
+ throw failed_constructor();
+ }
+ }
if ((prop = node.property ("movable")) == 0) {
error << _("TempoSection XML node has no \"movable\" property") << endmsg;
root->add_property ("start", buf);
snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
root->add_property ("beats-per-minute", buf);
+ snprintf (buf, sizeof (buf), "%f", _note_type);
+ root->add_property ("note-type", buf);
snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
root->add_property ("movable", buf);
start.beats = 1;
start.ticks = 0;
- TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute());
+ TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type());
MeterSection *m = new MeterSection (start, _default_meter.beats_per_bar(), _default_meter.note_divisor());
t->set_movable (false);
where.ticks = 0;
- do_insert (new TempoSection (where, tempo.beats_per_minute()));
+ do_insert (new TempoSection (where, tempo.beats_per_minute(), tempo.note_type()));
}
StateChanged (Change (0));
const double beats_per_bar = metric.meter().beats_per_bar();
const double frames_per_bar = metric.meter().frames_per_bar (metric.tempo(), _frame_rate);
- const double beat_frames = metric.tempo().frames_per_beat (_frame_rate);
+ const double beat_frames = metric.tempo().frames_per_beat (_frame_rate, metric.meter());
/* now compute how far beyond that point we actually are. */
+ start.ticks/Meter::ticks_per_beat;
- start_frame = m.frame() + (nframes_t) rint( beat_offset * m.tempo().frames_per_beat(_frame_rate));
+ start_frame = m.frame() + (nframes_t) rint( beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter()));
m = metric_at(end);
beat_offset = bar_offset * m.meter().beats_per_bar() - (m.start().beats -1) + (end.beats - 1)
+ end.ticks/Meter::ticks_per_beat;
- end_frame = m.frame() + (nframes_t) rint(beat_offset * m.tempo().frames_per_beat(_frame_rate));
+ end_frame = m.frame() + (nframes_t) rint(beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter()));
frames = end_frame - start_frame;
double beat_frames = 0;
beats_per_bar = meter.beats_per_bar();
- beat_frames = tempo.frames_per_beat (_frame_rate);
+ beat_frames = tempo.frames_per_beat (_frame_rate,meter);
frames = 0;
beats_per_bar = meter->beats_per_bar ();
frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
- beat_frames = tempo->frames_per_beat (_frame_rate);
+ beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
if (meter->frame() > tempo->frame()) {
bar = meter->start().bars;
beats_per_bar = meter->beats_per_bar ();
frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
- beat_frames = tempo->frames_per_beat (_frame_rate);
+ beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
++i;
}
for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
- o << "Tempo @ " << *i << ' ' << t->beats_per_minute() << " BPM at " << t->start() << " frame= " << t->frame() << " (move? "
+ o << "Tempo @ " << *i << ' ' << t->beats_per_minute() << " BPM (denom = " << t->note_type() << ") at " << t->start() << " frame= " << t->frame() << " (move? "
<< t->movable() << ')' << endl;
} else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
o << "Meter @ " << *i << ' ' << m->beats_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()