View Issue Details

IDProjectCategoryView StatusLast Update
0000940ardourbugspublic2020-04-19 20:12
Reportercsuwi Assigned Toseablade  
PrioritynormalSeverityminorReproducibilityalways
Status closedResolutionfixed 
Summary0000940: Time signatures with subdivisions other than four not correctly measured.
Description
Basically, a bar of 3/8 should be half the length of a bar of 3/4 but as it is the subdivisions don't seem to make a difference.
Additional Informationlibardour 0.880
TagsNo tags attached.

Activities

paul

2005-04-01 12:43

administrator   ~0002154

This is caused by a conceptual/design issue that is confusing without documentation, and probably should be altered over the long term.

At present, Ardour defines the "b" in "bpm" to mean "whatever note length is defined by the mete denominator". If you set bpm to 120 and use 4/4, then its 120 quarter notes per minute. If you set the meter to 3/8, its 120 eighth notes per minute.

This is not quite so inconvenient as it seems, because for many common uses of meter switches, it is necessary to change the tempo even in systems where bpm == quarters per minute. The major difference in Ardour right now is that the new bpm you should enter must take into account that it is *not* a quarter-per-minute count, but a beat-per-minute count.

In the long term, we will probably change this so that the user can define the interpretation of bpm to be one of (quarters-per-minute, meter-denominator-per-minute,some-other-note-length-per-minute).

Let me know if this all makes sense to you.

csuwi

2005-04-01 16:40

reporter   ~0002157

It makes sense as an explanation but I think it makes it incompatible with convention and with tools such as Hydrogen and Rosegarden.

The long term solution seems reasonable, certainly.

I also notice some weirdness with odd time signatures scrambling the position of bar lines. I will report this as a seperate bug.

2007-12-21 13:17

 

dm_timesig_patch (12,457 bytes)   
Index: gtk2_ardour/tempo_dialog.cc
===================================================================
--- gtk2_ardour/tempo_dialog.cc	(revision 2798)
+++ gtk2_ardour/tempo_dialog.cc	(working copy)
@@ -37,6 +37,7 @@
 	  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")),
@@ -48,7 +49,7 @@
 	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)
@@ -63,23 +64,60 @@
 	  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)"));
+	
+	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)"));
+		
+	/* 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 (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);
@@ -115,9 +153,11 @@
 
 	bpm_frame.set_name ("MetricDialogFrame");
 	bpm_spinner.set_name ("MetricEntry");
+	note_frame.set_name ("MetricDialogFrame");
 
 	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);
@@ -131,6 +171,7 @@
 	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
@@ -168,7 +209,41 @@
 	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", &note_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", &note_type) != 1) {
+			error << string_compose(_("incomprehensible note type entry (%1)"), text) << endmsg;
+			return 0;
+		}
+	}
 
+	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"),
 	  note_frame (_("Meter denominator")),
Index: gtk2_ardour/editor_tempodisplay.cc
===================================================================
--- gtk2_ardour/editor_tempodisplay.cc	(revision 2798)
+++ gtk2_ardour/editor_tempodisplay.cc	(working copy)
@@ -308,13 +308,14 @@
 	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 ();
@@ -429,13 +430,14 @@
 	}
 
 	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));
Index: gtk2_ardour/tempo_dialog.h
===================================================================
--- gtk2_ardour/tempo_dialog.h	(revision 2798)
+++ gtk2_ardour/tempo_dialog.h	(working copy)
@@ -37,6 +37,9 @@
 
 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;
@@ -44,8 +47,8 @@
     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;
@@ -58,13 +61,15 @@
     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 
Index: libs/ardour/ardour/tempo.h
===================================================================
--- libs/ardour/ardour/tempo.h	(revision 2798)
+++ libs/ardour/ardour/tempo.h	(working copy)
@@ -40,27 +40,29 @@
 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 {
Index: libs/ardour/tempo.cc
===================================================================
--- libs/ardour/tempo.cc	(revision 2798)
+++ libs/ardour/tempo.cc	(working copy)
@@ -43,12 +43,17 @@
 
 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()));
 }
 
 /***********************************************************************/
@@ -86,6 +91,16 @@
 		error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
 		throw failed_constructor();
 	}
+	
+	if ((prop = node.property ("note-type")) == 0) {
+		error << _("TempoSection XML node has no \"note-type\" property") << endmsg;
+		throw failed_constructor();
+	}
+	
+	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;
@@ -109,6 +124,8 @@
 	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);
 
@@ -614,7 +631,7 @@
 
 	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. */
 
@@ -667,7 +684,7 @@
 		+ 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);
 
@@ -676,7 +693,7 @@
 	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;
 
@@ -697,7 +714,7 @@
 	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;
 
@@ -1088,7 +1105,7 @@
 
 	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;
@@ -1198,7 +1215,7 @@
 
 			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;
 		}
dm_timesig_patch (12,457 bytes)   

drmoore

2007-12-21 13:20

reporter   ~0004609

Attached a patch that attempts to change this behaviour in:
URL: http://subversion.ardour.org/svn/ardour2/branches/2.0-ongoing
Revision: 2798

Adds a tempo denominator dropdown to TempoDialog
Adds a tempo denominator member var to Tempo
Modifies time calculations to take this into account.

Not extensively tested but feedback greatly appreciated.

paul

2007-12-21 15:01

administrator   ~0004610

drmoore - i applied your patch after a few fixes. thanks very much. this does have one disadvantage that in existing ardour sessions with tempo changes, the changes will not happen in the way they have before. but i am reasonably happy with the idea that this is right thing to do anyway, so its just time to force a little pain on users. i might even fix that too (i can see a way to force the old behaviour for old sessions only).

2007-12-28 21:46

 

dm_timesig_patch_trunk (14,719 bytes)   
diff --git a/gtk2_ardour/editor_tempodisplay.cc b/gtk2_ardour/editor_tempodisplay.cc
index 831fe3b..f18392c 100644
--- a/gtk2_ardour/editor_tempodisplay.cc
+++ b/gtk2_ardour/editor_tempodisplay.cc
@@ -235,13 +235,14 @@ Editor::mouse_add_new_tempo_event (nframes_t frame)
 	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 ();
@@ -356,13 +357,14 @@ Editor::edit_tempo_section (TempoSection* section)
 	}
 
 	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));
diff --git a/gtk2_ardour/tempo_dialog.cc b/gtk2_ardour/tempo_dialog.cc
index d69acf7..8eab45a 100644
--- a/gtk2_ardour/tempo_dialog.cc
+++ b/gtk2_ardour/tempo_dialog.cc
@@ -37,6 +37,7 @@ TempoDialog::TempoDialog (TempoMap& map, nframes_t frame, const string & action)
 	  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")),
@@ -48,7 +49,7 @@ TempoDialog::TempoDialog (TempoMap& map, nframes_t frame, const string & action)
 	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)
@@ -63,23 +64,56 @@ 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 (1);
 	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)"));
+ 	
+ 	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);
@@ -115,9 +149,11 @@ TempoDialog::init (const BBT_Time& when, double bpm, bool movable)
 
 	bpm_frame.set_name ("MetricDialogFrame");
 	bpm_spinner.set_name ("MetricEntry");
+	note_frame.set_name ("MetricDialogFrame");
 
 	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);
@@ -131,6 +167,7 @@ TempoDialog::init (const BBT_Time& when, double bpm, bool movable)
 	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
@@ -168,6 +205,40 @@ TempoDialog::get_bbt_time (BBT_Time& requested)
 	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", &note_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", &note_type) != 1) {
+			error << string_compose(_("incomprehensible note type entry (%1)"), text) << endmsg;
+			return 0;
+		}
+	}
+
+	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"),
diff --git a/gtk2_ardour/tempo_dialog.h b/gtk2_ardour/tempo_dialog.h
index b9f6a16..a92f26f 100644
--- a/gtk2_ardour/tempo_dialog.h
+++ b/gtk2_ardour/tempo_dialog.h
@@ -37,6 +37,9 @@
 
 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;
@@ -44,8 +47,8 @@ struct TempoDialog : public ArdourDialog
     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;
@@ -58,13 +61,15 @@ struct TempoDialog : public ArdourDialog
     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 
diff --git a/libs/ardour/ardour/tempo.h b/libs/ardour/ardour/tempo.h
index 2d8462a..79df425 100644
--- a/libs/ardour/ardour/tempo.h
+++ b/libs/ardour/ardour/tempo.h
@@ -40,27 +40,29 @@ using std::list;
 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 {
diff --git a/libs/ardour/smf_source.cc b/libs/ardour/smf_source.cc
index b8d82bd..b332d74 100644
--- a/libs/ardour/smf_source.cc
+++ b/libs/ardour/smf_source.cc
@@ -345,7 +345,7 @@ SMFSource::read_unlocked (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, n
 	
 	// FIXME: assumes tempo never changes after start
 	const double frames_per_beat = _session.tempo_map().tempo_at(_timeline_position).frames_per_beat(
-			_session.engine().frame_rate());
+			_session.engine().frame_rate(), _session.tempo_map().meter_at(_timeline_position));
 	
 	const uint64_t start_ticks = (uint64_t)((start / frames_per_beat) * _ppqn);
 
@@ -457,7 +457,7 @@ SMFSource::append_event_unlocked(const MidiEvent& ev)
 	
 	// FIXME: assumes tempo never changes after start
 	const double frames_per_beat = _session.tempo_map().tempo_at
-			(_timeline_position).frames_per_beat(_session.engine().frame_rate());
+			(_timeline_position).frames_per_beat(_session.engine().frame_rate(),_session.tempo_map().meter_at(_timeline_position));
 	
 	const uint32_t delta_time = (uint32_t)((ev.time() - _last_ev_time) / frames_per_beat * _ppqn);
 
@@ -888,7 +888,7 @@ SMFSource::load_model(bool lock, bool force_reload)
 	
 	// FIXME: assumes tempo never changes after start
 	const double frames_per_beat = _session.tempo_map().tempo_at(_timeline_position).frames_per_beat(
-			_session.engine().frame_rate());
+			_session.engine().frame_rate(),_session.tempo_map().meter_at(_timeline_position));
 	
 	uint32_t delta_t = 0;
 	int ret;
diff --git a/libs/ardour/tempo.cc b/libs/ardour/tempo.cc
index cd59e93..dc3301d 100644
--- a/libs/ardour/tempo.cc
+++ b/libs/ardour/tempo.cc
@@ -43,12 +43,17 @@ Tempo    TempoMap::_default_tempo (120.0);
 
 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()));
 }
 
 /***********************************************************************/
@@ -86,6 +91,16 @@ TempoSection::TempoSection (const XMLNode& node)
 		error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
 		throw failed_constructor();
 	}
+	
+	if ((prop = node.property ("note-type")) == 0) {
+		error << _("TempoSection XML node has no \"note-type\" property") << endmsg;
+		throw failed_constructor();
+	}
+	
+	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;
@@ -109,6 +124,8 @@ TempoSection::get_state() const
 	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);
 
@@ -614,7 +631,7 @@ TempoMap::bbt_time_with_metric (nframes_t frame, BBT_Time& bbt, const Metric& me
 
 	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. */
 
@@ -667,7 +684,7 @@ TempoMap::count_frames_between ( const BBT_Time& start, const BBT_Time& end) con
 		+ 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);
 
@@ -676,7 +693,7 @@ TempoMap::count_frames_between ( const BBT_Time& start, const BBT_Time& end) con
 	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;
 
@@ -697,7 +714,7 @@ TempoMap::count_frames_between_metrics (const Meter& meter, const Tempo& tempo,
 	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;
 
@@ -1088,7 +1105,7 @@ TempoMap::get_points (nframes_t lower, nframes_t upper) const
 
 	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;
@@ -1198,7 +1215,7 @@ TempoMap::get_points (nframes_t lower, nframes_t upper) const
 
 			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;
 		}
dm_timesig_patch_trunk (14,719 bytes)   

drmoore

2007-12-28 21:49

reporter   ~0004614

added a version of the patch (sans paul's bugfixes) based on the current trunk. It needed some minor modifications to smf_source.cc to compile.

csuwi

2008-10-23 14:40

reporter   ~0005208

This can be closed. All seems good in 2.6.

seablade

2008-11-24 11:45

manager   ~0005332

Resolving issue as it seems to be fixed in recent versions of Ardour per OP's observations. If there is still any issue post up and I will reopen.

     Seablade

system

2020-04-19 20:12

developer   ~0021449

Issue has been closed automatically, by Trigger Close Plugin.
Feel free to re-open with additional information if you think the issue is not resolved.

Issue History

Date Modified Username Field Change
2005-03-29 11:31 csuwi New Issue
2005-03-29 11:31 csuwi E-mail => bjb-ardour-dev@deus.net
2005-03-29 11:31 csuwi Name => Ben Bell
2005-04-01 12:43 paul Note Added: 0002154
2005-04-01 12:43 paul Status new => feedback
2005-04-01 16:40 csuwi Note Added: 0002157
2007-12-21 13:17 drmoore File Added: dm_timesig_patch
2007-12-21 13:20 drmoore Note Added: 0004609
2007-12-21 15:01 paul Note Added: 0004610
2007-12-28 21:46 drmoore File Added: dm_timesig_patch_trunk
2007-12-28 21:49 drmoore Note Added: 0004614
2008-10-23 14:40 csuwi Note Added: 0005208
2008-11-24 11:45 seablade cost => 0.00
2008-11-24 11:45 seablade Status feedback => resolved
2008-11-24 11:45 seablade Resolution open => fixed
2008-11-24 11:45 seablade Assigned To => seablade
2008-11-24 11:45 seablade Note Added: 0005332
2020-04-19 20:12 system Note Added: 0021449
2020-04-19 20:12 system Status resolved => closed