--- /dev/null
+/* Copyright 2009-2010 Yorba Foundation
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later). See the COPYING file in this distribution.
+ */
+
+public class ProjectProperties : Gtk.Dialog {
+ const int NUMBER_OF_SAMPLES = 4;
+ const long G_USEC_PER_SEC = 1000000;
+
+ TimeVal[] tap_time = new TimeVal[NUMBER_OF_SAMPLES];
+ int tap_index = 0;
+ bool sample_full = false;
+ Gtk.Adjustment tempo_adjustment;
+ Gtk.Adjustment volume_adjustment;
+ Gtk.ComboBox timesignature_combo;
+ Gtk.ToggleButton click_during_play;
+ Gtk.ToggleButton click_during_record;
+
+ public void setup(Model.Project project, Gtk.Builder builder) {
+ tempo_adjustment = (Gtk.Adjustment) builder.get_object("tempo_adjustment");
+ volume_adjustment = (Gtk.Adjustment) builder.get_object("volume_adjustment");
+ timesignature_combo = (Gtk.ComboBox) builder.get_object("timesignature_combo");
+ click_during_play = (Gtk.ToggleButton) builder.get_object("playback");
+ click_during_record = (Gtk.ToggleButton) builder.get_object("record");
+ set_tempo(project.get_bpm());
+ set_volume(project.click_volume);
+ set_during_play(project.click_during_play);
+ set_during_record(project.click_during_record);
+ set_time_signature(project.get_time_signature());
+ }
+
+ public void set_tempo(int tempo) {
+ tempo_adjustment.set_value(tempo);
+ }
+
+ public int get_tempo() {
+ return (int) tempo_adjustment.get_value();
+ }
+
+ public void set_volume(double volume) {
+ volume_adjustment.set_value(volume);
+ }
+
+ public double get_click_volume() {
+ return volume_adjustment.get_value();
+ }
+
+ public Fraction get_time_signature() {
+ return Fraction.from_string(timesignature_combo.get_active_text());
+ }
+
+ void set_time_signature(Fraction time_signature) {
+ string sig = time_signature.to_string();
+
+ Gtk.TreeIter iter;
+ if (timesignature_combo.model.get_iter_first(out iter)) {
+ do {
+ string s;
+ timesignature_combo.model.get(iter, 0, out s, -1);
+ if (s == sig) {
+ timesignature_combo.set_active_iter(iter);
+ return;
+ }
+ } while (timesignature_combo.model.iter_next(ref iter));
+ }
+ }
+
+ void set_during_play(bool active) {
+ click_during_play.active = active;
+ }
+
+ public bool during_play() {
+ return click_during_play.active;
+ }
+
+ void set_during_record(bool active) {
+ click_during_record.active = active;
+ }
+
+ public bool during_record() {
+ return click_during_record.active;
+ }
+
+ public void on_tap() {
+ TimeVal time_val = TimeVal();
+ time_val.get_current_time();
+ tap_time[tap_index] = time_val;
+ ++tap_index;
+ if (tap_index == NUMBER_OF_SAMPLES) {
+ sample_full = true;
+ tap_index = 0;
+ }
+ calculate_bpm();
+ }
+
+ void calculate_bpm() {
+ int number_of_samples = sample_full ? NUMBER_OF_SAMPLES : tap_index;
+ if (number_of_samples < 2) {
+ return;
+ }
+
+ int start_index = sample_full ? tap_index : 0;
+
+ double delta_sum = 0;
+ for (int i = 0; i < number_of_samples - 1; ++i) {
+ int current_index = (i + start_index) % NUMBER_OF_SAMPLES;
+ int next_index = (current_index + 1) % NUMBER_OF_SAMPLES;
+ long difference =
+ (tap_time[next_index].tv_sec - tap_time[current_index].tv_sec) * G_USEC_PER_SEC +
+ tap_time[next_index].tv_usec - tap_time[current_index].tv_usec;
+
+ if (difference > 5 * G_USEC_PER_SEC) {
+ // User waited too long. Save the time and start over
+ tap_time[0] = tap_time[tap_index - 1];
+ sample_full = false;
+ tap_index = 1;
+ return;
+ }
+ delta_sum += difference;
+ }
+ double average = delta_sum/(number_of_samples - 1)/G_USEC_PER_SEC;
+ tempo_adjustment.set_value((int)(60.0/average));
+ }
+}