Tempo ramps - first stab at metric marks locked to frames or beats.
[ardour.git] / gtk2_ardour / tempo_dialog.cc
1 /*
2     Copyright (C) 2000-2007 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <cstdio> // for snprintf, grrr
21
22 #include <gtkmm/stock.h>
23
24 #include "gtkmm2ext/utils.h"
25
26 #include "tempo_dialog.h"
27 #include "ui_config.h"
28
29 #include "i18n.h"
30
31 using namespace std;
32 using namespace Gtk;
33 using namespace Gtkmm2ext;
34 using namespace ARDOUR;
35 using namespace PBD;
36
37 TempoDialog::TempoDialog (TempoMap& map, framepos_t frame, const string&)
38         : ArdourDialog (_("New Tempo"))
39         , bpm_adjustment (60.0, 1.0, 999.9, 0.1, 1.0)
40         , bpm_spinner (bpm_adjustment)
41         , when_bar_label (_("bar:"), ALIGN_LEFT, ALIGN_CENTER)
42         , when_beat_label (_("beat:"), ALIGN_LEFT, ALIGN_CENTER)
43         , pulse_selector_label (_("Pulse note"), ALIGN_LEFT, ALIGN_CENTER)
44         , tap_tempo_button (_("Tap tempo"))
45 {
46         Timecode::BBT_Time when;
47         Tempo tempo (map.tempo_at (frame));
48         map.bbt_time (frame, when);
49
50         init (when, tempo.beats_per_minute(), tempo.note_type(), TempoSection::Constant, true, PositionLockStyle::MusicTime);
51 }
52
53 TempoDialog::TempoDialog (TempoMap& map, TempoSection& section, const string&)
54         : ArdourDialog (_("Edit Tempo"))
55         , bpm_adjustment (60.0, 1.0, 999.9, 0.1, 1.0)
56         , bpm_spinner (bpm_adjustment)
57         , when_bar_label (_("bar:"), ALIGN_LEFT, ALIGN_CENTER)
58         , when_beat_label (_("beat:"), ALIGN_LEFT, ALIGN_CENTER)
59         , pulse_selector_label (_("Pulse note"), ALIGN_LEFT, ALIGN_CENTER)
60         , tap_tempo_button (_("Tap tempo"))
61 {
62         Timecode::BBT_Time when;
63         map.bbt_time (map.frame_at_beat (section.beat()), when);
64         init (when, section.beats_per_minute(), section.note_type(), section.type(), section.movable(), section.position_lock_style());
65 }
66
67 void
68 TempoDialog::init (const Timecode::BBT_Time& when, double bpm, double note_type, TempoSection::Type type, bool movable, PositionLockStyle style)
69 {
70         vector<string> strings;
71         NoteTypes::iterator x;
72
73         bpm_spinner.set_numeric (true);
74         bpm_spinner.set_digits (2);
75         bpm_spinner.set_wrap (true);
76         bpm_spinner.set_value (bpm);
77         bpm_spinner.set_alignment (1.0);
78
79         note_types.insert (make_pair (_("whole"), 1.0));
80         strings.push_back (_("whole"));
81         note_types.insert (make_pair (_("second"), 2.0));
82         strings.push_back (_("second"));
83         note_types.insert (make_pair (_("third"), 3.0));
84         strings.push_back (_("third"));
85         note_types.insert (make_pair (_("quarter"), 4.0));
86         strings.push_back (_("quarter"));
87         note_types.insert (make_pair (_("eighth"), 8.0));
88         strings.push_back (_("eighth"));
89         note_types.insert (make_pair (_("sixteenth"), 16.0));
90         strings.push_back (_("sixteenth"));
91         note_types.insert (make_pair (_("thirty-second"), 32.0));
92         strings.push_back (_("thirty-second"));
93         note_types.insert (make_pair (_("sixty-fourth"), 64.0));
94         strings.push_back (_("sixty-fourth"));
95         note_types.insert (make_pair (_("one-hundred-twenty-eighth"), 128.0));
96         strings.push_back (_("one-hundred-twenty-eighth"));
97
98         set_popdown_strings (pulse_selector, strings);
99
100         for (x = note_types.begin(); x != note_types.end(); ++x) {
101                 if (x->second == note_type) {
102                         pulse_selector.set_active_text (x->first);
103                         break;
104                 }
105         }
106
107         if (x == note_types.end()) {
108                 pulse_selector.set_active_text (strings[3]); // "quarter"
109         }
110
111         strings.clear();
112
113         tempo_types.insert (make_pair (_("ramped"), TempoSection::Ramp));
114         strings.push_back (_("ramped"));
115         tempo_types.insert (make_pair (_("constant"), TempoSection::Constant));
116         strings.push_back (_("constant"));
117         set_popdown_strings (tempo_type, strings);
118         TempoTypes::iterator tt;
119         for (tt = tempo_types.begin(); tt != tempo_types.end(); ++tt) {
120                 if (tt->second == type) {
121                         tempo_type.set_active_text (tt->first);
122                         break;
123                 }
124         }
125         if (tt == tempo_types.end()) {
126                 tempo_type.set_active_text (strings[1]); // "constant"
127         }
128
129         strings.clear();
130
131         lock_styles.insert (make_pair (_("music"), PositionLockStyle::MusicTime));
132         strings.push_back (_("music"));
133         lock_styles.insert (make_pair (_("audio"), PositionLockStyle::AudioTime));
134         strings.push_back (_("audio"));
135         set_popdown_strings (lock_style, strings);
136         LockStyles::iterator ls;
137         for (ls = lock_styles.begin(); ls != lock_styles.end(); ++ls) {
138                 if (ls->second == style) {
139                         lock_style.set_active_text (ls->first);
140                         break;
141                 }
142         }
143         if (ls == lock_styles.end()) {
144                 lock_style.set_active_text (strings[0]); // "music"
145         }
146
147         Table* table;
148
149         if (UIConfiguration::instance().get_allow_non_quarter_pulse()) {
150                 table = manage (new Table (5, 7));
151         } else {
152                 table = manage (new Table (5, 6));
153         }
154
155         table->set_spacings (6);
156         table->set_homogeneous (false);
157
158         int row;
159         Label* bpm_label = manage (new Label(_("Beats per minute:"), ALIGN_LEFT, ALIGN_CENTER));
160         table->attach (*bpm_label, 0, 1, 0, 1);
161         table->attach (bpm_spinner, 1, 5, 0, 1);
162
163         if (UIConfiguration::instance().get_allow_non_quarter_pulse()) {
164                 table->attach (pulse_selector_label, 0, 1, 1, 2);
165                 table->attach (pulse_selector, 1, 5, 1, 2);
166                 row = 2;
167         } else {
168                 row = 1;
169         }
170
171         char buf[64];
172
173         snprintf (buf, sizeof (buf), "%" PRIu32, when.bars);
174         when_bar_entry.set_text (buf);
175         snprintf (buf, sizeof (buf), "%" PRIu32, when.beats);
176         when_beat_entry.set_text (buf);
177
178         if (movable) {
179                 when_bar_entry.set_width_chars(4);
180                 when_beat_entry.set_width_chars (4);
181                 when_bar_entry.set_alignment (1.0);
182                 when_beat_entry.set_alignment (1.0);
183
184                 when_bar_label.set_name ("MetricLabel");
185                 when_beat_label.set_name ("MetricLabel");
186
187                 table->attach (when_bar_label, 1, 2, row, row+1, Gtk::AttachOptions(0), Gtk::AttachOptions(0));
188                 table->attach (when_bar_entry, 2, 3, row, row+1, Gtk::AttachOptions(0), Gtk::AttachOptions(0));
189
190                 table->attach (when_beat_label, 3, 4, row, row+1, Gtk::AttachOptions(0), Gtk::AttachOptions(0));
191                 table->attach (when_beat_entry, 4, 5, row, row+1, Gtk::AttachOptions(0), Gtk::AttachOptions(0));
192
193                 Label* when_label = manage (new Label(_("Tempo begins at"), ALIGN_LEFT, ALIGN_CENTER));
194                 table->attach (*when_label, 0, 1, row, row+1);
195         }
196
197         Label* tempo_type_label = manage (new Label(_("Tempo Type:"), ALIGN_LEFT, ALIGN_CENTER));
198         table->attach (*tempo_type_label, 0, 1, row+1, row+2);
199         table->attach (tempo_type, 1, 2, row+1, row + 2);
200         get_vbox()->set_border_width (12);
201         get_vbox()->pack_end (*table);
202
203         Label* lock_style_label = manage (new Label(_("Lock Style:"), ALIGN_LEFT, ALIGN_CENTER));
204         table->attach (*lock_style_label, 0, 1, row+2, row+3);
205         table->attach (lock_style, 1, 2, row+2, row + 3);
206         get_vbox()->set_border_width (12);
207         get_vbox()->pack_end (*table);
208
209         table->show_all ();
210
211         add_button (Stock::CANCEL, RESPONSE_CANCEL);
212         add_button (Stock::APPLY, RESPONSE_ACCEPT);
213         set_response_sensitive (RESPONSE_ACCEPT, true);
214         set_default_response (RESPONSE_ACCEPT);
215
216         bpm_spinner.show ();
217         tap_tempo_button.show ();
218         get_vbox()->pack_end (tap_tempo_button);
219         bpm_spinner.grab_focus ();
220
221         set_name ("MetricDialog");
222
223         bpm_spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &TempoDialog::response), RESPONSE_ACCEPT));
224         bpm_spinner.signal_button_press_event().connect (sigc::mem_fun (*this, &TempoDialog::bpm_button_press), false);
225         bpm_spinner.signal_button_release_event().connect (sigc::mem_fun (*this, &TempoDialog::bpm_button_release), false);
226         bpm_spinner.signal_changed().connect (sigc::mem_fun (*this, &TempoDialog::bpm_changed));
227         when_bar_entry.signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &TempoDialog::response), RESPONSE_ACCEPT));
228         when_bar_entry.signal_key_release_event().connect (sigc::mem_fun (*this, &TempoDialog::entry_key_release), false);
229         when_beat_entry.signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &TempoDialog::response), RESPONSE_ACCEPT));
230         when_beat_entry.signal_key_release_event().connect (sigc::mem_fun (*this, &TempoDialog::entry_key_release), false);
231         pulse_selector.signal_changed().connect (sigc::mem_fun (*this, &TempoDialog::pulse_change));
232         tempo_type.signal_changed().connect (sigc::mem_fun (*this, &TempoDialog::tempo_type_change));
233         lock_style.signal_changed().connect (sigc::mem_fun (*this, &TempoDialog::lock_style_change));
234         tap_tempo_button.signal_button_press_event().connect (sigc::mem_fun (*this, &TempoDialog::tap_tempo_button_press), false);
235         tap_tempo_button.signal_focus_out_event().connect (sigc::mem_fun (*this, &TempoDialog::tap_tempo_focus_out));
236
237         tapped = false;
238 }
239
240 bool
241 TempoDialog::is_user_input_valid() const
242 {
243         return (when_beat_entry.get_text() != "")
244                 && (when_bar_entry.get_text() != "")
245                 && (when_bar_entry.get_text() != "0");
246 }
247
248 void
249 TempoDialog::bpm_changed ()
250 {
251         set_response_sensitive (RESPONSE_ACCEPT, is_user_input_valid());
252 }
253
254 bool
255 TempoDialog::bpm_button_press (GdkEventButton*)
256 {
257         return false;
258 }
259
260 bool
261 TempoDialog::bpm_button_release (GdkEventButton*)
262 {
263         /* the value has been modified, accept should work now */
264
265         set_response_sensitive (RESPONSE_ACCEPT, is_user_input_valid());
266         return false;
267 }
268
269 bool
270 TempoDialog::entry_key_release (GdkEventKey*)
271 {
272         set_response_sensitive (RESPONSE_ACCEPT, is_user_input_valid());
273         return false;
274 }
275
276 double
277 TempoDialog::get_bpm ()
278 {
279         return bpm_spinner.get_value ();
280 }
281
282 bool
283 TempoDialog::get_bbt_time (Timecode::BBT_Time& requested)
284 {
285         if (sscanf (when_bar_entry.get_text().c_str(), "%" PRIu32, &requested.bars) != 1) {
286                 return false;
287         }
288
289         if (sscanf (when_beat_entry.get_text().c_str(), "%" PRIu32, &requested.beats) != 1) {
290                 return false;
291         }
292
293         requested.ticks = 0;
294
295         return true;
296 }
297
298 double
299 TempoDialog::get_note_type ()
300 {
301         NoteTypes::iterator x = note_types.find (pulse_selector.get_active_text());
302
303         if (x == note_types.end()) {
304                 error << string_compose(_("incomprehensible pulse note type (%1)"), pulse_selector.get_active_text()) << endmsg;
305                 return 0;
306         }
307
308         return x->second;
309 }
310
311 TempoSection::Type
312 TempoDialog::get_tempo_type ()
313 {
314         TempoTypes::iterator x = tempo_types.find (tempo_type.get_active_text());
315
316         if (x == tempo_types.end()) {
317                 error << string_compose(_("incomprehensible tempo type (%1)"), tempo_type.get_active_text()) << endmsg;
318                 return TempoSection::Constant;
319         }
320
321         return x->second;
322 }
323
324 PositionLockStyle
325 TempoDialog::get_lock_style ()
326 {
327         LockStyles::iterator x = lock_styles.find (lock_style.get_active_text());
328
329         if (x == lock_styles.end()) {
330                 error << string_compose(_("incomprehensible lock style (%1)"), lock_style.get_active_text()) << endmsg;
331                 return PositionLockStyle::MusicTime;
332         }
333
334         return x->second;
335 }
336
337 void
338 TempoDialog::pulse_change ()
339 {
340         set_response_sensitive (RESPONSE_ACCEPT, is_user_input_valid());
341 }
342
343 void
344 TempoDialog::tempo_type_change ()
345 {
346         set_response_sensitive (RESPONSE_ACCEPT, is_user_input_valid());
347 }
348
349 void
350 TempoDialog::lock_style_change ()
351 {
352         set_response_sensitive (RESPONSE_ACCEPT, is_user_input_valid());
353 }
354
355 bool
356 TempoDialog::tap_tempo_button_press (GdkEventButton *ev)
357 {
358         double t;
359
360         // Linear least-squares regression
361         if (tapped) {
362                 t = 1e-6 * (g_get_monotonic_time () - first_t); // Subtract first_t to avoid precision problems
363
364                 double n = tap_count;
365                 sum_y += t;
366                 sum_x += n;
367                 sum_xy += n * t;
368                 sum_xx += n * n;
369                 double T = (sum_xy/n - sum_x/n * sum_y/n) / (sum_xx/n - sum_x/n * sum_x/n);
370
371                 if (t - last_t < T / 1.2 || t - last_t > T * 1.2) {
372                         tapped = false;
373                 } else {
374                         bpm_spinner.set_value (60.0 / T);
375                 }
376         }
377         if (!tapped) {
378                 first_t = g_get_monotonic_time ();
379                 t = 0.0;
380                 sum_y = 0.0;
381                 sum_x = 1.0;
382                 sum_xy = 0.0;
383                 sum_xx = 1.0;
384                 tap_count = 1.0;
385
386                 tapped = true;
387         }
388         tap_count++;
389         last_t = t;
390
391         return true;
392 }
393
394 bool
395 TempoDialog::tap_tempo_focus_out (GdkEventFocus* )
396 {
397         tapped = false;
398         return false;
399 }
400
401 MeterDialog::MeterDialog (TempoMap& map, framepos_t frame, const string&)
402         : ArdourDialog (_("New Meter"))
403 {
404         Timecode::BBT_Time when;
405         frame = map.round_to_bar(frame, RoundNearest);
406         Meter meter (map.meter_at(frame));
407
408         map.bbt_time (frame, when);
409         init (when, meter.divisions_per_bar(), meter.note_divisor(), true, PositionLockStyle::MusicTime);
410 }
411
412 MeterDialog::MeterDialog (TempoMap& map, MeterSection& section, const string&)
413         : ArdourDialog (_("Edit Meter"))
414 {
415         Timecode::BBT_Time when;
416         map.bbt_time (map.frame_at_beat (section.beat()), when);
417         init (when, section.divisions_per_bar(), section.note_divisor(), section.movable(), section.position_lock_style());
418 }
419
420 void
421 MeterDialog::init (const Timecode::BBT_Time& when, double bpb, double divisor, bool movable, PositionLockStyle style)
422 {
423         char buf[64];
424         vector<string> strings;
425         NoteTypes::iterator x;
426
427         snprintf (buf, sizeof (buf), "%.2f", bpb);
428         bpb_entry.set_text (buf);
429         bpb_entry.select_region (0, -1);
430         bpb_entry.set_alignment (1.0);
431
432         note_types.insert (make_pair (_("whole"), 1.0));
433         strings.push_back (_("whole"));
434         note_types.insert (make_pair (_("second"), 2.0));
435         strings.push_back (_("second"));
436         note_types.insert (make_pair (_("third"), 3.0));
437         strings.push_back (_("third"));
438         note_types.insert (make_pair (_("quarter"), 4.0));
439         strings.push_back (_("quarter"));
440         note_types.insert (make_pair (_("eighth"), 8.0));
441         strings.push_back (_("eighth"));
442         note_types.insert (make_pair (_("sixteenth"), 16.0));
443         strings.push_back (_("sixteenth"));
444         note_types.insert (make_pair (_("thirty-second"), 32.0));
445         strings.push_back (_("thirty-second"));
446         note_types.insert (make_pair (_("sixty-fourth"), 64.0));
447         strings.push_back (_("sixty-fourth"));
448         note_types.insert (make_pair (_("one-hundred-twenty-eighth"), 128.0));
449         strings.push_back (_("one-hundred-twenty-eighth"));
450
451         set_popdown_strings (note_type, strings);
452
453         for (x = note_types.begin(); x != note_types.end(); ++x) {
454                 if (x->second == divisor) {
455                         note_type.set_active_text (x->first);
456                         break;
457                 }
458         }
459
460         if (x == note_types.end()) {
461                 note_type.set_active_text (strings[3]); // "quarter"
462         }
463
464         strings.clear();
465
466         lock_styles.insert (make_pair (_("music"), PositionLockStyle::MusicTime));
467         strings.push_back (_("music"));
468         lock_styles.insert (make_pair (_("audio ur brane wul xplod"), PositionLockStyle::AudioTime));
469         strings.push_back (_("audio"));
470         set_popdown_strings (lock_style, strings);
471         LockStyles::iterator ls;
472         for (ls = lock_styles.begin(); ls != lock_styles.end(); ++ls) {
473                 if (ls->second == style) {
474                         lock_style.set_active_text (ls->first);
475                         break;
476                 }
477         }
478         if (ls == lock_styles.end()) {
479                 lock_style.set_active_text (strings[0]); // "music"
480         }
481
482         Label* note_label = manage (new Label (_("Note value:"), ALIGN_LEFT, ALIGN_CENTER));
483         Label* lock_label = manage (new Label (_("Lock style:"), ALIGN_LEFT, ALIGN_CENTER));
484         Label* bpb_label = manage (new Label (_("Beats per bar:"), ALIGN_LEFT, ALIGN_CENTER));
485         Table* table = manage (new Table (3, 3));
486         table->set_spacings (6);
487
488         table->attach (*bpb_label, 0, 1, 0, 1, FILL|EXPAND, FILL|EXPAND);
489         table->attach (bpb_entry, 1, 2, 0, 1, FILL|EXPAND, FILL|EXPAND);
490         table->attach (*note_label, 0, 1, 1, 2, FILL|EXPAND, FILL|EXPAND);
491         table->attach (note_type, 1, 2, 1, 2, FILL|EXPAND, SHRINK);
492
493         snprintf (buf, sizeof (buf), "%" PRIu32, when.bars);
494         when_bar_entry.set_text (buf);
495         when_bar_entry.set_alignment (1.0);
496
497         if (movable) {
498                 Label* when_label = manage (new Label(_("Meter begins at bar:"), ALIGN_LEFT, ALIGN_CENTER));
499
500                 table->attach (*when_label, 0, 1, 2, 3, FILL | EXPAND, FILL | EXPAND);
501                 table->attach (when_bar_entry, 1, 2, 2, 3, FILL | EXPAND, FILL | EXPAND);
502         }
503
504         table->attach (*lock_label, 0, 1, 3, 4, FILL|EXPAND, FILL|EXPAND);
505         table->attach (lock_style, 1, 2, 3, 4, FILL|EXPAND, SHRINK);
506
507         get_vbox()->set_border_width (12);
508         get_vbox()->pack_start (*table, false, false);
509
510         add_button (Stock::CANCEL, RESPONSE_CANCEL);
511         add_button (Stock::APPLY, RESPONSE_ACCEPT);
512         set_response_sensitive (RESPONSE_ACCEPT, true);
513         set_default_response (RESPONSE_ACCEPT);
514
515         get_vbox()->show_all ();
516
517         set_name ("MetricDialog");
518         bpb_entry.signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &MeterDialog::response), RESPONSE_ACCEPT));
519         bpb_entry.signal_key_press_event().connect (sigc::mem_fun (*this, &MeterDialog::entry_key_press), false);
520         bpb_entry.signal_key_release_event().connect (sigc::mem_fun (*this, &MeterDialog::entry_key_release));
521         when_bar_entry.signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &MeterDialog::response), RESPONSE_ACCEPT));
522         when_bar_entry.signal_key_press_event().connect (sigc::mem_fun (*this, &MeterDialog::entry_key_press), false);
523         when_bar_entry.signal_key_release_event().connect (sigc::mem_fun (*this, &MeterDialog::entry_key_release));
524         note_type.signal_changed().connect (sigc::mem_fun (*this, &MeterDialog::note_type_change));
525         lock_style.signal_changed().connect (sigc::mem_fun (*this, &MeterDialog::lock_style_change));
526
527 }
528
529 bool
530 MeterDialog::is_user_input_valid() const
531 {
532         return (when_bar_entry.get_text() != "")
533                 && (when_bar_entry.get_text() != "0")
534                 && (bpb_entry.get_text() != "");
535 }
536
537 bool
538 MeterDialog::entry_key_press (GdkEventKey* ev)
539 {
540
541         switch (ev->keyval) {
542
543         case GDK_0:
544         case GDK_1:
545         case GDK_2:
546         case GDK_3:
547         case GDK_4:
548         case GDK_5:
549         case GDK_6:
550         case GDK_7:
551         case GDK_8:
552         case GDK_9:
553         case GDK_KP_0:
554         case GDK_KP_1:
555         case GDK_KP_2:
556         case GDK_KP_3:
557         case GDK_KP_4:
558         case GDK_KP_5:
559         case GDK_KP_6:
560         case GDK_KP_7:
561         case GDK_KP_8:
562         case GDK_KP_9:
563         case GDK_period:
564         case GDK_comma:
565         case  GDK_KP_Delete:
566         case  GDK_KP_Enter:
567         case  GDK_Delete:
568         case  GDK_BackSpace:
569         case  GDK_Escape:
570         case  GDK_Return:
571         case  GDK_Home:
572         case  GDK_End:
573         case  GDK_Left:
574         case  GDK_Right:
575         case  GDK_Num_Lock:
576         case  GDK_Tab:
577                 return FALSE;
578         default:
579                 break;
580         }
581
582         return TRUE;
583 }
584
585 bool
586 MeterDialog::entry_key_release (GdkEventKey*)
587 {
588         set_response_sensitive (RESPONSE_ACCEPT, is_user_input_valid());
589         return false;
590 }
591
592 void
593 MeterDialog::note_type_change ()
594 {
595         set_response_sensitive (RESPONSE_ACCEPT, is_user_input_valid());
596 }
597
598 void
599 MeterDialog::lock_style_change ()
600 {
601         set_response_sensitive (RESPONSE_ACCEPT, is_user_input_valid());
602 }
603
604 double
605 MeterDialog::get_bpb ()
606 {
607         double bpb = 0;
608
609         if (sscanf (bpb_entry.get_text().c_str(), "%lf", &bpb) != 1) {
610                 return 0;
611         }
612
613         return bpb;
614 }
615
616 double
617 MeterDialog::get_note_type ()
618 {
619         NoteTypes::iterator x = note_types.find (note_type.get_active_text());
620
621         if (x == note_types.end()) {
622                 error << string_compose(_("incomprehensible meter note type (%1)"), note_type.get_active_text()) << endmsg;
623                 return 0;
624         }
625
626         return x->second;
627 }
628
629 PositionLockStyle
630 MeterDialog::get_lock_style ()
631 {
632         LockStyles::iterator x = lock_styles.find (lock_style.get_active_text());
633
634         if (x == lock_styles.end()) {
635                 error << string_compose(_("incomprehensible meter lock style (%1)"), lock_style.get_active_text()) << endmsg;
636                 return PositionLockStyle::MusicTime;
637         }
638
639         return x->second;
640 }
641
642 bool
643 MeterDialog::get_bbt_time (Timecode::BBT_Time& requested)
644 {
645         if (sscanf (when_bar_entry.get_text().c_str(), "%" PRIu32, &requested.bars) != 1) {
646                 return false;
647         }
648
649         requested.beats = 1;
650         requested.ticks = 0;
651
652         return true;
653 }