Adding first code drop
authordruid23 <usr@dru-id.co.uk>
Tue, 28 Sep 2010 20:58:25 +0000 (21:58 +0100)
committerdruid23 <usr@dru-id.co.uk>
Tue, 28 Sep 2010 20:58:25 +0000 (21:58 +0100)
new file:   build.sh
new file:   src/.directory
new file:   src/AcmPopUp.vala
new file:   src/AppSettings.vala
new file:   src/ApplicationSettings.vala
new file:   src/AudioCaptureMonitor.vala
new file:   src/CdlParser.vala
new file:   src/Constants.vala
new file:   src/DbMonitorWidget.vala
new file:   src/DemoRecorder.vala
new file:   src/DynamicsPopUp.vala
new file:   src/EchoPopUp.vala
new file:   src/EncodePipeline.vala
new file:   src/EncoderFactory.vala
new file:   src/EqualizerPopUp.vala
new file:   src/EqualizerPreset.vala
new file:   src/FirstRunWizard.vala
new file:   src/GraphicEqualizer.vala
new file:   src/IXmlSerializable.vala
new file:   src/ImportBin.vala
new file:   src/InputBin.vala
new file:   src/MixdownPopUp.vala
new file:   src/MixerBin.vala
new file:   src/PlayPipeline.vala
new file:   src/PlayerTransport.vala
new file:   src/Program.vala
new file:   src/ProgressPopUp.vala
new file:   src/ProjectSettings.vala
new file:   src/ProjectSettingsDialog.vala
new file:   src/RecordPipeline.vala
new file:   src/RecordPipeline_Old.vala
new file:   src/SettingsStructures.vala
new file:   src/SimpleHashMap.vala
new file:   src/Tagging.vala
new file:   src/Track.vala
new file:   src/TrackBin.vala
new file:   src/TrackEffectsPopUp.vala
new file:   src/TrackPipeline.vala
new file:   src/TrackTransport.vala
new file:   src/VolumeAndPanPopUp.vala
new file:   src/XmlHelpers.vala
new file:   src/YesNoCancelDialog.vala

42 files changed:
build.sh [new file with mode: 0755]
src/.directory [new file with mode: 0644]
src/AcmPopUp.vala [new file with mode: 0644]
src/AppSettings.vala [new file with mode: 0644]
src/ApplicationSettings.vala [new file with mode: 0644]
src/AudioCaptureMonitor.vala [new file with mode: 0644]
src/CdlParser.vala [new file with mode: 0644]
src/Constants.vala [new file with mode: 0644]
src/DbMonitorWidget.vala [new file with mode: 0644]
src/DemoRecorder.vala [new file with mode: 0644]
src/DynamicsPopUp.vala [new file with mode: 0644]
src/EchoPopUp.vala [new file with mode: 0644]
src/EncodePipeline.vala [new file with mode: 0644]
src/EncoderFactory.vala [new file with mode: 0644]
src/EqualizerPopUp.vala [new file with mode: 0644]
src/EqualizerPreset.vala [new file with mode: 0644]
src/FirstRunWizard.vala [new file with mode: 0644]
src/GraphicEqualizer.vala [new file with mode: 0644]
src/IXmlSerializable.vala [new file with mode: 0644]
src/ImportBin.vala [new file with mode: 0644]
src/InputBin.vala [new file with mode: 0644]
src/MixdownPopUp.vala [new file with mode: 0644]
src/MixerBin.vala [new file with mode: 0644]
src/PlayPipeline.vala [new file with mode: 0644]
src/PlayerTransport.vala [new file with mode: 0644]
src/Program.vala [new file with mode: 0644]
src/ProgressPopUp.vala [new file with mode: 0644]
src/ProjectSettings.vala [new file with mode: 0644]
src/ProjectSettingsDialog.vala [new file with mode: 0644]
src/RecordPipeline.vala [new file with mode: 0644]
src/RecordPipeline_Old.vala [new file with mode: 0644]
src/SettingsStructures.vala [new file with mode: 0644]
src/SimpleHashMap.vala [new file with mode: 0644]
src/Tagging.vala [new file with mode: 0644]
src/Track.vala [new file with mode: 0644]
src/TrackBin.vala [new file with mode: 0644]
src/TrackEffectsPopUp.vala [new file with mode: 0644]
src/TrackPipeline.vala [new file with mode: 0644]
src/TrackTransport.vala [new file with mode: 0644]
src/VolumeAndPanPopUp.vala [new file with mode: 0644]
src/XmlHelpers.vala [new file with mode: 0644]
src/YesNoCancelDialog.vala [new file with mode: 0644]

diff --git a/build.sh b/build.sh
new file mode 100755 (executable)
index 0000000..3273d59
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,48 @@
+#! /bin/bash
+mkdir bin
+#/home/druid23/MyDocs/vala/vala-0.9.4/compiler/valac \
+valac \
+--pkg gtk+-2.0 \
+--pkg hildon-1 \
+--pkg hildon-fm-2 \
+--pkg gstreamer-0.10 \
+--pkg libxml-2.0 \
+--pkg libosso \
+-o bin/demorecorder \
+src/Program.vala \
+src/DemoRecorder.vala \
+src/ApplicationSettings.vala \
+src/AcmPopUp.vala \
+src/AppSettings.vala \
+src/AudioCaptureMonitor.vala \
+src/CdlParser.vala \
+src/DbMonitorWidget.vala \
+src/DynamicsPopUp.vala \
+src/EchoPopUp.vala \
+src/EncoderFactory.vala \
+src/EncodePipeline.vala \
+src/EqualizerPopUp.vala \
+src/FirstRunWizard.vala \
+src/GraphicEqualizer.vala \
+src/InputBin.vala \
+src/ImportBin.vala \
+src/MixdownPopUp.vala \
+src/MixerBin.vala \
+src/Constants.vala \
+src/PlayerTransport.vala \
+src/PlayPipeline.vala \
+src/ProgressPopUp.vala \
+src/ProjectSettings.vala \
+src/ProjectSettingsDialog.vala \
+src/RecordPipeline.vala \
+src/SettingsStructures.vala \
+src/SimpleHashMap.vala \
+src/Tagging.vala \
+src/Track.vala \
+src/TrackBin.vala \
+src/TrackEffectsPopUp.vala \
+src/TrackPipeline.vala \
+src/TrackTransport.vala \
+src/VolumeAndPanPopUp.vala \
+src/XmlHelpers.vala \
+src/YesNoCancelDialog.vala
diff --git a/src/.directory b/src/.directory
new file mode 100644 (file)
index 0000000..4df1ffe
--- /dev/null
@@ -0,0 +1,8 @@
+[Dolphin]
+AdditionalInfo=35
+Sorting=6
+Timestamp=2010,9,28,21,33,50
+ViewMode=1
+
+[Settings]
+ShowDotFiles=true
diff --git a/src/AcmPopUp.vala b/src/AcmPopUp.vala
new file mode 100644 (file)
index 0000000..1a618f0
--- /dev/null
@@ -0,0 +1,291 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+namespace IdWorks {
+  
+  public class AcmPopUp : Hildon.Dialog {
+    
+    SettingsStructures.MonitorSettings settings;
+    AudioCaptureMonitor acm;
+    Gtk.Label lbl_level_1;
+    Gtk.Label lbl_level_2;
+    Gtk.HScale pregain_slider;
+    Gtk.HScale volume_slider;
+    Hildon.Button dynamics_settings_btn;
+    Hildon.CheckButton dynamics_btn;
+    Hildon.TouchSelector source_selector;
+    DbMonitorWidget monitor_1;
+    DbMonitorWidget monitor_2;
+    const string level_format = "%02.1f/%02.1fdB";
+    
+    public AcmPopUp(string title, Gtk.Widget parent) {
+      this.set_title(title);
+      this.set_parent(parent);
+      this.settings = SettingsStructures.MonitorSettings();
+      this.construct_interface();
+      update_settings();
+    }
+    public AcmPopUp.with_settings(string title, Gtk.Widget parent, SettingsStructures.MonitorSettings settings) {
+      this.set_title(title);
+      this.set_parent(parent);
+      this.settings = settings;
+      this.construct_interface();
+      update_settings();
+    }
+    
+    public void set_settings_structure(SettingsStructures.MonitorSettings settings) {
+      this.settings = settings;
+      update_settings();
+    }
+    public SettingsStructures.MonitorSettings get_settings_structure() {
+      return this.settings;
+    }
+    
+    private void update_settings() {
+      set_volume(settings.input.volume);
+      set_pregain(settings.input.pregain);
+      //set_source(settings.input.source);
+      dynamics_btn.set_active(settings.input.dynamics.active);
+      toggle_dynamics(settings.input.dynamics.active);
+    }
+    
+    private void set_volume(double val) {
+      volume_slider.set_value(val);
+      settings.input.volume = val;
+      if (null != acm) acm.set_volume(val);
+    }
+    private void set_pregain(double val) {
+      pregain_slider.set_value(val);
+      settings.input.pregain = val;
+      if (null != acm) acm.set_pregain(val);
+    }
+    
+    private void set_source(string name) {
+      switch (name) {
+        case "Microphone":
+          source_selector.set_active(0, 0);
+          break;
+        case "Monitor":
+          source_selector.set_active(0, 1);
+          break;
+        case "Bluetooth":
+          source_selector.set_active(0, 2);
+          break;
+        default:
+          source_selector.set_active(0, 0);
+          break;
+      }
+      settings.input.source = name;
+      if (null != acm) acm.set_source(name);
+    }
+    private void set_input_source(string name) {
+      settings.input.source = name;
+      if (null != acm) acm.set_source(name);
+    }
+    
+    private void construct_interface() {
+      this.set_default_response(Gtk.ResponseType.CANCEL);
+      this.add_button(Gtk.STOCK_OK, Gtk.ResponseType.OK);
+      this.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL);
+      
+      Gtk.VBox control_area = (Gtk.VBox)this.get_content_area();
+      
+      acm = new AudioCaptureMonitor("Recorder");
+      acm.update_level.connect(update_level_callback);
+      
+      Gtk.HButtonBox input_bar = new Gtk.HButtonBox();
+      input_bar.set_layout(Gtk.ButtonBoxStyle.START);
+      input_bar.set_homogeneous(true);
+      input_bar.set_spacing(2);
+      
+      source_selector = new Hildon.TouchSelector.text();
+      source_selector.append_text("Microphone");
+      source_selector.append_text("Monitor");
+      source_selector.append_text("Bluetooth");
+      //source_selector.set_active(0, 0);
+      this.set_source(settings.input.source);
+      Hildon.PickerButton source_picker = new Hildon.PickerButton(Hildon.SizeType.FINGER_HEIGHT | Hildon.SizeType.AUTO_WIDTH, Hildon.ButtonArrangement.VERTICAL);
+      source_picker.set_title("Source:");
+      source_picker.set_selector(source_selector);
+      source_picker.set_alignment(0, 0, 1, 1);
+      source_picker.set_image(new Gtk.Image.from_icon_name("camera_volume_unmuted", Gtk.IconSize.BUTTON));
+      source_picker.value_changed.connect( (s) => { set_input_source(s.get_selector().get_current_text()); } );
+      input_bar.pack_start(source_picker, true, true, 2);
+      
+      Hildon.CheckButton audio_btn = new Hildon.CheckButton(Hildon.SizeType.FINGER_HEIGHT | Hildon.SizeType.AUTO_WIDTH);
+      audio_btn.set_label("Enable monitor");
+      //audio_btn.set_image(new Gtk.Image.from_icon_name("statusarea_volume_headset", Gtk.IconSize.BUTTON));
+      audio_btn.toggled.connect( (s) => {toggle_monitor(s.get_active());} );
+      input_bar.pack_start(audio_btn, true, false, 2);
+      
+      control_area.pack_start(input_bar, true, true, 4);
+      
+      Gtk.HBox monitor_1_bar = new Gtk.HBox(false, 2);
+      lbl_level_1 = new Gtk.Label(level_format.printf(0, 0));
+      monitor_1 = new DbMonitorWidget();
+      monitor_1.set_layout(DbMonitorWidget.Layout.HORIZONTAL);
+      monitor_1.set_peak(1);
+      monitor_1.set_decay(1.5);
+      monitor_1_bar.pack_start(new Gtk.Label("L"), false, false, 2);
+      monitor_1_bar.pack_start(monitor_1, true, true, 2);
+      monitor_1_bar.pack_start(lbl_level_1, false, false, 2);
+      control_area.pack_start(monitor_1_bar, true, true, 2);
+      
+      Gtk.HBox monitor_2_bar = new Gtk.HBox(false, 4);
+      lbl_level_2 = new Gtk.Label(level_format.printf(0, 0));
+      monitor_2 = new DbMonitorWidget();
+      monitor_2.set_layout(DbMonitorWidget.Layout.HORIZONTAL);
+      monitor_2.set_peak(1);
+      monitor_2.set_decay(1.5);
+      monitor_2_bar.pack_start(new Gtk.Label("R"), false, false, 2);
+      monitor_2_bar.pack_start(monitor_2, true, true, 2);
+      monitor_2_bar.pack_start(lbl_level_2, false, false, 2);
+      control_area.pack_start(monitor_2_bar, true, true, 2);
+      
+      Gtk.HBox pregain_bar = new Gtk.HBox(false, 4);
+      Gtk.Label pregain_label = new Gtk.Label("Pre-gain");
+      pregain_bar.pack_start(pregain_label, false, false , 2);
+      pregain_slider = new Gtk.HScale.with_range(0.0, 10.0, 0.1);
+      pregain_slider.set_value_pos(Gtk.PositionType.LEFT);
+      pregain_slider.value_changed.connect((s) => {this.set_pregain(s.get_value());});
+      pregain_bar.pack_start(pregain_slider, true, true , 2);
+      
+      control_area.pack_start(pregain_bar, true, true , 2);
+      
+      Gtk.HBox dynamics_bar = new Gtk.HBox(false, 4);
+      dynamics_btn = new Hildon.CheckButton(Hildon.SizeType.FINGER_HEIGHT | Hildon.SizeType.AUTO_WIDTH);
+      dynamics_btn.set_label("Enable dynamics");
+      dynamics_btn.toggled.connect( (s) => {toggle_dynamics(s.get_active());} );
+      dynamics_bar.pack_start(dynamics_btn, true, false, 2);
+      dynamics_settings_btn = new Hildon.Button(Hildon.SizeType.FINGER_HEIGHT | Hildon.SizeType.AUTO_WIDTH, Hildon.ButtonArrangement.HORIZONTAL);
+      dynamics_settings_btn.set_text("Settings", "");
+      dynamics_settings_btn.set_image(new Gtk.Image.from_icon_name("general_settings", Gtk.IconSize.BUTTON));
+      dynamics_settings_btn.set_image_position(Gtk.PositionType.LEFT);
+      dynamics_settings_btn.clicked.connect(do_dynamics_popup);
+      dynamics_bar.pack_start(dynamics_settings_btn, true, false, 2);
+      
+      control_area.pack_start(dynamics_bar, true, true , 2);
+      
+      Gtk.HBox volume_bar = new Gtk.HBox(false, 4);
+      Gtk.Label volume_label = new Gtk.Label("Volume");
+      volume_bar.pack_start(volume_label, false, false , 2);
+      volume_slider = new Gtk.HScale.with_range(0.0, 10.0, 0.1);
+      volume_slider.set_value_pos(Gtk.PositionType.LEFT);
+      volume_slider.value_changed.connect((s) => {this.set_volume(s.get_value());});
+      volume_bar.pack_start(volume_slider, true, true , 2);
+      
+      control_area.pack_start(volume_bar, true, true , 2);
+      
+      this.update_settings();
+      this.show_all();
+    }
+    
+    public void play() {
+      acm.play();
+    }
+    
+    public void pause() {
+      acm.pause();
+    }
+    
+    public void stop() {
+      acm.stop();
+    }
+    
+    private void toggle_monitor(bool state) {
+      if (null != acm) {
+        acm.set_monitor_mute(!state);
+      }
+    }
+    
+    private void toggle_dynamics(bool state) {
+      this.settings.input.dynamics.active = state;
+      this.dynamics_settings_btn.set_sensitive(state);
+      if (null != acm) {
+        if (!state) {
+          SettingsStructures.DynamicsSettings ds = SettingsStructures.DynamicsSettings();
+          acm.set_dynamics(ds.mode, ds.characteristics, ds.threshold, ds.ratio); // shouldn't be applied
+        }
+        else {
+          acm.set_dynamics(settings.input.dynamics.mode, settings.input.dynamics.characteristics, settings.input.dynamics.threshold, settings.input.dynamics.ratio);
+        }
+      }
+    }
+    
+    private void do_dynamics_popup() {
+      stdout.printf("dynamics popup new called\n");
+      stdout.flush();
+      DynamicsPopUp dlg = new DynamicsPopUp.with_settings("Monitoring dynamics settings", this, settings.input.dynamics);
+      dlg.set_transient_for(this.get_transient_for());
+      dlg.mode_updated.connect(this.dynamics_update_mode_callback);
+      dlg.characteristics_updated.connect(this.dynamics_update_characteristics_callback);
+      dlg.ratio_updated.connect(this.dynamics_update_ratio_callback);
+      dlg.threshold_updated.connect(this.dynamics_update_threshold_callback);
+      stdout.printf("dynamics connections made\n");
+      stdout.flush();
+      dlg.run();
+      stdout.printf("dynamics back from run\n");
+      stdout.flush();
+      dlg.destroy();
+    }
+    private void dynamics_update_mode_callback(DynamicsPopUp sender, string val) {
+      settings.input.dynamics.mode = val;
+      if (null != acm) {
+        acm.set_dynamics_mode(val);
+      }
+    }
+    private void dynamics_update_characteristics_callback(DynamicsPopUp sender, string val) {
+      settings.input.dynamics.characteristics = val;
+      if (null != acm) {
+        acm.set_dynamics_characteristics(val);
+      }
+    }
+    private void dynamics_update_ratio_callback(DynamicsPopUp sender, double val) {
+      settings.input.dynamics.ratio = val;
+      if (null != acm) {
+        acm.set_dynamics_ratio(val);
+      }
+    }
+    private void dynamics_update_threshold_callback(DynamicsPopUp sender, double val) {
+      settings.input.dynamics.threshold = val;
+      if (null != acm) {
+        acm.set_dynamics_threshold(val);
+      }
+    }
+    
+    private void update_level_callback(AudioCaptureMonitor sender, int channels, int channel, double rms_dB, double peak_dB, double decay_dB, double rms) {
+      //string text = level_format.printf(channel.to_string(), rms_dB, peak_dB, decay_dB, rms, Math.pow (10, decay_dB / 20));
+      string text = level_format.printf(peak_dB, decay_dB);
+      if (0 == channel) {
+        lbl_level_1.set_text(text);
+        monitor_1.set_peak(Math.pow (10, peak_dB / 20));
+        monitor_1.set_decay(Math.pow (10, decay_dB / 20));
+      }
+      else if (1 == channel) {
+        lbl_level_2.set_text(text);
+        monitor_2.set_peak(Math.pow (10, peak_dB / 20));
+        monitor_2.set_decay(Math.pow (10, decay_dB / 20));
+      }
+      else {
+        stdout.printf("channel %s found\n", channel.to_string());
+        stdout.flush();
+      }
+    }
+    
+  }
+  
+}
\ No newline at end of file
diff --git a/src/AppSettings.vala b/src/AppSettings.vala
new file mode 100644 (file)
index 0000000..415f891
--- /dev/null
@@ -0,0 +1,19 @@
+namespace IdWorks {
+
+protected struct AppSettings {
+
+  public string working_folder; 
+  
+  public string serialize_to_string() {
+    string ret = "";
+    /// TODO
+    return ret;
+  }
+  
+  public void deserialize_from_string(string data) {
+    /// TODO
+  }
+  
+}
+
+}
diff --git a/src/ApplicationSettings.vala b/src/ApplicationSettings.vala
new file mode 100644 (file)
index 0000000..e352213
--- /dev/null
@@ -0,0 +1,652 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+namespace IdWorks {
+
+public class ApplicationSettings {
+
+  public enum OpenAction {
+    LAST_PROJECT,
+    NEW_PROJECT,
+    RECENT_PROJECTS,
+    NAMED_PROJECT;
+    
+    public static OpenAction from_label(string val) {
+      switch (val) {
+        case "Last open project":
+          return LAST_PROJECT;
+        case "New project":
+          return NEW_PROJECT;
+        case "Recent projects list":
+          return RECENT_PROJECTS;
+        case "Specified project":
+          return NAMED_PROJECT;
+        default:
+          return LAST_PROJECT;
+      }
+    }
+    public static OpenAction from_name(string val) {
+      switch (val) {
+        case "LAST_PROJECT":
+          return LAST_PROJECT;
+        case "NEW_PROJECT":
+          return NEW_PROJECT;
+        case "RECENT_PROJECTS":
+          return RECENT_PROJECTS;
+        case "NAMED_PROJECT":
+          return NAMED_PROJECT;
+        default:
+          return LAST_PROJECT;
+      }
+    }
+    public static string to_label(OpenAction val) {
+      switch (val) {
+        case LAST_PROJECT:
+          return "Last open project";
+        case NEW_PROJECT:
+          return "New project";
+        case RECENT_PROJECTS:
+          return "Recent projects list";
+        case NAMED_PROJECT:
+          return "Specified project";
+        default:
+          return "Last open project";
+      }
+    }
+    public static string to_name(OpenAction val) {
+      switch(val) {
+        case LAST_PROJECT:
+          return "LAST_PROJECT";
+        case NEW_PROJECT:
+          return "NEW_PROJECT";
+        case RECENT_PROJECTS:
+          return "RECENT_PROJECTS";
+        case NAMED_PROJECT:
+          return "NAMED_PROJECT";
+        default:
+          return "LAST_PROJECT";
+      }
+    }
+  }
+  public enum CloseAction {
+    AUTO_SAVE_PROJECT,
+    PROMPT_SAVE_PROJECT,
+    DO_NOTHING;
+    
+    public static CloseAction from_label(string val) {
+      switch (val) {
+        case "Auto-save the project":
+          return AUTO_SAVE_PROJECT; 
+        case "Prompt to save the project":
+          return PROMPT_SAVE_PROJECT;
+        case "Do nothing":
+          return DO_NOTHING;
+        default:
+          return AUTO_SAVE_PROJECT;
+      }
+    }
+    public static CloseAction from_name(string val) {
+      switch (val) {
+        case "AUTO_SAVE_PROJECT":
+          return AUTO_SAVE_PROJECT;
+        case "PROMPT_SAVE_PROJECT":
+          return PROMPT_SAVE_PROJECT;
+        case "DO_NOTHING":
+          return DO_NOTHING;
+        default:
+          return PROMPT_SAVE_PROJECT;
+      }
+    }
+    public static string to_label(CloseAction val) {
+      switch (val) {
+        case AUTO_SAVE_PROJECT:
+          return "Auto-save the project";
+        case PROMPT_SAVE_PROJECT:
+          return "Prompt to save the project";
+        case DO_NOTHING:
+          return "Do nothing";
+        default:
+          return "Auto-save the project";
+      }
+    } 
+    public static string to_name(CloseAction val) {
+      switch(val) {
+        case AUTO_SAVE_PROJECT:
+          return "AUTO_SAVE_PROJECT";
+        case PROMPT_SAVE_PROJECT:
+          return "PROMPT_SAVE_PROJECT";
+        case DO_NOTHING:
+          return "DO_NOTHING";
+        default:
+          return "PROMPT_SAVE_PROJECT";
+      }
+    }
+  }
+  public enum AddTrackAction {
+    SET_ACTIVE,
+    SET_SOLO,
+    SET_INACTIVE,
+    SET_MUTE;
+    
+    public static AddTrackAction from_label(string val) {
+      switch (val) {
+        case "Enable track":
+          return SET_ACTIVE;
+        case "Solo track":
+          return SET_SOLO;
+        case "Disable track":
+          return SET_INACTIVE;
+        case "Mute track":
+          return SET_MUTE;
+        default:
+          return SET_ACTIVE;
+      }
+    }
+    public static AddTrackAction from_name(string val) {
+      switch (val) {
+        case "SET_ACTIVE":
+          return SET_ACTIVE;
+        case "SET_SOLO":
+          return SET_SOLO;
+        case "SET_INACTIVE":
+          return SET_INACTIVE;
+        case "SET_MUTE":
+          return SET_MUTE;
+        default:
+          return SET_ACTIVE;
+      }
+    }
+    public static string to_label(AddTrackAction val) {
+      switch (val) {
+        case SET_ACTIVE:
+          return "Enable track";
+        case SET_SOLO:
+          return "Solo track";
+        case SET_INACTIVE:
+          return "Disable track";
+        case SET_MUTE:
+          return "Mute track";
+        default:
+          return "Enable track";
+      }
+    }
+    public static string to_name(AddTrackAction val) {
+      switch(val) {
+        case SET_ACTIVE:
+          return "SET_ACTIVE";
+        case SET_SOLO:
+          return "SET_SOLO";
+        case SET_INACTIVE:
+          return "SET_INACTIVE";
+        case SET_MUTE:
+          return "SET_MUTE";
+        default:
+          return "SET_ACTIVE";
+      }
+    }
+  }
+  public enum BounceTracksAction {
+    SET_SOURCE_INACTIVE,
+    SET_SOURCE_MUTE,
+    SET_TARGET_SOLO,
+    SET_TARGET_INACTIVE,
+    SET_TARGET_MUTE;
+    
+    public static BounceTracksAction from_label(string val) {
+      switch (val) {
+        case "Disable source tracks":
+          return SET_SOURCE_INACTIVE;
+        case "Mute source tracks":
+          return SET_SOURCE_MUTE;
+        case "Solo new track":
+          return SET_TARGET_SOLO;
+        case "Disable new track":
+          return SET_TARGET_INACTIVE;
+        case "Mute new track":
+          return SET_TARGET_MUTE;
+        default:
+          return SET_SOURCE_INACTIVE;
+      }
+    }
+    public static BounceTracksAction from_name(string val) {
+      switch (val) {
+        case "SET_SOURCE_INACTIVE":
+          return SET_SOURCE_INACTIVE;
+        case "SET_SOURCE_MUTE":
+          return SET_SOURCE_MUTE;
+        case "SET_TARGET_SOLO":
+          return SET_TARGET_SOLO;
+        case "SET_TARGET_INACTIVE":
+          return SET_TARGET_INACTIVE;
+        case "SET_TARGET_MUTE":
+          return SET_TARGET_MUTE;
+        default:
+          return SET_SOURCE_INACTIVE;
+      }
+    }
+    public static string to_label(BounceTracksAction val) {
+      switch (val) {
+        case SET_SOURCE_INACTIVE:
+          return "Disable source tracks";
+        case SET_SOURCE_MUTE:
+          return "Mute source tracks";
+        case SET_TARGET_SOLO:
+          return "Solo new track";
+        case SET_TARGET_INACTIVE:
+          return "Disable new track";
+        case SET_TARGET_MUTE:
+          return "Mute new track";
+        default:
+          return "Disable source tracks";
+      }
+    }
+    public static string to_name(BounceTracksAction val) {
+      switch (val) {
+        case SET_SOURCE_INACTIVE:
+          return "SET_SOURCE_INACTIVE";
+        case SET_SOURCE_MUTE:
+          return "SET_SOURCE_MUTE";
+        case SET_TARGET_SOLO:
+          return "SET_TARGET_SOLO";
+        case SET_TARGET_INACTIVE:
+          return "SET_TARGET_INACTIVE";
+        case SET_TARGET_MUTE:
+          return "SET_TARGET_MUTE";
+        default:
+          return "SET_SOURCE_INACTIVE";
+      }
+    }
+  }
+  public enum RemoveTrackAction {
+    PROMPT,
+    DELETE_FILE,
+    PRESERVE_FILE;
+    
+    public static RemoveTrackAction from_label(string val) {
+      switch (val) {
+        case "Prompt with choices":
+          return PROMPT;
+        case "Delete the file":
+          return DELETE_FILE;
+        case "Preserve the file":
+          return PRESERVE_FILE;
+        default:
+          return PROMPT;
+      }
+    }
+    public static RemoveTrackAction from_name(string val) {
+      switch (val) {
+        case "PROMPT":
+          return PROMPT;
+        case "DELETE_FILE":
+          return DELETE_FILE;
+        case "PRESERVE_FILE":
+          return PRESERVE_FILE;
+        default:
+          return PROMPT;
+      }
+    }
+    public static string to_label(RemoveTrackAction val) {
+      switch (val) {
+        case PROMPT:
+          return "Prompt with choices";
+        case DELETE_FILE:
+          return "Delete the file";
+        case PRESERVE_FILE:
+          return "Preserve the file";
+        default:
+          return "Prompt with choices";
+      }
+    }
+    public static string to_name(RemoveTrackAction val) {
+      switch(val) {
+        case PROMPT:
+          return "PROMPT";
+        case DELETE_FILE:
+          return "DELETE_FILE";
+        case PRESERVE_FILE:
+          return "PRESERVE_FILE";
+        default:
+          return "PROMPT";
+      }
+    }
+  }
+  public enum MixdownAction {
+    NONE,
+    PLAY_TARGET;
+    
+    public static MixdownAction from_label(string val) {
+      switch (val) {
+        case "Do nothing":
+          return NONE;
+        case "Play mix":
+          return PLAY_TARGET;
+        default:
+          return NONE;
+      }
+    }
+    public static MixdownAction from_name(string val) {
+      switch (val) {
+        case "NONE":
+          return NONE;
+        case "PLAY_TARGET":
+          return PLAY_TARGET;
+        default:
+          return NONE;
+      }
+    }
+    public static string to_label(MixdownAction val) {
+      switch (val) {
+        case NONE:
+          return "Do nothing";
+        case PLAY_TARGET:
+          return "Play mix";
+        default:
+          return "Do nothing";
+      }
+    }
+    public static string to_name(MixdownAction val) {
+      switch(val) {
+        case NONE:
+          return "NONE";
+        case PLAY_TARGET:
+          return "PLAY_TARGET";
+        default:
+          return "NONE";
+      }
+    }
+  }
+  
+  public string working_directory {public get; public set;}
+  public string last_project_name {public get; public set;}
+  public string last_project_location {public get; public set;}
+  public string named_project_name {public get; public set;}
+  public string named_project_location {public get; public set;}
+  public string[] recent_project_names {public get; public set;}
+  public string[] recent_project_locations {public get; public set;}
+  public uint64 delay_max_delay {public get; public set;}
+  public bool mixdown_enable_lamenc {public get; public set;}
+  public bool mixdown_enable_mpegaudio {public get; public set;}
+  public OpenAction open_action {public get; public set;}
+  public CloseAction close_action {public get; public set;}
+  public AddTrackAction add_track_action {public get; public set;}
+  public BounceTracksAction bounce_tracks_action {public get; public set;}
+  public RemoveTrackAction remove_track_action {public get; public set;}
+  public MixdownAction mixdown_action {public get; public set;}
+  
+  public ApplicationSettings() {
+    working_directory = Environment.get_home_dir() + "/DemoRecordings";
+    last_project_name = "";
+    last_project_location = "";
+    named_project_name = "";
+    named_project_location = "";
+    recent_project_names = new string[]{"","","","","","","","","",""};
+    recent_project_locations = new string[]{"","","","","","","","","",""};
+    delay_max_delay = Time.Nanoseconds.SECOND / 2;
+    mixdown_enable_lamenc = false;
+    mixdown_enable_mpegaudio = false;
+    open_action = OpenAction.LAST_PROJECT;
+    close_action = CloseAction.PROMPT_SAVE_PROJECT;
+    add_track_action = AddTrackAction.SET_ACTIVE;
+    bounce_tracks_action = BounceTracksAction.SET_SOURCE_INACTIVE;
+    remove_track_action = RemoveTrackAction.PROMPT;
+  }
+  
+  public static ApplicationSettings load_settings(string filename, string version, out string errors) {
+    errors = "";
+    ApplicationSettings settings = new ApplicationSettings();
+    bool success = false;
+    // prepare the Xml Parser
+    Xml.Parser.init();
+    // load the document
+    Xml.Doc* doc = Xml.Parser.parse_file(filename);
+    if (null != doc) {
+      // load the root node
+      Xml.Node* root = doc->get_root_element();
+      if (null != root) {
+        // check element is ProjectSettings
+        if ("ApplicationSettings" == root->name) {
+          // check version
+          string? doc_version = XmlHelpers.get_node_attribute(root, "version");
+          if (null != doc_version && version == doc_version) {
+            // looking good let's get parsing.
+            try {
+              settings.from_xml_node(root);
+              success = true;
+            }
+            catch (GLib.Error ex) {
+              errors = "ApplicationSettings parser threw an error.";
+            }
+            finally {
+              // no op
+            }
+          }
+          else {
+            errors = "Incorrect version or unversioned document.";
+          }
+        }
+        else {
+          errors = "Not a valid settings file.";
+        }
+      }
+      else {
+        errors = "No root node found.";
+      }
+      // clean up
+      delete doc;
+    }
+    else {
+      errors = "Couldn't parse file as valid XML.";
+    }
+    // clean up the parser
+    Xml.Parser.cleanup();
+    return (success) ? settings : null;
+  }
+  
+  public static bool save_settings(string filename, ApplicationSettings settings, ref string errors) {
+    //stdout.printf("save settings called\n");
+    //stdout.flush();
+    string contents = settings.to_xml_string();
+    errors = "";
+    if (0 < contents.length) {
+      contents = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + contents;
+      //stdout.printf(contents);
+      //stdout.printf("\n" + filename + "\n");
+      //stdout.flush();
+      try {
+        if (FileUtils.set_contents(filename, contents, contents.length)) {
+          //stdout.printf("save settings worked\n");
+          //stdout.flush();
+          return true;
+        }
+        else {
+          errors = "Couldn't save the settings file!\n%s".printf("FILE ERROR");
+        }
+      }
+      catch (FileError ex) {
+        errors = "Couldn't save the settings file!\n%s".printf("FILE ERROR");
+      }
+    }
+    else {
+      errors = "Couldn't serialize the settings!.";
+    }
+    //stdout.printf("save settings failed\n");
+    //stdout.flush();
+    return false;
+  }
+  
+  private string to_xml_string() {
+    return ("<ApplicationSettings version=\"1.0\">\n" +
+            "<working_directory type=\"string\"><![CDATA[%s]]></working_directory>\n" +
+            "<last_project_name type=\"string\"><![CDATA[%s]]></last_project_name>\n" +
+            "<last_project_location type=\"string\"><![CDATA[%s]]></last_project_location>\n" +
+            "<named_project_name type=\"string\"><![CDATA[%s]]></named_project_name>\n" +
+            "<named_project_location type=\"string\"><![CDATA[%s]]></named_project_location>\n" +
+            "<recent_project_names type=\"CDL\"><![CDATA[%s]]></recent_project_names>\n" +
+            "<recent_project_locations type=\"CDL\"><![CDATA[%s]]></recent_project_locations>\n" +
+            "<delay_max_delay type=\"uint64\">%llu</delay_max_delay>\n" +
+            "<mixdown_enable_lamenc type=\"bool\">%s</mixdown_enable_lamenc>\n" +
+            "<mixdown_enable_mpegaudio type=\"bool\">%s</mixdown_enable_mpegaudio>\n" +
+            "<open_action type=\"OpenAction\">%s</open_action>\n" +
+            "<close_action type=\"CloseAction\">%s</close_action>\n" +
+            "<add_track_action type=\"AddTrackAction\">%s</add_track_action>\n" +
+            "<bounce_tracks_action type=\"BounceTracksAction\">%s</bounce_tracks_action>\n" +
+            "<remove_track_action type=\"RemoveTrackAction\">%s</remove_track_action>\n" +
+            "</ApplicationSettings>\n").printf(
+            working_directory,
+            last_project_name,
+            last_project_location,
+            "",
+            "",
+            "",
+            "",
+            delay_max_delay,
+            mixdown_enable_lamenc.to_string(),
+            mixdown_enable_mpegaudio.to_string(),
+            OpenAction.to_name(open_action),
+            CloseAction.to_name(close_action),
+            AddTrackAction.to_name(add_track_action),
+            BounceTracksAction.to_name(bounce_tracks_action),
+            RemoveTrackAction.to_name(remove_track_action)
+            );
+  }
+  
+  private void from_xml_node(Xml.Node* node) {
+    // Loop over the passed node's children
+    for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {
+      // Spaces between tags are also nodes, discard them
+      if (iter->type != Xml.ElementType.ELEMENT_NODE) {
+        continue;
+      }
+      // Get the node's name
+      string node_name = iter->name;
+      // Get the nodes type
+      string? type = XmlHelpers.get_node_attribute(iter, "type");
+      // Get the node's content with <tags> stripped
+      if (null != type) {
+        // check the type
+        switch(type) {
+          case "string":
+            switch(node_name) {
+              case "working_directory":
+                this.working_directory = iter->get_content();
+                break;
+              case "last_project_name":
+                this.last_project_name = iter->get_content();
+                break;
+              case "last_project_location":
+                this.last_project_location = iter->get_content();
+                break;
+              case "named_project_name":
+                this.named_project_name = iter->get_content();
+                break;
+              case "named_project_location":
+                this.named_project_location = iter->get_content();
+                break;
+              default:
+                break;
+            }
+            break;
+          case "bool":
+            switch(node_name) {
+              case "mixdown_enable_lamenc":
+                this.mixdown_enable_lamenc = iter->get_content().to_bool();
+                break;
+              case "mixdown_enable_mpegaudio":
+                this.mixdown_enable_mpegaudio = iter->get_content().to_bool();
+                break;
+              default:
+                break;
+            }
+            break;
+          case "uint64":
+            switch(node_name) {
+              case "delay_max_delay":
+                this.delay_max_delay = iter->get_content().to_uint64();
+                break;
+              default:
+                break;
+            }
+            break;
+          case "CDL":
+            switch(node_name) {
+              case "recent_project_names":
+                /// TODO this.recent_project_names = iter->get_content().to_uint64();
+                break;
+              case "recent_project_locations":
+                /// TODO this.recent_project_locations = iter->get_content().to_uint64();
+                break;
+              default:
+                break;
+            }
+            break;
+          case "OpenAction":
+            switch(node_name) {
+              case "open_action":
+                this.open_action = OpenAction.from_name(iter->get_content());
+                break;
+              default:
+                break;
+            }
+            break;
+          case "CloseAction":
+            switch(node_name) {
+              case "close_action":
+                this.close_action = CloseAction.from_name(iter->get_content());
+                break;
+              default:
+                break;
+            }
+            break;
+          case "AddTrackAction":
+            switch(node_name) {
+              case "add_track_action":
+                this.add_track_action = AddTrackAction.from_name(iter->get_content());
+                break;
+              default:
+                break;
+            }
+            break;
+          case "BounceTracksAction":
+            switch(node_name) {
+              case "bounce_tracks_action":
+                this.bounce_tracks_action = BounceTracksAction.from_name(iter->get_content());
+                break;
+              default:
+                break;
+            }
+            break;
+          case "RemoveTrackAction":
+            switch(node_name) {
+              case "remove_track_action":
+                this.remove_track_action = RemoveTrackAction.from_name(iter->get_content());
+                break;
+              default:
+                break;
+            }
+            break;
+          default:
+            continue;
+        }
+      }
+      
+    }
+  }
+
+}
+
+}
diff --git a/src/AudioCaptureMonitor.vala b/src/AudioCaptureMonitor.vala
new file mode 100644 (file)
index 0000000..bead983
--- /dev/null
@@ -0,0 +1,238 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+namespace IdWorks {
+
+public class AudioCaptureMonitor : Gst.Pipeline {
+  //Gst.Pipeline pipeline;
+  //Gst.Element src;
+  //Gst.Element converter;
+  //Gst.Element pregain;
+  //Gst.Element dynamics;
+  //Gst.Element volume;
+  InputBin input;
+  Gst.Element level;
+  Gst.Element monitor;
+  Gst.Element sink_element;
+  uint watch_id;
+  
+  public AudioCaptureMonitor(string name) {
+    this.name = name;
+    construct_pipeline(SettingsStructures.MonitorSettings());
+  }
+  public AudioCaptureMonitor.with_settings(string name, SettingsStructures.MonitorSettings settings) {
+    this.name = name;
+    construct_pipeline(settings);
+  }
+  
+  private void construct_pipeline(SettingsStructures.MonitorSettings settings) {
+    /*
+    this.src = Gst.ElementFactory.make("pulsesrc", "capture");
+    this.src.set_property("device", "source.hw0");
+    this.add(this.src);
+    
+    this.converter = Gst.ElementFactory.make("audioconvert", "converter");
+    this.add(this.converter);
+    
+    this.pregain = Gst.ElementFactory.make("volume", "pregain");
+    this.pregain.set_property("volume", 1);
+    this.add(this.pregain);
+    
+    this.dynamics = Gst.ElementFactory.make("audiodynamic", "dynamics");
+    this.add(this.dynamics);
+    this.dynamics.set_property("mode", "compressor"); // compressor or expander
+    this.dynamics.set_property("characteristics", "hard-knee"); // hard-knee or soft-knee
+    this.dynamics.set_property("threshold", 1.0); // [0, 1]
+    this.dynamics.set_property("ratio", 1.0); // >= 0
+    
+    this.volume = Gst.ElementFactory.make("volume", "volume");
+    this.volume.set_property("volume", 1);
+    this.add(this.volume);
+    */
+    
+    this.input = new InputBin.with_settings("input", settings.input);
+    this.add(this.input);
+    
+    this.level = Gst.ElementFactory.make("level", "level");
+    this.level.set_property("interval", 50 * Time.Nanoseconds.MILLISECOND);
+    this.level.set_property("peak-ttl", 2 * Time.Nanoseconds.SECOND);
+    this.add(this.level);
+    
+    this.monitor = Gst.ElementFactory.make("volume", "monitor");
+    this.monitor.set_property("volume", 1);
+    this.monitor.set_property("mute", true);
+    this.add(this.monitor);
+    
+    this.sink_element = Gst.ElementFactory.make("autoaudiosink", "fakesink");
+    this.add(this.sink_element);
+    
+    this.level.set_property("message", true);
+    
+    this.sink_element.set_property("sync", true);
+    
+    
+    //Gst.Caps caps = Gst.Caps.from_string ("audio/x-raw-int,channels=2");
+    //this.converter.link_filtered(level, caps);
+    /*
+    this.src.link(converter);
+    this.converter.link(pregain);
+    this.pregain.link(dynamics);
+    this.dynamics.link(volume);
+    this.volume.link(level);
+    */
+    this.input.link(level);
+    this.level.link(monitor);
+    this.monitor.link(sink_element);
+    
+    Gst.Bus bus = this.get_bus();
+    
+    watch_id = bus.add_watch(message_handler);
+    
+  }
+  
+  public void set_dynamics(string mode, string characteristics, double threshold, double ratio) {
+    this.input.set_dynamics(mode, characteristics, threshold, ratio);
+    /*
+    this.dynamics.set_property("mode", mode); // compressor or expander
+    this.dynamics.set_property("characteristics", characteristics); // hard-knee or soft-knee
+    this.dynamics.set_property("threshold", threshold); // [0, 1]
+    this.dynamics.set_property("ratio", ratio); // >= 0
+    */
+  }
+  public void set_dynamics_mode(string mode) {
+    this.input.set_dynamics_mode(mode);
+    //this.dynamics.set_property("mode", mode); // compressor or expander
+  }
+  public void set_dynamics_characteristics(string characteristics) {
+    this.input.set_dynamics_characteristics(characteristics);
+    //this.dynamics.set_property("characteristics", characteristics); // hard-knee or soft-knee
+  }
+  public void set_dynamics_threshold(double threshold) {
+    this.input.set_dynamics_threshold(threshold);
+    //this.dynamics.set_property("threshold", threshold); // [0, 1]
+  }
+  public void set_dynamics_ratio(double ratio) {
+    this.input.set_dynamics_ratio(ratio);
+    //this.dynamics.set_property("ratio", ratio); // >= 0
+  }
+  
+  public void set_source(string name) {
+    this.input.set_source(name);
+  }
+  public string get_source() {
+    return this.input.get_source();
+  }
+  
+  public void set_pregain(double val)
+  requires (val >= 0.0 && val <= 10.0) {
+    this.input.set_pregain(val);
+    //this.pregain.set_property("volume", val);
+  }
+  public double get_pregain()
+  ensures (result >= 0.0 && result <= 10.0) {
+    return this.input.get_pregain();
+    /*GLib.Value val = 0;
+    this.pregain.get_property("volume", ref val);
+    return val.get_double();*/
+  }
+  
+  public void set_volume(double val)
+  requires (val >= 0.0 && val <= 10.0) {
+    this.input.set_volume(val);
+    //this.volume.set_property("volume", val);
+  }
+  public double get_volume()
+  ensures (result >= 0.0 && result <= 10.0) {
+    return this.input.get_volume();
+    /*GLib.Value val = 0;
+    this.volume.get_property("volume", ref val);
+    return val.get_double();*/
+  }
+  
+  public void set_mute(bool val) {
+    this.input.set_mute(val);
+    //this.volume.set_property("mute", val);
+  }
+  public bool get_mute() {
+    return this.input.get_mute();
+    /*GLib.Value val = 0;
+    this.volume.get_property("mute", ref val);
+    return val.get_boolean();*/
+  }
+  
+  public void set_monitor_mute(bool val) {
+    this.monitor.set_property("mute", val);
+  }
+  public bool get_monitor_mute() {
+    GLib.Value val = 0;
+    this.monitor.get_property("mute", ref val);
+    return val.get_boolean();
+  }
+  
+  public void play() {
+    this.set_state(Gst.State.PLAYING);
+  }
+  
+  public void pause() {
+    this.set_state(Gst.State.READY);
+  }
+  
+  public void stop() {
+    this.set_state(Gst.State.READY);
+  }
+  
+  public signal void update_level(int channels, int channel, double rms_dB, double peak_dB, double decay_dB, double rms);
+  
+  public bool message_handler(Gst.Bus bus, Gst.Message message) {
+    if (Gst.MessageType.ELEMENT == message.type) {
+      Gst.Structure s = message.get_structure();
+      string name = s.get_name();
+      
+      if ("level" == name) {
+        int channels;
+        double rms_dB, peak_dB, decay_dB;
+        double rms;
+        Gst.Value list;
+        Gst.Value value;
+        int i;
+              
+        list = s.get_value("rms"); // any value gives us the channelsby list size
+        channels = 0;
+        channels = (int) list.list_get_size();
+        
+        for (i = 0; i < channels; ++i) {          
+          list = s.get_value ("rms");
+          value = list.list_get_value (i);
+          rms_dB = value.get_double ();
+          list = s.get_value ("peak");
+          value = list.list_get_value (i);
+          peak_dB = value.get_double ();
+          list = s.get_value ("decay");
+          value = list.list_get_value (i);
+          decay_dB = value.get_double ();
+          /* converting from dB to normal gives us a value between 0.0 and 1.0 */
+          rms = Math.pow (10, rms_dB / 20);
+          update_level(channels, i, rms_dB, peak_dB, decay_dB, rms);
+        }
+      }
+    }
+    return true;
+  }
+
+}
+
+}
\ No newline at end of file
diff --git a/src/CdlParser.vala b/src/CdlParser.vala
new file mode 100644 (file)
index 0000000..dd394e6
--- /dev/null
@@ -0,0 +1,70 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+namespace IdWorks {
+  
+  public static class CdlParser : Object {
+    
+    const unichar CHAR_DLM = ',';
+    const unichar CHAR_ESC = '\"';
+    const unichar CHAR_EOL = '\n';
+    
+    public static string[] ParseLine(string line) {
+      string[] values = {};
+      
+      bool in_esc = false;
+      
+      int pos = 0;
+      StringBuilder buffer = new StringBuilder();
+      unichar c;
+      
+      while ((c = line[pos]) != CHAR_EOL && !in_esc && pos < line.length) {
+        switch(c) {
+          case CHAR_ESC:
+            // if we are already escaped add to buffer
+            if (in_esc) {
+              buffer.append_unichar(c);
+              in_esc = false;
+            }
+            // else enter escape mode
+            else {
+              in_esc = true;
+            }
+            break;
+          case CHAR_DLM:
+            // add the buffer to list and clear the buffer
+            values += buffer.str;
+            buffer.truncate(0);
+            break;
+          default:
+            // normal char to add to buffer
+            buffer.append_unichar(c);
+            break;
+        }
+        ++pos;
+      }
+      // check for data left floating over
+      if (0 < buffer.len) values += buffer.str;
+      buffer.truncate(0);
+      buffer = null;
+      
+      return values;
+    }
+    
+  }
+  
+}
diff --git a/src/Constants.vala b/src/Constants.vala
new file mode 100644 (file)
index 0000000..703b27f
--- /dev/null
@@ -0,0 +1,62 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+namespace IdWorks {
+
+  namespace Time {
+
+    public const string CLOCK_FORMAT = "%02llu:%02llu.%01llu";
+    public static string time_to_string(int64 t) {
+      uint64 ut = (uint64)t / Nanoseconds.MILLISECOND;
+      return CLOCK_FORMAT.printf((ut / Milliseconds.MINUTE), (ut / Milliseconds.SECOND) % Seconds.MINUTE, (ut % Milliseconds.SECOND) / 100);
+    }
+    public static int64 get_current_time() {
+      TimeVal tv = TimeVal();
+      tv.get_current_time();
+      return (((int64)tv.tv_sec * 1000000) + (int64)tv.tv_usec) * 1000;
+    }
+
+    namespace Nanoseconds {
+      public const uint64 NANOSECOND = 1;
+      public const uint64 MILLISECOND = 1000000 * NANOSECOND;
+      public const uint64 SECOND = 1000 * MILLISECOND;
+      public const uint64 MINUTE = 60 * SECOND;
+      public const uint64 HOUR = 60 * MINUTE;
+      public const uint64 DAY = 24 * HOUR;
+      public const uint64 WEEK = 7 * DAY;
+    }
+
+    namespace Milliseconds {
+      public const uint64 MILLISECOND = 1;
+      public const uint64 SECOND = 1000 * MILLISECOND;
+      public const uint64 MINUTE = 60 * SECOND;
+      public const uint64 HOUR = 60 * MINUTE;
+      public const uint64 DAY = 24 * HOUR;
+      public const uint64 WEEK = 7 * DAY;
+    }
+
+    namespace Seconds {
+      public const uint64 SECOND = 1;
+      public const uint64 MINUTE = 60 * SECOND;
+      public const uint64 HOUR = 60 * MINUTE;
+      public const uint64 DAY = 24 * HOUR;
+      public const uint64 WEEK = 7 * DAY;
+    }
+
+  }
+
+}
\ No newline at end of file
diff --git a/src/DbMonitorWidget.vala b/src/DbMonitorWidget.vala
new file mode 100644 (file)
index 0000000..0b97bfa
--- /dev/null
@@ -0,0 +1,218 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+namespace IdWorks {
+
+public class DbMonitorWidget : Gtk.Widget {
+  
+  public static enum Layout {
+    VERTICAL = 0,
+    HORIZONTAL
+  }
+  
+  private static const int BORDER_WIDTH = 4;
+  private static const int BORDER_HEIGHT = 4;
+  //private Pango.Layout layout;
+  private static const double MIN_VALUE = 0.0;
+  private static const double MAX_VALUE = 1.5;
+  
+  private static const double ZERO_VALUE = 1.0;
+  
+  private double peak;
+  private double decay;
+  public void set_peak(double val) {
+    this.peak = (MAX_VALUE > val) ? val : MAX_VALUE;
+    this.redraw_canvas();
+  }
+  public void set_decay(double val) {
+    this.decay = (MAX_VALUE > val) ? val : MAX_VALUE;
+    this.redraw_canvas();
+  }
+  private Layout layout = Layout.VERTICAL;
+  public void set_layout(Layout layout) {
+    this.layout = layout;
+  }
+  
+  construct {
+    // nothing TODO
+  }
+  
+  /*
+  * This method Gtk+ is calling on a widget to ask
+  * the widget how large it wishes to be. It's not guaranteed
+  * that Gtk+ will actually give this size to the widget.
+  */
+  public override void size_request (out Gtk.Requisition requisition) {
+    requisition.width = (Layout.VERTICAL == this.layout) ? 40 : 250;
+    requisition.height = (Layout.VERTICAL == this.layout) ? 250 : 40;
+  }
+  
+  /*
+  * This method gets called by Gtk+ when the actual size is known
+  * and the widget is told how much space could actually be allocated.
+  * It is called every time the widget size changes, for example when the
+  * user resizes the window.
+  */
+  public override void size_allocate (Gdk.Rectangle allocation) {
+    // The base method will save the allocation and move/resize the
+    // widget's GDK window if the widget is already realized.
+    base.size_allocate (allocation);
+    
+    // Move/resize other realized windows if necessary
+  }
+  
+  private void redraw_canvas () {
+    if (null == this.window) {
+      return;
+    }    
+    unowned Gdk.Region region = this.window.get_clip_region ();
+    // redraw the cairo canvas completely by exposing it
+    this.window.invalidate_region (region, true);
+    this.window.process_updates (true);
+  }
+  
+  private void draw() {
+    // Cairo context to draw on
+    Cairo.Context cr = Gdk.cairo_create (this.window);
+    // In this example, draw a rectangle in the foreground color
+    Gdk.cairo_set_source_color (cr, this.style.fg[this.state]);
+    cr.rectangle (BORDER_WIDTH, BORDER_HEIGHT,
+                  this.allocation.width - 2 * BORDER_WIDTH,
+                  this.allocation.height - 2 * BORDER_HEIGHT);
+                  cr.set_line_width (1.0);
+                  //cr.set_line_join (LineJoin.ROUND);
+                  cr.stroke ();
+                  
+    // draw the bar for the peak level
+    if (0.0 < this.peak) {
+      if (Layout.VERTICAL == this.layout) {
+        cr.rectangle (BORDER_WIDTH
+        , (this.allocation.height - 2 * BORDER_HEIGHT) - ( (this.allocation.height - 2 * BORDER_HEIGHT) / (MAX_VALUE - MIN_VALUE) ) * this.peak
+        , this.allocation.width - 2 * BORDER_WIDTH
+        , (((this.allocation.height - 2 * BORDER_HEIGHT) / (MAX_VALUE - MIN_VALUE)) * this.peak) + BORDER_HEIGHT
+        );
+      }
+      else {
+        cr.rectangle (BORDER_WIDTH
+        , BORDER_HEIGHT
+        , (((this.allocation.width - 2 * BORDER_WIDTH) / (MAX_VALUE - MIN_VALUE)) * this.peak) + BORDER_WIDTH
+        , this.allocation.height - 2 * BORDER_HEIGHT
+        );
+      }
+      cr.set_line_width (2.0);
+      cr.fill();
+    }
+    
+    // draw a line for the decay level
+    if (0.0 < this.decay) {
+      if (Layout.VERTICAL == this.layout) {
+        cr.rectangle (BORDER_WIDTH
+        , (this.allocation.height - 2 * BORDER_HEIGHT) - ( (this.allocation.height - 2 * BORDER_HEIGHT) / (MAX_VALUE - MIN_VALUE) ) * this.decay
+        , this.allocation.width - 2 * BORDER_WIDTH
+        , BORDER_HEIGHT
+        );
+      }
+      else {
+        cr.rectangle ((((this.allocation.width - 2 * BORDER_WIDTH) / (MAX_VALUE - MIN_VALUE)) * this.decay) + BORDER_WIDTH
+        , BORDER_HEIGHT
+        , BORDER_WIDTH
+        , this.allocation.height - 2 * BORDER_HEIGHT
+        );
+      }
+      cr.set_line_width (2.0);
+      cr.fill();
+    }
+    
+    // draw 0 db marker
+    if (Layout.VERTICAL == this.layout) {
+      cr.rectangle (0
+      , (this.allocation.height) - ( (this.allocation.height) / (MAX_VALUE - MIN_VALUE) ) * ZERO_VALUE
+      , this.allocation.width
+      , 1
+      );
+    }
+    else {
+      cr.rectangle ((((this.allocation.width) / (MAX_VALUE - MIN_VALUE)) * ZERO_VALUE)
+      , 0
+      , 1
+      , this.allocation.height
+      );
+    }
+    cr.set_line_width (1.0);
+    cr.fill();
+    
+  }
+  
+  /*
+  * This method is responsible for creating GDK (windowing system)
+  * resources. In this example we will create a new GDK window which we
+  * then draw on.
+  */
+  public override void realize () {
+    // Create a new Gdk.Window which we can draw on.
+    // Also say that we want to receive exposure events by setting
+    // the event_mask
+    var attrs = Gdk.WindowAttr () {
+      window_type = Gdk.WindowType.CHILD,
+      wclass = Gdk.WindowClass.INPUT_OUTPUT,
+      event_mask = get_events () | Gdk.EventMask.EXPOSURE_MASK
+    };
+    this.window = new Gdk.Window (get_parent_window (), attrs, 0);
+    this.window.move_resize (this.allocation.x, this.allocation.y,
+                             this.allocation.width, this.allocation.height);
+                             
+    // Associate the GDK window with ourselves, Gtk+ needs a reference
+    // between the widget and the GDK window
+    this.window.set_user_data (this);
+    
+    // Attach the style to the GDK window. A style contains colors and
+    // GC contexts used for drawing
+    this.style = this.style.attach (this.window);
+    
+    // The default color of the background should be what
+    // the style (theme engine) tells us
+    this.style.set_background (this.window, Gtk.StateType.NORMAL);
+    
+    // Set an internal flag telling that we're realized
+    set_flags (Gtk.WidgetFlags.REALIZED);
+  }
+  
+  /*
+  * This method is called when the widget is asked to draw itself.
+  * Remember that this will be called a lot of times, so it's usually
+  * a good idea to write this code as optimized as it can be, don't
+  * create any resources in here.
+  */
+  public override bool expose_event (Gdk.EventExpose event) {
+    this.draw();
+    return true;
+  }
+  
+  /*
+  * This method is responsible for freeing the GDK resources.
+  */
+  public override void unrealize () {
+    // The base method will de-associate the GDK window we created in
+    // method 'realize' with ourselves.
+    base.unrealize ();
+    
+    // De-associate other windows with 'set_user_data (null)' if necessary
+  }
+  
+}
+
+}
diff --git a/src/DemoRecorder.vala b/src/DemoRecorder.vala
new file mode 100644 (file)
index 0000000..d0172da
--- /dev/null
@@ -0,0 +1,934 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+namespace IdWorks {
+
+public class DemoRecorder : Hildon.Program {
+  
+  ApplicationSettings application_settings;
+  Hildon.StackableWindow window;
+  Gtk.VBox tracks;
+  SettingsStructures.ProjectSettings project;
+  bool project_dirty;
+  EncodePipeline encodepipeline;
+  PlayPipeline playpipeline;
+  RecordPipeline recordpipeline;
+  PlayerTransport player;
+  MixerBin mixer;
+  uint track_counter;
+  bool recording;
+  bool playing;
+  bool encoding;
+  uint screen_timer_id;
+  uint playback_timer_id;
+  uint recording_timer_id;
+  uint encoding_timer_id;
+  ulong recording_position_duration_id;
+  Osso.Context osso_context;
+  //int64 track_position;
+  //int64 record_position;
+  //int64 track_duration;
+  
+  public signal void encoding_starting();
+  public signal void encoding_started();
+  public signal void encoding_ending();
+  public signal void encoding_ended();
+  public signal void recording_starting();
+  public signal void recording_started();
+  public signal void recording_ending();
+  public signal void recording_ended();
+  public signal void playback_starting();
+  public signal void playback_started();
+  public signal void playback_ending();
+  public signal void playback_ended();
+  public signal void playback_pausing();
+  public signal void playback_paused();
+  public signal void playback_resuming();
+  public signal void playback_resumed();
+  public signal void playback_position_duration(int64 position, int64 duration);
+  public signal void recording_position_duration(int64 position, int64 duration);
+  public signal void encoding_position_duration(int64 position, int64 duration);
+  
+  public void playpipeline_position_duration_callback(Gst.Pipeline sender, int64 position, int64 duration) {
+    playback_position_duration(position, duration);
+    if (position >= duration) playback_time_elapsed();
+  }
+  public void recordpipeline_position_duration_callback(Gst.Pipeline sender, int64 position, int64 duration) {
+    recording_position_duration(position, duration);
+    if (0 < duration && position >= duration) recording_time_elapsed();
+  }
+  public void encodepipeline_position_duration_callback(Gst.Pipeline sender, int64 position, int64 duration) {
+    encoding_position_duration(position, duration);
+    if (position >= duration) encoding_time_elapsed();
+  }
+  private bool update_time_and_duration_play_pipeline() {
+    playpipeline.get_duration_info();
+    return true;
+  }
+  private bool update_time_and_duration_record_pipeline() {
+    recordpipeline.get_duration_info();
+    return true;
+  }
+  private bool update_time_and_duration_encode_pipeline() {
+    encodepipeline.get_duration_info();
+    return true;
+  }
+  private void recording_time_elapsed() {
+    Source.remove(recording_timer_id);
+    /*
+    this should only ever fire for fixed time 'recording'
+    i.e. bouncing or mixing tracks
+    hence we can use it as a trigger
+    */
+  }
+  private void encoding_time_elapsed() {
+    Source.remove(encoding_timer_id);
+    /*
+    this should only ever fire for fixed time 'recording'
+    i.e. bouncing or mixing tracks
+    hence we can use it as a trigger
+    */
+  }
+  private void playback_time_elapsed() {
+    if (!this.recording) Source.remove(playback_timer_id);
+  }
+  
+  construct {
+    osso_context = new Osso.Context("DemoRecorder", "0.1", false, null);
+    project = SettingsStructures.ProjectSettings();
+    recording = false;
+    playing = false;
+    track_counter = 0;
+    window = new Hildon.StackableWindow();
+    window.destroy.connect(Gtk.main_quit);
+    window.delete_event.connect(on_delete_event);
+    window.set_title("Demo Recorder");
+    construct_menu();
+    Gtk.VBox container = new Gtk.VBox(false, 0);
+    Hildon.PannableArea scrollwin = new Hildon.PannableArea();
+    tracks = new Gtk.VBox(false, 0);
+    player = new PlayerTransport();
+    player.window = this.window;
+    this.playback_position_duration.connect(player.position_duration_callback);
+    player.play_clicked.connect(mixer_play_all);
+    player.pause_toggled.connect(mixer_pause);
+    player.stop_clicked.connect(mixer_stop);
+    player.volume_updated.connect(mixer_volume_updated);
+    player.panorama_updated.connect(mixer_panorama_updated);
+    player.eq_updated.connect(mixer_eq_updated);
+    player.record_toggled.connect(mixer_record);
+    container.pack_start(player, false, false, 0);
+    scrollwin.add_with_viewport(tracks);
+    container.pack_end(scrollwin, true, true, 0);
+    window.add(container);
+    mixer = new MixerBin("mixer");
+    //playpipeline = new PlayPipeline("mixer");
+  }
+  
+  private void mixer_volume_updated(PlayerTransport sender, double volume) {
+    mixer.set_volume(volume);
+  }
+  
+  private void mixer_panorama_updated(PlayerTransport sender, double panorama) {
+    mixer.set_panorama(panorama);
+  }
+  
+  private void mixer_eq_updated(PlayerTransport sender, int band, double val) {
+    mixer.set_eq(band, val);
+  }
+  
+  private bool keep_screen_on() {
+    if (this.recording) {
+      Osso.Status status = this.osso_context.display_blanking_pause();
+      /*if (Osso.Status.OK == status) {
+        stdout.printf("%s\n", "OK");
+      }
+      else if (Osso.Status.INVALID == status) {
+        stdout.printf("%s\n", "INVALID");
+      }
+      else if (Osso.Status.ERROR == status) {
+        stdout.printf("%s\n", "ERROR");
+      }
+      else {
+        stdout.printf("%s\n", "UNKNOWN");
+      }
+      stdout.flush();*/
+      return true;
+    }
+    else {
+      Source.remove(screen_timer_id);
+      return false;
+    }
+  }
+  
+  private void mixer_play_all() {
+    /// TODO clean up old mixer first?
+    mixer = new MixerBin("mixer");
+    int idx = 0;
+    if (0 < project.tracks.length()) {
+      this.playing = true;
+//       for (int i = 0; i < tracks.children.length(); ++i) {
+//         TrackTransport tt = tracks.children.nth_data(i) as TrackTransport;
+//         if (null != tt) {
+//           if (tt.get_active_state()) {
+//             stdout.printf("Track %s active and has been added\n", i.to_string());
+//             //track.bin.set_start_time((Gst.ClockTime)(Time.Nanoseconds.SECOND * idx));
+//             mixer.add_track(tt.track_bin);
+//           }
+//         }
+//         tt = null;
+//       }
+       while (idx < project.tracks.length()) {
+         Track track = project.tracks.nth_data(idx);
+         if (track.active) {
+           stdout.printf("Track %s active and has been added\n", idx.to_string());
+           //track.bin.set_start_time((Gst.ClockTime)(Time.Nanoseconds.SECOND * idx));
+           mixer.add_track(track.bin);
+         }
+         ++idx;
+       }
+      playpipeline = new PlayPipeline("player", mixer);
+      this.playback_position_duration.connect(player.position_duration_callback);
+      playpipeline.position_duration.connect(playpipeline_position_duration_callback);
+      playback_starting();
+      playpipeline.play();
+      playback_started();
+      update_time_and_duration_play_pipeline();
+      if (0 != playback_timer_id) Source.remove(playback_timer_id);
+      playback_timer_id = Timeout.add ((uint)Time.Milliseconds.SECOND / 10, update_time_and_duration_play_pipeline);
+    }
+    if (this.recording) {
+      keep_screen_on();
+      if (0 != screen_timer_id) Source.remove(screen_timer_id);
+      screen_timer_id = Timeout.add ((uint)Time.Milliseconds.SECOND * 20, keep_screen_on);
+      this.playback_position_duration.disconnect(player.position_duration_callback);
+      this.recording_position_duration.connect(player.position_duration_callback);
+      recordpipeline.position_duration.connect(recordpipeline_position_duration_callback);
+      recording_starting();
+      recordpipeline.record();
+      recording_started();
+      update_time_and_duration_record_pipeline();
+      if (0 != recording_timer_id) Source.remove(recording_timer_id);
+      recording_timer_id = Timeout.add ((uint)Time.Milliseconds.SECOND / 10, update_time_and_duration_record_pipeline);
+    }
+  }
+  
+  private void mixer_play() {
+    this.playing = true;
+    playback_resuming();
+    playpipeline.play();
+    playback_resumed();
+  }
+  
+  private void mixer_pause(bool val) {
+    if (val) {
+      playback_pausing();
+      playpipeline.pause();
+      playback_paused();
+    }
+    else {
+      playback_resuming();
+      playpipeline.play();
+      playback_resumed();
+    }
+  }
+  
+  private void mixer_record(bool val) {
+    if (val) {
+      prepare_record();
+    }
+    else {
+      this.recording = false;
+    }
+  }
+  
+  private void mixer_stop() {
+    if (this.recording) {
+      recording_ending();
+      this.recording = false;
+      recordpipeline.stop();
+      //this.import_track_from_uri(Filename.to_uri(recordpipeline.get_location()));
+      Track track = Track.with_uri(Filename.to_uri(recordpipeline.get_location()), "Track-" + (project.last_track_number).to_string(), true, true, 1.0, 0.0);
+      project.tracks.append(track);
+      add_track_transport(track);
+      recordpipeline = null;
+      recording_ended();
+    }
+    if (this.playing) {
+      playback_ending();
+      playpipeline.stop();
+      playpipeline = null;
+      this.playing = false;
+      playback_ended();
+    }
+    //stdout.flush();
+  }
+  
+  private void construct_menu() {
+    Hildon.AppMenu menu = new Hildon.AppMenu();
+    var btnNew  = new Gtk.Button.with_label("New project");
+    btnNew.clicked.connect(new_project);
+    menu.append(btnNew);
+    var btnOpen  = new Gtk.Button.with_label("Open project");
+    btnOpen.clicked.connect(open_project);
+    menu.append(btnOpen);
+    var btnSave  = new Gtk.Button.with_label("Save project");
+    btnSave.clicked.connect(save_project);
+    menu.append(btnSave);
+    var btnImport  = new Gtk.Button.with_label("Import track");
+    btnImport.clicked.connect(open_import);
+    menu.append(btnImport);
+    var btnStartACM  = new Gtk.Button.with_label("Input settings");
+    btnStartACM.clicked.connect(start_acm);
+    menu.append(btnStartACM);
+    var btnBounce  = new Gtk.Button.with_label("Bounce tracks");
+    btnBounce.clicked.connect(prepare_bounce);
+    menu.append(btnBounce);
+    var btnMixdown  = new Gtk.Button.with_label("Mixdown");
+    btnMixdown.clicked.connect(prepare_mixdown);
+    menu.append(btnMixdown);
+    var btnSettings  = new Gtk.Button.with_label("Settings");
+    btnSettings.clicked.connect(show_settings);
+    menu.append(btnSettings);
+    menu.show_all();
+    window.set_app_menu(menu);
+  }
+  private void show_settings() {
+    File settings = File.new_for_path(Environment.get_home_dir() + "/.demorecorder/settings.xml");
+    string errors = "";
+    FirstRunWizard dlg = new FirstRunWizard(window, "Demo Recorder");
+    dlg.set_transient_for(window);
+    dlg.app_settings = application_settings;
+    dlg.run();
+    application_settings = dlg.app_settings;
+    bool saved = ApplicationSettings.save_settings(settings.get_path(), application_settings, ref errors);
+    dlg.destroy();
+    dlg = null;
+  }
+  private void new_project() {
+    bool good = false;
+    // is the current project dirty?
+    if (project_dirty) {
+      // run an are you sure dialog
+      // Do you want to save your current project first? Yes, No, Cancel
+      YesNoCancelDialog alert = new YesNoCancelDialog(window, "Save changes to current project?", "You have unsaved changes in your current project.\nWould you like to save them first?", Gtk.ResponseType.CANCEL);
+      alert.set_transient_for(window);
+      int ret = alert.run();
+      alert.destroy();
+      alert = null;
+      // yes - save_project()
+      if (Gtk.ResponseType.YES == ret) {
+        save_project();
+      }
+      // cancel - return and do nothing
+      else if (Gtk.ResponseType.CANCEL == ret || Gtk.ResponseType.NO != ret) {
+        return;
+      }
+      // carry on and create_project()
+    }
+    ProjectSettingsDialog dlg = new ProjectSettingsDialog.with_values(window, "New project settings", "New project", "", "", application_settings.working_directory);
+    dlg.set_transient_for(window);
+    int ret = dlg.run();
+    if (Gtk.ResponseType.OK == ret) {
+      good = true;
+      this.project = SettingsStructures.ProjectSettings();
+      this.project.name = dlg.get_project_name();
+      this.project.working_directory = dlg.get_project_directory();
+      // ensure that the directories exist.
+      DirUtils.create_with_parents(this.project.working_directory + "/tracks", 0755);
+      DirUtils.create_with_parents(this.project.working_directory + "/mixes", 0755);
+      DirUtils.create_with_parents(this.project.working_directory + "/tmp", 0755);
+      this.project.location = dlg.get_project_filename();
+      save_project();
+    }
+    dlg.destroy();
+    dlg = null;
+    if (good) {
+      // Now should clean up the display in preparation for working on this new project.
+      //load_project(this.project.location);
+      application_settings.last_project_name = project.name;
+      application_settings.last_project_location = project.location;
+      window.set_title(project.name);
+      remove_all_tracks();
+    }
+  }
+  
+  private void open_project() {
+    // is the current project dirty?
+    if (project_dirty) {
+      // run an are you sure dialog
+      // Do you want to save your current project first? Yes, No, Cancel
+      YesNoCancelDialog alert = new YesNoCancelDialog(window, "Save changes to current project?", "You have unsaved changes in your current project.\nWould you like to save them first?", Gtk.ResponseType.CANCEL);
+      alert.set_transient_for(window);
+      int ret = alert.run();
+      alert.destroy();
+      alert = null;
+      // yes - save_project()
+      if (Gtk.ResponseType.YES == ret) {
+        save_project();
+      }
+      // cancel - return and do nothing
+      else if (Gtk.ResponseType.CANCEL == ret || Gtk.ResponseType.NO != ret) {
+        return;
+      }
+      // carry on and load_project()
+    }
+    // get a filename
+    Hildon.FileChooserDialog file_chooser = new Hildon.FileChooserDialog(window, Gtk.FileChooserAction.OPEN);
+    file_chooser.set_show_upnp(false);
+    file_chooser.set_current_folder(application_settings.working_directory);
+    int ret = file_chooser.run();
+    if (Gtk.ResponseType.OK == ret) {
+      load_project(file_chooser.get_filename());
+    }
+    file_chooser.destroy ();
+    file_chooser = null;
+  }
+  
+  private void load_project(string location) {
+    remove_all_tracks();
+    project = SettingsStructures.ProjectSettings();
+    string errors = "";
+    if (XmlHelpers.parse_project_settings_file(location, "1.0", ref project, ref errors)) {
+      // need to tear down and build up the interface again.
+      // as a test let's save it for inspection
+      //save_project_settings(project.location);
+      /// TODO
+      application_settings.last_project_name = project.name;
+      application_settings.last_project_location = project.location;
+      if (ApplicationSettings.save_settings(Environment.get_home_dir() + "/.demorecorder/settings.xml", application_settings, ref errors)) {
+        window.set_title(project.name);
+        for (int idx = 0; idx < project.tracks.length(); ++idx) {
+          add_track_transport((Track)project.tracks.nth_data(idx));
+        }
+      }
+      else {
+        stdout.printf("%s\n", errors);
+        stdout.flush();
+      }
+    }
+    else {
+      Hildon.Note note = new Hildon.Note.information(window, "Project couldn't be opened!\n%s".printf(errors));
+      note.run();
+      note.destroy();
+      note = null;
+    }
+  }
+  
+  private void remove_all_tracks() {
+    List<Gtk.Widget> children = tracks.get_children();
+    for (int idx = 0; idx < children.length(); ++idx) {
+      tracks.remove((Gtk.Widget)children.nth_data(idx));
+    }
+    for (int idx = ((int)project.tracks.length()) - 1; idx > -1; --idx) {
+      project.tracks.delete_link(project.tracks.nth(idx));
+    }
+  }
+  private void remove_track_interactive(TrackTransport tt) {
+    string message = "";
+    string ns = "";
+     switch(application_settings.remove_track_action) {
+      case ApplicationSettings.RemoveTrackAction.DELETE_FILE:
+        remove_track(tt);
+        message = "Track deleted.";
+        FileUtils.remove(Filename.from_uri(tt.track.uri, out ns));
+        break;
+      case ApplicationSettings.RemoveTrackAction.PRESERVE_FILE:
+        remove_track(tt);
+        message = "Track removed.";
+        break;
+      case ApplicationSettings.RemoveTrackAction.PROMPT:
+        YesNoCancelDialog dlg = new YesNoCancelDialog(window, "Remove Track", "Do you want to delete the source file permanently?", Gtk.ResponseType.CANCEL);
+        dlg.set_transient_for(window);
+        int ret = dlg.run();
+        dlg.destroy();
+        if (Gtk.ResponseType.YES == ret) {
+          remove_track(tt);
+          message = "Track deleted.";
+          FileUtils.remove(Filename.from_uri(tt.track.uri, out ns));
+        }
+        else if (Gtk.ResponseType.NO == ret) {
+          remove_track(tt);
+          message = "Track removed.";
+        }
+        break;
+       default:
+         break;
+     }
+    if ("" != message) {
+      Hildon.Note note = new Hildon.Note.information(window, "%s".printf(message));
+      note.run();
+      note.destroy();
+      note = null;
+    }
+  }
+  private void remove_track(TrackTransport tt) {
+    project.tracks.remove(tt.track);
+    tracks.remove(tt);
+    tt = null;
+  }
+  
+  private void save_project() {
+    if (0 < project.location.length) {
+      save_project_settings(project.location);
+    }
+    else {
+      do_save_as_project();
+    }
+    //stdout.printf(project.to_xml_string());
+  }
+  private void do_save_as_project() {
+    // get a filename
+    Hildon.FileChooserDialog file_chooser = new Hildon.FileChooserDialog(window, Gtk.FileChooserAction.SAVE);
+    file_chooser.set_show_upnp(false);
+    int ret = file_chooser.run();
+    if (Gtk.ResponseType.OK == ret) {
+      project.location = file_chooser.get_filename();
+      save_project_settings(project.location);
+    }
+    file_chooser.destroy ();
+    file_chooser = null;
+  }
+  private void save_project_settings(string location) {
+    bool success = false;
+    Hildon.gtk_window_set_progress_indicator(window, (uint)true);
+    string contents = project.to_xml_string();
+    // should ask each ui element for it's updated settings to be on the safe side first.
+    /// TODO
+    if (0 < contents.length)
+    {
+      contents = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + contents;
+      if (FileUtils.set_contents(location, contents, contents.length)) {
+        project_dirty = false;
+        // alert user of success? no!
+        //Gtk.Widget banner = Hildon.Banner.show_information(window, null, "Project saved.");
+        //((Hildon.Banner)banner).set_timeout((uint)Time.Milliseconds.SECOND);
+      }
+      else
+      {
+        // alert user of failure to save file.
+        Hildon.Note alert = new Hildon.Note.information(window, "Project couldn't be saved!\nFailed to save the project settings file.");
+        alert.run();
+        alert.destroy();
+        alert = null;
+      }
+    }
+    else {
+      // alert user of failure to create serialize settings.
+      Hildon.Note alert = new Hildon.Note.information(window, "Project couldn't be saved!\nFailed to serialize the project settings.");
+      alert.run();
+      alert.destroy();
+      alert = null;
+    }
+    Hildon.gtk_window_set_progress_indicator(window, (uint)false);
+  }
+  
+  private void start_acm() {
+    AcmPopUp acm = new AcmPopUp.with_settings("Input Settings", window, project.monitor);
+    acm.set_transient_for(window);
+    acm.play();
+    if (Gtk.ResponseType.OK == acm.run()) {
+      project.monitor = acm.get_settings_structure();
+      project_dirty = true;
+    }
+    acm.stop();
+    acm.destroy();
+    acm = null;
+  }
+  
+  private void prepare_mixdown() {
+    MixdownPopUp dlg = new MixdownPopUp.with_settings("Mixdown your track", window, project.mixdown);
+    dlg.set_transient_for(window);
+    if (Gtk.ResponseType.OK == dlg.run()) {
+      project.mixdown = dlg.get_settings_structure();
+      project_dirty = true;
+      do_mixdown();
+    }
+    dlg.destroy();
+    dlg = null;
+  }
+  
+  private void do_mixdown() {
+    Gst.Caps caps = new Gst.Caps.simple ("audio/x-raw-int",
+                                         "rate", typeof (int), 48000,
+                                         "channels", typeof (int), 2,
+                                         "depth", typeof (int), 16);
+    mixer = new MixerBin("mixer");
+    int idx = 0;
+    if (0 < project.tracks.length()) {
+      while (idx < project.tracks.length()) {
+        Track track = project.tracks.nth_data(idx);
+        if (track.active) {
+          mixer.add_track(track.bin);
+        }
+        ++idx;
+      }
+      this.recording = false;
+      this.playing = false;
+      Gst.Element? encoder = EncoderFactory.get_encoder_bin("encoder", project.mixdown.encoder, project.mixdown.quality, "artist=%s,title=%s,album=%s".printf(project.mixdown.artist, project.mixdown.title, project.mixdown.album));
+      if (null != encoder) {
+        encodepipeline = new EncodePipeline("mixdown", project.mixdown.location, mixer, encoder, caps);
+        encodepipeline.position_duration.connect(encodepipeline_position_duration_callback);
+        Hildon.gtk_window_set_progress_indicator(window, (uint)true);
+        encoding_starting();
+        encodepipeline.encode();
+        encoding_started();
+        update_time_and_duration_encode_pipeline();
+        if (0 != recording_timer_id) Source.remove(encoding_timer_id);
+        encoding_timer_id = Timeout.add ((uint)Time.Milliseconds.SECOND, update_time_and_duration_encode_pipeline);
+        ProgressPopUp dlg = new ProgressPopUp("Mixing down", window);
+        dlg.set_message("Mixing down your track.\nThis may take some time depending on the duration of the audio.\n");
+        dlg.set_transient_for(window);
+        this.encoding_position_duration.connect((p,d) => {
+          double percent = ((double)(p / Time.Nanoseconds.MILLISECOND) / (double)(d / Time.Nanoseconds.MILLISECOND)); 
+          dlg.set_progress(percent);
+          dlg.set_progress_text("%s of %s completed".printf(Time.time_to_string(p), Time.time_to_string(d)));
+          //if (p >= d) {
+          //  dlg.close_me();
+          //  this.mixer_stop();
+          //}
+        });
+        encodepipeline.end_of_stream.connect((e) => {encoding_ending();dlg.close_me();encoding_time_elapsed();this.mixer_stop();encoding_ended();});
+        if (Gtk.ResponseType.CANCEL == dlg.run()) {
+          // Should really stop the mixer and delete the files!
+          /// TODO
+          this.mixer_stop();
+        }
+        dlg.destroy();
+        dlg = null;
+        //encoder = null;
+        Hildon.gtk_window_set_progress_indicator(window, (uint)false);
+        Gtk.Widget banner = Hildon.Banner.show_information(window, null, "Mixdown complete");
+        ((Hildon.Banner)banner).set_timeout((uint)Time.Milliseconds.SECOND);
+      }
+      else {
+        Gtk.Widget banner = Hildon.Banner.show_information(window, null, "Mixdown failed! Encoder not available.");
+        ((Hildon.Banner)banner).set_timeout((uint)Time.Milliseconds.SECOND);
+      }
+    }
+  }
+  
+  private void prepare_bounce() {
+    project.last_track_number++;
+    string new_location = "%s/tracks/bounce-%s.wav".printf(project.working_directory, project.last_track_number.to_string());
+    string name = "bounce-" + project.last_track_number.to_string();
+    do_bounce(new_location);
+  }
+  
+  private void do_bounce(string filename) {
+    Gst.Caps caps = new Gst.Caps.simple ("audio/x-raw-int",
+                                         "rate", typeof (int), 48000,
+                                         "channels", typeof (int), 2,
+                                         "depth", typeof (int), 16);
+    //Gst.Element src = Gst.ElementFactory.make("pulsesrc", "input");
+    //src.set_property("device", "source.hw0");
+    mixer = new MixerBin("mixer");
+    int idx = 0;
+    if (0 < project.tracks.length()) {
+      while (idx < project.tracks.length()) {
+        Track track = project.tracks.nth_data(idx);
+        if (track.active) {
+          mixer.add_track(track.bin);
+        }
+        ++idx;
+      }
+      this.recording = true;
+      this.playing = false;
+      recordpipeline = new RecordPipeline("bouncer", filename, mixer, caps, "wavenc");
+      recordpipeline.bouncing = true;
+      recordpipeline.position_duration.connect(recordpipeline_position_duration_callback);
+      Hildon.gtk_window_set_progress_indicator(window, (uint)true);
+      recording_starting();
+      recordpipeline.record();
+      recording_started();
+      update_time_and_duration_record_pipeline();
+      if (0 != recording_timer_id) Source.remove(recording_timer_id);
+      recording_timer_id = Timeout.add ((uint)Time.Milliseconds.SECOND, update_time_and_duration_record_pipeline);
+      ProgressPopUp dlg = new ProgressPopUp("Bouncing tracks", window);
+      dlg.set_transient_for(window);
+      dlg.set_message("Bouncing your selected tracks.\nThis may take some time depending on the duration of the audio.\n");
+      this.recording_position_duration.connect((p,d) => {
+        double percent = ((double)(p / Time.Nanoseconds.MILLISECOND) / (double)(d / Time.Nanoseconds.MILLISECOND)); 
+        dlg.set_progress(percent);
+        dlg.set_progress_text("%s of %s completed".printf(Time.time_to_string(p), Time.time_to_string(d)));
+        if (p >= d) {
+          dlg.close_me();
+          this.mixer_stop();
+          this.recording = false;
+          if (0 != recording_timer_id) Source.remove(recording_timer_id);
+        }
+      });
+      if (Gtk.ResponseType.CANCEL == dlg.run()) {
+        // Should really stop the mixer and delete the files!
+        /// TODO
+        this.recording = false;
+        this.mixer_stop();
+      }
+      dlg.destroy();
+      dlg = null;
+      Hildon.gtk_window_set_progress_indicator(window, (uint)false);
+      Gtk.Widget banner = Hildon.Banner.show_information(window, null, "Bounced track added to mixer");
+      ((Hildon.Banner)banner).set_timeout((uint)Time.Milliseconds.SECOND);
+    }
+  }
+  
+  public void prepare_record() {
+    project.last_track_number++;
+    string new_location = "%s/tracks/track-%s.wav".printf(project.working_directory, project.last_track_number.to_string());
+    string name = "Track-" + project.last_track_number.to_string();
+    do_record(name, new_location);
+  }
+  
+  public void do_record(string name, string filename) {
+    Gst.Caps caps = new Gst.Caps.simple ("audio/x-raw-int",
+                                         "rate", typeof (int), 48000,
+                                         "channels", typeof (int), 2,
+                                         "depth", typeof (int), 16);
+    InputBin src = new InputBin.with_settings("input", project.monitor.input);
+    //Gst.Element src = Gst.ElementFactory.make("pulsesrc", "input");
+    //src.set_property("device", "source.hw0");
+    recordpipeline = new RecordPipeline("recorder", filename, src, caps, "wavenc");
+    recordpipeline.bouncing = false;
+    this.recording = true;
+    //recordpipeline.record();
+  }
+  
+  public void open_import() {
+    Hildon.FileChooserDialog file_chooser = new Hildon.FileChooserDialog(window, Gtk.FileChooserAction.OPEN);
+    file_chooser.set_show_upnp(false);
+    file_chooser.set_current_folder(application_settings.working_directory);
+    if (file_chooser.run () == Gtk.ResponseType.OK) {
+      import_track_from_uri(file_chooser.get_uri());
+    }
+    file_chooser.destroy ();
+  }
+  
+  public void import_track_from_uri(string uri) {
+    /// TODO - Should become import track from uri and convert to wav and name appropriately
+    ++project.last_track_number;
+    project_dirty = true;
+    Gst.Caps caps = new Gst.Caps.simple ("audio/x-raw-int",
+                                         "rate", typeof (int), 48000,
+                                         "channels", typeof (int), 2,
+                                         "depth", typeof (int), 16);
+    /// TODO check that directories exist
+    string new_location = "%s/tracks/import-%s.wav".printf(project.working_directory, project.last_track_number.to_string());
+    string name = "Import-" + project.last_track_number.to_string();
+    ImportBin import_bin = new ImportBin(name);
+    import_bin.set_uri(uri);
+    
+    /// TODO encode to correct destination and add track
+    this.recording = false;
+    this.playing = false;
+    recordpipeline = new RecordPipeline("importer", new_location, import_bin, caps, "wavenc");
+    recordpipeline.bouncing = true;
+    recordpipeline.position_duration.connect(recordpipeline_position_duration_callback);
+    Hildon.gtk_window_set_progress_indicator(window, (uint)true);
+    recording_starting();
+    recordpipeline.record();
+    recording_started();
+    update_time_and_duration_record_pipeline();
+    if (0 != recording_timer_id) Source.remove(recording_timer_id);
+    recording_timer_id = Timeout.add ((uint)Time.Milliseconds.SECOND, update_time_and_duration_record_pipeline);
+    ProgressPopUp dlg = new ProgressPopUp("Importing track", window);
+    dlg.set_transient_for(window);
+    dlg.set_message("Importing your selected track.\nThis may take some time depending on the duration of the audio.\n");
+    this.recording_position_duration.connect((p,d) => {
+      double percent = ((double)(p / Time.Nanoseconds.MILLISECOND) / (double)(d / Time.Nanoseconds.MILLISECOND)); 
+      dlg.set_progress(percent);
+      dlg.set_progress_text("%s of %s completed".printf(Time.time_to_string(p), Time.time_to_string(d)));
+    });
+    recordpipeline.end_of_stream.connect(() => {
+      dlg.close_me();
+      recordpipeline.stop();
+      if (0 != recording_timer_id) Source.remove(recording_timer_id);
+      Track track = Track.with_uri(Filename.to_uri(new_location), name, true, true, 1.0, 0.0);
+      project.tracks.append(track);
+      add_track_transport(track);
+      this.recording = false;
+    });
+    if (Gtk.ResponseType.CANCEL == dlg.run()) {
+      // Should really stop the mixer and delete the files!
+      /// TODO
+      //Track track = new Track.with_uri(Filename.to_uri(new_location), name, true, true, 1.0, 0.0);
+      //project.tracks.append(track);
+      //add_track_transport(track);
+      recordpipeline.stop();
+      this.recording = false;
+    }
+    dlg.destroy();
+    dlg = null;
+    Hildon.gtk_window_set_progress_indicator(window, (uint)false);
+    Gtk.Widget banner = Hildon.Banner.show_information(window, null, "Imported track added to mixer");
+    ((Hildon.Banner)banner).set_timeout((uint)Time.Milliseconds.SECOND);
+  }
+  
+  private void add_track_transport(Track track) {
+    TrackTransport trackt = new TrackTransport();
+    trackt.window = this.window;
+    trackt.track_bin = track.bin;
+    trackt.track = track;
+    trackt.title = track.label; //"Track %n".printf(project.track.length());
+    trackt.name = track.label;
+    trackt.delete_clicked.connect((tt) => {this.remove_track_interactive(tt);});
+    trackt.volume_updated.connect((volume) => {trackt.track_bin.set_volume(volume);});
+    trackt.panorama_updated.connect((panorama) => {trackt.track_bin.set_panorama(panorama);});
+    trackt.eq_updated.connect((band, val) => {trackt.track_bin.set_eq(band, val);});
+    trackt.echo_max_delay_updated.connect((val) => {trackt.track_bin.set_echo_max_delay(val);});
+    trackt.echo_delay_updated.connect((val) => {trackt.track_bin.set_echo_delay(val);});
+    trackt.echo_feedback_updated.connect((val) => {trackt.track_bin.set_echo_feedback(val);});
+    trackt.echo_intensity_updated.connect((val) => {trackt.track_bin.set_echo_intensity(val);});
+    trackt.echo_toggled.connect((val) => {trackt.track_bin.set_echo_active(val);});
+    trackt.dynamics_mode_updated.connect((val) => {trackt.track_bin.set_dynamics_mode(val);});
+    trackt.dynamics_characteristics_updated.connect((val) => {trackt.track_bin.set_dynamics_characteristics(val);});
+    trackt.dynamics_ratio_updated.connect((val) => {trackt.track_bin.set_dynamics_ratio(val);});
+    trackt.dynamics_threshold_updated.connect((val) => {trackt.track_bin.set_dynamics_threshold(val);});
+    trackt.dynamics_toggled.connect((val) => {trackt.track_bin.set_dynamics_active(val);});
+    trackt.active_toggled.connect((val) => {trackt.track.active = val;});
+    this.playback_starting.connect(trackt.playback_starting_callback);
+    this.playback_ending.connect(trackt.playback_ending_callback);
+    this.recording_starting.connect(trackt.recording_starting_callback);
+    this.recording_ending.connect(trackt.recording_ending_callback);
+    trackt.set_active_state(track.active);
+    tracks.pack_start(trackt, false, false, 4);
+    tracks.show_all();
+  }
+  
+  public void run() {
+    window.show_all();
+    
+    application_settings = new ApplicationSettings();
+    bool first_run = true;
+    string settings_path = "";
+    string errors = "";
+    File settings;
+    // can we get at our settings file? 
+    File home_dir = File.new_for_path (Environment.get_home_dir());
+    // do we have a .demorecorder dir
+    File app_dir = File.new_for_path(home_dir.get_path() + "/.demorecorder");
+    if (app_dir.query_exists(null)) {
+      // now check for the existence of the settings file_chooser
+      settings = File.new_for_path(app_dir.get_path() + "/settings.xml");
+      if (settings.query_exists(null)) {
+        first_run = false;
+      }
+      else {
+        // if not assume first run and show settings wizard
+      }
+    }
+    else {
+      int dir_ret = DirUtils.create_with_parents(app_dir.get_path(), 0755);
+    }
+    settings = File.new_for_path(app_dir.get_path() + "/settings.xml");
+    if (first_run) {
+      // create and save settings
+      /// TODO
+      FirstRunWizard dlg = new FirstRunWizard(window, "Demo Recorder");
+      dlg.set_transient_for(window);
+      dlg.run();
+      application_settings = dlg.app_settings;
+      bool saved = ApplicationSettings.save_settings(settings.get_path(), application_settings, ref errors);
+      dlg.destroy();
+      dlg = null;
+    }
+    // may as well load the settings now
+    application_settings = ApplicationSettings.load_settings(settings.get_path(), "1.0", out errors);
+    if (null == application_settings) {
+      // this is major, bail for now
+      Gtk.Widget banner = Hildon.Banner.show_information(window, null, "Failed to create settings!\n%s\nQuiting application.".printf(errors));
+      ((Hildon.Banner)banner).set_timeout((uint)Time.Milliseconds.SECOND * 2);
+      stdout.printf(errors + "\n");
+      stdout.flush();
+      window.destroy();
+      exit(-1);
+    }
+    bool has_last_project = (0 < application_settings.last_project_location.length);
+    // we have either old or new settings
+    if (has_last_project) {
+      switch(application_settings.open_action) {
+        case ApplicationSettings.OpenAction.RECENT_PROJECTS:
+          // show recent projects dialog
+          /// TODO
+          load_project(application_settings.last_project_location);
+          break;
+        case ApplicationSettings.OpenAction.NAMED_PROJECT:
+          if (0 < application_settings.named_project_location.length) {
+            load_project(application_settings.named_project_location);
+          }
+          else {
+            // show recent projects dialog
+            /// TODO
+            load_project(application_settings.last_project_location);
+          }
+          break;
+        case ApplicationSettings.OpenAction.LAST_PROJECT:
+          load_project(application_settings.last_project_location);
+          break;
+        default:
+          new_project();
+          break;
+      }
+    }
+    else {
+      new_project();
+    }
+    
+    Gtk.main();
+  }
+  
+  private bool on_delete_event() {
+    bool ret = false;
+    // do we have a dirty project?
+    if (project_dirty) {
+      // what should we do?
+      switch(application_settings.close_action) {
+        case ApplicationSettings.CloseAction.AUTO_SAVE_PROJECT:
+          save_project();
+          ret = project_dirty;
+          break;
+        case ApplicationSettings.CloseAction.PROMPT_SAVE_PROJECT:
+          YesNoCancelDialog alert = new YesNoCancelDialog(window, "Save changes to current project?", "You have unsaved changes in your current project.\nWould you like to save them first?", Gtk.ResponseType.CANCEL);
+          alert.set_transient_for(window);
+          int yn_ret = alert.run();
+          alert.destroy();
+          alert = null;
+          // yes - save_project()
+          if (Gtk.ResponseType.YES == yn_ret) {
+            save_project();
+            ret = project_dirty;
+            break;
+          }
+          // cancel - return and do nothing
+          else if (Gtk.ResponseType.NO == yn_ret) {
+            ret = false;
+            break;
+          }
+          else { //(Gtk.ResponseType.CANCEL != ret) {
+            ret = true;
+            break;
+          }
+        default: // DO_NOTHING
+          ret = false;
+          break;
+      }
+    }
+    return ret;
+  }
+  
+}
+
+}
diff --git a/src/DynamicsPopUp.vala b/src/DynamicsPopUp.vala
new file mode 100644 (file)
index 0000000..b2b2885
--- /dev/null
@@ -0,0 +1,198 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+namespace IdWorks {
+
+public class DynamicsPopUp : Hildon.Dialog {
+
+  Hildon.TouchSelector characteristics_selector;
+  Hildon.TouchSelector mode_selector;
+  Gtk.HScale ratio_slider;
+  Gtk.HScale threshold_slider;
+  
+  public signal void characteristics_updated(string val);
+  public signal void mode_updated(string val);
+  public signal void ratio_updated(double val);
+  public signal void threshold_updated(double val);
+  
+  public SettingsStructures.DynamicsSettings get_settings_structure() {
+    SettingsStructures.DynamicsSettings settings = SettingsStructures.DynamicsSettings();
+    settings.active = true;
+    settings.mode = this.get_mode();
+    settings.characteristics = this.get_characteristics();
+    settings.ratio = this.get_ratio();
+    settings.threshold = this.get_threshold();
+    return settings;
+  }
+  public void set_settings_structure(SettingsStructures.DynamicsSettings settings) {
+    // ignore active for now
+    this.set_mode(settings.mode);
+    this.set_characteristics(settings.characteristics);
+    this.set_ratio(settings.ratio);
+    this.set_threshold(settings.threshold);
+  }
+  
+  public DynamicsPopUp(string title, Gtk.Widget parent) {
+    this.set_title(title);
+    this.set_parent(parent);
+    this.construct_interface(SettingsStructures.DynamicsSettings());
+  }
+  public DynamicsPopUp.with_settings(string title, Gtk.Widget parent, SettingsStructures.DynamicsSettings settings) {
+    this.set_title(title);
+    this.set_parent(parent);
+    stdout.printf("dynamics popup constructing interface\n");
+    stdout.flush();
+    this.construct_interface(settings);
+    stdout.printf("dynamics popup interface constructed\n");
+    stdout.flush();
+  }
+   
+  private void construct_interface(SettingsStructures.DynamicsSettings settings) {
+    this.set_default_response(Gtk.ResponseType.ACCEPT);
+    this.add_button(Gtk.STOCK_CLOSE, Gtk.ResponseType.ACCEPT);
+    
+    Gtk.VBox control_area = (Gtk.VBox)this.get_content_area();
+    
+    Gtk.HButtonBox button_bar = new Gtk.HButtonBox();
+    button_bar.set_layout(Gtk.ButtonBoxStyle.START);
+    button_bar.set_homogeneous(true);
+    button_bar.set_spacing(4);
+    
+    mode_selector = new Hildon.TouchSelector.text();
+    mode_selector.append_text("compressor");
+    mode_selector.append_text("expander");
+    //mode_selector.set_active(0, 0);
+    stdout.printf("dynamics setting mode %s\n", settings.mode);
+    stdout.flush();
+    this.set_mode(settings.mode);
+    stdout.printf("dynamics mode set %s\n", settings.mode);
+    stdout.flush();
+    Hildon.PickerButton mode_picker = new Hildon.PickerButton(Hildon.SizeType.FINGER_HEIGHT | Hildon.SizeType.AUTO_WIDTH, Hildon.ButtonArrangement.VERTICAL);
+    mode_picker.set_title("Mode:");
+    mode_picker.set_selector(mode_selector);
+    mode_picker.set_alignment(0, 0, 1, 1);
+    stdout.printf("dynamics mode connect\n");
+    stdout.flush();
+    mode_picker.value_changed.connect( (s) => { mode_updated(s.get_selector().get_current_text()); } );
+    stdout.printf("dynamics mode connected\n");
+    stdout.flush();
+    button_bar.pack_start(mode_picker, true, true, 2);
+    
+    stdout.printf("dynamics mode starting 0\n");
+    stdout.flush();
+    characteristics_selector = new Hildon.TouchSelector.text();
+    stdout.printf("dynamics mode starting 1\n");
+    stdout.flush();
+    characteristics_selector.append_text("soft-knee");
+    stdout.printf("dynamics mode starting 2\n");
+    stdout.flush();
+    characteristics_selector.append_text("hard-knee");
+    //characteristics_selector.set_active(0, 0);
+    stdout.printf("dynamics setting characteristics %s\n", settings.characteristics);
+    stdout.flush();
+    this.set_characteristics(settings.characteristics);
+    Hildon.PickerButton characteristics_picker = new Hildon.PickerButton(Hildon.SizeType.FINGER_HEIGHT | Hildon.SizeType.AUTO_WIDTH, Hildon.ButtonArrangement.VERTICAL);
+    characteristics_picker.set_title("Characteristics:");
+    characteristics_picker.set_selector(characteristics_selector);
+    characteristics_picker.set_alignment(0, 0, 1, 1);
+    characteristics_picker.value_changed.connect( (s) => { characteristics_updated(s.get_selector().get_current_text()); } );
+    button_bar.pack_start(characteristics_picker, true, true, 2);
+    
+    control_area.pack_start(button_bar, true, true, 2);
+    
+    Gtk.HBox ratio_bar = new Gtk.HBox(false, 4);
+    
+    Gtk.Label ratio_label = new Gtk.Label("Ratio: ");
+    ratio_slider = new Gtk.HScale.with_range(0.0, 25.0, 0.5);
+    ratio_slider.set_value_pos(Gtk.PositionType.LEFT);
+    stdout.printf("dynamics setting ratio\n");
+    stdout.flush();
+    this.set_ratio(settings.ratio);
+    ratio_slider.value_changed.connect((s) => {ratio_updated(s.get_value());});
+    ratio_bar.pack_start(ratio_label, false, false, 2);
+    ratio_bar.pack_start(ratio_slider, true, true, 2);
+    
+    control_area.pack_start(ratio_bar, true, true, 2);
+    
+    Gtk.HBox threshold_bar = new Gtk.HBox(false, 4);
+    
+    Gtk.Label threshold_label = new Gtk.Label("Threshold: ");
+    threshold_slider = new Gtk.HScale.with_range(0.0, 1.0, 0.05);
+    threshold_slider.set_value_pos(Gtk.PositionType.LEFT);
+    stdout.printf("dynamics setting threshold\n");
+    stdout.flush();
+    this.set_threshold(settings.threshold);
+    threshold_slider.value_changed.connect((s) => {threshold_updated(s.get_value());});
+    threshold_bar.pack_start(threshold_label, false, false, 2);
+    threshold_bar.pack_start(threshold_slider, true, true, 2);
+    
+    control_area.pack_start(threshold_bar, true, true, 2);
+    
+    this.show_all();
+  }
+  
+  public string get_mode() {
+    return mode_selector.get_current_text();
+  }
+  public void set_mode(string val) {
+    switch (val) {
+      case "compressor":
+        mode_selector.set_active(0, 0);
+        break;
+      case "expander":
+        mode_selector.set_active(0, 1);
+        break;
+      default:
+        mode_selector.set_active(0, 0);
+        break;
+    }
+  }
+  
+  public string get_characteristics() {
+    return characteristics_selector.get_current_text();
+  }
+  public void set_characteristics(string val) {
+    switch (val) {
+      case "soft-knee":
+        characteristics_selector.set_active(0, 0);
+        break;
+      case "hard-knee":
+        characteristics_selector.set_active(0, 1);
+        break;
+      default:
+        characteristics_selector.set_active(0, 0);
+        break;
+    }
+  }
+  
+  public double get_ratio() {
+    return ratio_slider.get_value();
+  }
+  public void set_ratio(double val) {
+    ratio_slider.set_value(val);
+  }
+  
+  public double get_threshold() {
+    return threshold_slider.get_value();
+  }
+  public void set_threshold(double val) {
+    threshold_slider.set_value(val);
+  }
+
+}
+
+}
\ No newline at end of file
diff --git a/src/EchoPopUp.vala b/src/EchoPopUp.vala
new file mode 100644 (file)
index 0000000..404163b
--- /dev/null
@@ -0,0 +1,131 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+namespace IdWorks {
+
+public class EchoPopUp : Hildon.Dialog {
+  
+  Gtk.HScale delay_slider;
+  Gtk.HScale feedback_slider;
+  Gtk.HScale intensity_slider;
+  
+  const uint64 MAX_DELAY = 1 * Time.Milliseconds.SECOND;
+  
+  public signal void delay_updated(uint64 val);
+  public signal void feedback_updated(double val);
+  public signal void intensity_updated(double val);
+  
+  public EchoPopUp (string title, Gtk.Widget parent) {
+    this.set_title(title);
+    this.set_parent(parent);
+    
+    construct_interface(SettingsStructures.EchoSettings());
+  }
+  public EchoPopUp.with_settings (string title, Gtk.Widget parent, SettingsStructures.EchoSettings settings) {
+    this.set_title(title);
+    this.set_parent(parent);
+    
+    construct_interface(settings);
+  }
+  
+  public SettingsStructures.EchoSettings get_settings_structure() {
+    SettingsStructures.EchoSettings settings = SettingsStructures.EchoSettings();
+    settings.active = true;
+    settings.max_delay = MAX_DELAY;
+    settings.delay = this.get_delay();
+    settings.feedback = this.get_feedback();
+    settings.intensity = this.get_intensity();
+    return settings;
+  }
+  public void set_settings_structure(SettingsStructures.EchoSettings settings) {
+    // ignore active for now
+    // ignore max_delay for now
+    this.set_delay(settings.delay);
+    this.set_feedback(settings.feedback);
+    this.set_intensity(settings.intensity);
+  }
+  
+  private void construct_interface(SettingsStructures.EchoSettings settings) {
+    
+    this.set_default_response(Gtk.ResponseType.ACCEPT);
+    this.add_button(Gtk.STOCK_CLOSE, Gtk.ResponseType.ACCEPT);
+    
+    Gtk.VBox control_area = (Gtk.VBox)this.get_content_area();
+    
+    Gtk.HBox delay_bar = new Gtk.HBox(false, 4);
+    
+    Gtk.Label delay_label = new Gtk.Label("Delay (ms): ");
+    delay_slider = new Gtk.HScale.with_range(0, MAX_DELAY, Time.Milliseconds.MILLISECOND);
+    delay_slider.set_value_pos(Gtk.PositionType.LEFT);
+    delay_slider.set_value(settings.delay);
+    delay_slider.value_changed.connect((s) => {delay_updated((uint64)(s.get_value()) * Time.Nanoseconds.MILLISECOND);});
+    delay_bar.pack_start(delay_label, false, false, 2);
+    delay_bar.pack_start(delay_slider, true, true, 2);
+    
+    control_area.pack_start(delay_bar, true, true, 2);
+    
+    Gtk.HBox feedback_bar = new Gtk.HBox(false, 4);    
+    
+    Gtk.Label feedback_label = new Gtk.Label("Feedback (ratio): ");
+    feedback_slider = new Gtk.HScale.with_range(0.0, 1.0, 0.05);
+    feedback_slider.set_value_pos(Gtk.PositionType.LEFT);
+    feedback_slider.set_value(settings.feedback);
+    feedback_slider.value_changed.connect((s) => {feedback_updated(s.get_value());});
+    feedback_bar.pack_start(feedback_label, false, false, 2);
+    feedback_bar.pack_start(feedback_slider, true, true, 2);
+    
+    control_area.pack_start(feedback_bar, true, true, 2);
+    
+    Gtk.HBox intensity_bar = new Gtk.HBox(false, 4);    
+    
+    Gtk.Label intensity_label = new Gtk.Label("Intensity (ratio): ");
+    intensity_slider = new Gtk.HScale.with_range(0.0, 1.0, 0.05);
+    intensity_slider.set_value_pos(Gtk.PositionType.LEFT);
+    intensity_slider.set_value(settings.intensity);
+    intensity_slider.value_changed.connect((s) => {intensity_updated(s.get_value());});
+    intensity_bar.pack_start(intensity_label, false, false, 2);
+    intensity_bar.pack_start(intensity_slider, true, true, 2);
+    
+    control_area.pack_start(intensity_bar, true, true, 2);
+    
+    this.show_all();
+  }
+  
+  public uint64 get_delay() {
+    return (uint64) (delay_slider.get_value() * Time.Nanoseconds.MILLISECOND);
+  }
+  public void set_delay(uint64 val) {
+    delay_slider.set_value((double) (val / Time.Nanoseconds.MILLISECOND));
+  }
+  
+  public double get_feedback() {
+    return feedback_slider.get_value();
+  }
+  public void set_feedback(double val) {
+    feedback_slider.set_value(val);
+  }
+  
+  public double get_intensity() {
+    return intensity_slider.get_value();
+  }
+  public void set_intensity(double val) {
+    intensity_slider.set_value(val);
+  }
+
+}
+
+}
\ No newline at end of file
diff --git a/src/EncodePipeline.vala b/src/EncodePipeline.vala
new file mode 100644 (file)
index 0000000..00e3bdf
--- /dev/null
@@ -0,0 +1,208 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+namespace IdWorks {
+  
+  public class EncodePipeline : Gst.Pipeline {
+    
+    Gst.Element src;
+    Gst.Element queue;
+    Gst.Element resampler;
+    Gst.Element encoder;
+    Gst.Element converter;
+    Gst.Element filesink;
+    //string name;
+    string location;
+    //string encoder_element;
+    Gst.Caps caps;
+    Gst.Query duration_query;
+    Gst.Query position_query;
+    int64 position;
+    int64 old_position;
+    int64 duration;
+    int same_position_counter;
+    
+    public signal void position_duration(int64 position, int64 duration);
+    public signal void end_of_stream();
+    public signal void stream_error(string msg);
+    
+    public EncodePipeline(string name, string location, Gst.Element src, Gst.Element encoder, Gst.Caps caps) {
+      this.name = name;
+      this.location = location;
+      this.caps = caps;
+      this.src = src;
+      this.encoder = encoder;
+      //set the tag handling delegate
+      //this.handle_tags_delegate = this.handle_tags;
+      //set some default values
+      this.position=0;
+      this.duration=0;
+      this.construct_pipeline();
+      //define the queries
+      duration_query = new Gst.Query.duration(Gst.Format.TIME);
+      position_query = new Gst.Query.position(Gst.Format.TIME);
+    }
+    
+    public bool set_src(Gst.Element src) {
+      this.set_state(Gst.State.NULL);
+      if (null != this.src) {
+        clear_src();
+      }
+      this.src = src;
+      this.add(this.src);
+      this.src.link(this.queue);
+      return true;
+    }
+    
+    public void clear_src() {
+      if (null != this.src) {
+        this.set_state(Gst.State.NULL);
+        this.src.unlink(this.queue);
+        this.remove(this.src);
+        this.src = null;
+      }
+    }
+    
+    public bool set_encoder(Gst.Element encoder) {
+      this.set_state(Gst.State.NULL);
+      if (null != this.encoder) {
+        clear_encoder();
+      }
+      this.encoder = encoder;
+      this.add(this.encoder);
+      this.converter.link(this.encoder);
+      this.encoder.link(this.filesink);
+      return true;
+    }
+    
+    public void clear_encoder() {
+      if (null != this.encoder) {
+        this.set_state(Gst.State.NULL);
+        this.converter.unlink(this.encoder);
+        this.encoder.unlink(this.filesink);
+        this.remove(this.encoder);
+        this.encoder = null;
+      }
+    }
+    
+    public bool set_location(string location) {
+      this.set_state(Gst.State.NULL);
+      this.location = location;
+      this.filesink.set_property("location", location);
+      return true;
+    }
+    
+    public string get_location() {
+      return this.location;
+    }
+    
+    public void get_duration_info() {
+      bool duration_result,position_result;
+      Gst.Format format = Gst.Format.TIME;
+      duration_result = this.query(this.duration_query);
+      position_result = this.query(this.position_query);
+      if ( duration_result && position_result  ) {
+        this.duration_query.parse_duration(out format, out this.duration);
+        this.position_query.parse_position(out format, out this.position);
+        if (old_position == this.position) {
+          ++same_position_counter;
+        }
+        old_position = this.position;
+        this.position_duration(this.position,this.duration);
+        if (
+          (this.position >= this.duration) ||
+          ((5 < same_position_counter) && ((this.position + 100 * Time.Nanoseconds.MILLISECOND) >= this.duration))
+          ) {
+          this.end_of_stream();
+        }
+      }
+    }
+    
+    public void encode() {
+      this.set_state(Gst.State.PLAYING);
+    }
+    
+    public void stop() {
+      this.end_of_stream();
+      this.set_state(Gst.State.READY);
+    }
+    
+    private void construct_pipeline() {
+      ///this.name= name;
+      
+      if (null != this.src) {
+        this.add(this.src);
+      }
+      
+      this.queue = Gst.ElementFactory.make("queue", "buffer");
+      this.add(this.queue);
+      
+      this.converter = Gst.ElementFactory.make("audioconvert", "converter");
+      this.add(this.converter);
+      
+      
+      //this.resampler = Gst.ElementFactory.make("audioresample", "resampler");
+      //this.resampler.set_property("quality", 10);
+      //this.add(resampler);
+      //this.encoder = Gst.ElementFactory.make(encoder_element, "encoder");
+      this.add(this.encoder);
+      if (null != this.encoder) {
+//         stdout.printf("Encoder added!\n");
+//         stdout.flush();
+      }
+      this.filesink = Gst.ElementFactory.make("filesink", "fileoutput");
+      if (null != this.filesink) {
+        this.filesink.set_property("location", location);
+//         stdout.printf("Location set!\n");
+//         stdout.flush();
+        this.add(this.filesink);
+//         stdout.printf("Filesink added!\n");
+//         stdout.flush();
+      }
+      
+      if (null != this.src && this.src.link(this.queue)) {
+//         stdout.printf("Source linked!\n");
+//         stdout.flush();
+      }
+      if (this.queue.link(this.converter)) {
+//         stdout.printf("Queue linked!\n");
+//         stdout.flush();
+      }
+      /*
+      if (this.converter.link(this.resampler)) {
+        stdout.printf("Converter linked!\n");
+        stdout.flush();
+      }
+      if (this.resampler.link_filtered(this.encoder, caps)) {
+        stdout.printf("Resampler linked!\n");
+        stdout.flush();
+      }
+      */
+      if (this.converter.link(this.encoder)) {
+//         stdout.printf("Converter linked!\n");
+//         stdout.flush();
+      }
+      if (this.encoder.link(this.filesink)) {
+//         stdout.printf("Encoder linked!\n");
+//         stdout.flush();
+      }
+    }
+    
+    
+  }
+  
+}
\ No newline at end of file
diff --git a/src/EncoderFactory.vala b/src/EncoderFactory.vala
new file mode 100644 (file)
index 0000000..f2eec5d
--- /dev/null
@@ -0,0 +1,126 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+namespace IdWorks {
+
+public static class EncoderFactory {
+
+  public static Gst.Bin? get_encoder_bin(string name, string encoder, string quality, string? taglist) {
+    switch (encoder) {
+      case "vorbisenc":
+        return get_encoder_vorbisenc(name, quality, taglist);
+      case "nokiaaacenc":
+        return get_encoder_nokiaaacenc(name, quality, taglist);
+      case "wavenc":
+        return get_encoder_wavenc(name, quality, taglist);
+      default:
+        return null;
+    }
+  }
+  
+  public static Gst.Bin get_encoder_wavenc(string name, string quality, string? taglist) {
+    // Quality ignored for now
+    Gst.Bin enc_bin = new Gst.Bin(name);
+    Gst.Element taginject = Gst.ElementFactory.make("taginject", "injector");
+    Gst.ElementFactory.make("wavenc", "encoder");
+    enc_bin.add(taginject);
+    enc_bin.add(enc_bin);
+    if (null != taglist) {
+      taginject.set_property("tags", taglist);
+    }
+    taginject.link(enc_bin);
+    Gst.GhostPad sink_pad = new Gst.GhostPad("sink", taginject.get_static_pad("sink"));
+    enc_bin.add_pad(sink_pad);
+    Gst.GhostPad src_pad = new Gst.GhostPad("src", enc_bin.get_static_pad("src"));
+    enc_bin.add_pad(src_pad);
+    return enc_bin;
+  }
+  
+  public static Gst.Bin get_encoder_vorbisenc(string name, string quality, string? taglist) {
+    double q = 1.0;
+    switch (quality) {
+      case "high":
+        q = 1.0;
+        break;
+      case "medium":
+        q = 0.6;
+        break;
+      case "low":
+        q = 0.3;
+        break;
+      default:
+        q = 1.0;
+        break;
+    }
+    Gst.Bin enc_bin = new Gst.Bin(name);
+    Gst.Element taginject = Gst.ElementFactory.make("taginject", "injector");
+    Gst.Element encoder = Gst.ElementFactory.make("vorbisenc", "encoder");
+    Gst.Element muxer = Gst.ElementFactory.make("oggmux", "muxer");
+    enc_bin.add(taginject);
+    enc_bin.add(encoder);
+    enc_bin.add(muxer);
+    encoder.set_property("quality", q);
+    if (null != taglist) {
+      taginject.set_property("tags", taglist);
+    }
+    taginject.link(encoder);
+    encoder.link(muxer);
+    Gst.GhostPad sink_pad = new Gst.GhostPad("sink", taginject.get_static_pad("sink"));
+    enc_bin.add_pad(sink_pad);
+    Gst.GhostPad src_pad = new Gst.GhostPad("src", muxer.get_static_pad("src"));
+    enc_bin.add_pad(src_pad);
+    return enc_bin;
+  }
+  
+  public static Gst.Bin get_encoder_nokiaaacenc(string name, string quality, string? taglist) {
+    uint bitrate = 128000;
+    int output_format = 1;
+    switch (quality) {
+      case "high":
+        bitrate = 256000;
+        break;
+      case "medium":
+        bitrate = 128000;
+        break;
+      case "low":
+        bitrate = 64000;
+        break;
+      default:
+        bitrate = 256000;
+        break;
+    }
+    Gst.Bin enc_bin = new Gst.Bin(name);
+    Gst.Element taginject = Gst.ElementFactory.make("taginject", "injector");
+    Gst.Element encoder = Gst.ElementFactory.make("nokiaaacenc", "encoder");
+    enc_bin.add(taginject);
+    enc_bin.add(encoder);
+    encoder.set_property("bitrate", bitrate);
+    encoder.set_property("output-format", output_format);
+    if (null != taglist) {
+      taginject.set_property("tags", taglist);
+    }
+    taginject.link(encoder);
+    Gst.GhostPad sink_pad = new Gst.GhostPad("sink", taginject.get_static_pad("sink"));
+    enc_bin.add_pad(sink_pad);
+    Gst.GhostPad src_pad = new Gst.GhostPad("src", encoder.get_static_pad("src"));
+    enc_bin.add_pad(src_pad);
+    return enc_bin;
+  }
+
+}
+
+}
\ No newline at end of file
diff --git a/src/EqualizerPopUp.vala b/src/EqualizerPopUp.vala
new file mode 100644 (file)
index 0000000..adba54b
--- /dev/null
@@ -0,0 +1,55 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+namespace IdWorks {
+  
+  public class EqualizerPopUp : Hildon.Dialog {
+    
+    GraphicEqualizer equalizer;
+    
+    public signal void eq_updated(int band, double val);
+    public signal void name_updated(string name);
+    
+    private void eq_updated_callback(GraphicEqualizer sender, int band, double val) {
+      eq_updated(band, val);
+    }
+    
+    private void name_updated_callback(GraphicEqualizer sender, string name) {
+      name_updated(name);
+    }
+    
+    public EqualizerPopUp(string title, Gtk.Widget parent) {
+      this.set_title(title);
+      this.set_parent(parent);
+      
+      this.set_default_response(Gtk.ResponseType.ACCEPT);
+      this.add_button(Gtk.STOCK_CLOSE, Gtk.ResponseType.ACCEPT);
+      
+      Gtk.VBox control_area = (Gtk.VBox)this.get_content_area();
+      
+      equalizer = new GraphicEqualizer();
+      equalizer.name_updated.connect(name_updated_callback);
+      equalizer.eq_updated.connect(eq_updated_callback);
+      
+      control_area.pack_start(equalizer, true, true , 2);
+      
+      this.show_all();
+    }
+    
+  }
+  
+}
\ No newline at end of file
diff --git a/src/EqualizerPreset.vala b/src/EqualizerPreset.vala
new file mode 100644 (file)
index 0000000..c4bb1b8
--- /dev/null
@@ -0,0 +1,114 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+namespace IdWorks {
+  
+  public struct EqualizerSettings {
+    public double[] bands;
+    public string name;
+    
+    public EqualizerSettings() {    
+      this.name = "";
+      this.bands = new double[] {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
+    }
+    public EqualizerSettings.with_name(string val) {    
+      this.name = val;
+      this.bands = new double[] {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
+    }
+    public EqualizerSettings.with_values(string name, double[] bands) {
+      this.name = name;
+      this.bands = bands;
+    }
+    
+    public string to_string() {
+      return "%s: bands:{%02f %02f %02f %02f %02f %02f %02f %02f %02f %02f}\n".printf(
+      name
+      , bands[0]
+      , bands[1]
+      , bands[2]
+      , bands[3]
+      , bands[4]
+      , bands[5]
+      , bands[6]
+      , bands[7]
+      , bands[8]
+      , bands[9]);
+    }
+    
+    public string serialize_to_string() {
+      return "EqualizerPreset:%s,%02f,%02f,%02f,%02f,%02f,%02f,%02f,%02f,%02f,%02f\n".printf(
+      name.replace(",", "\\,")
+      , bands[0]
+      , bands[1]
+      , bands[2]
+      , bands[3]
+      , bands[4]
+      , bands[5]
+      , bands[6]
+      , bands[7]
+      , bands[8]
+      , bands[9]);
+    }
+    public string to_xml_string() {
+      return ("<EqualizerSettings>\n" +
+              "<name><![CDATA[%s]]></name>\n" +
+              "<band_0 type=\"double\">>%02f</band_0>\n" +
+              "<band_1 type=\"double\">>%02f</band_1>\n" +
+              "<band_2 type=\"double\">>%02f</band_2>\n" +
+              "<band_3 type=\"double\">>%02f</band_3>\n" +
+              "<band_4 type=\"double\">>%02f</band_4>\n" +
+              "<band_5 type=\"double\">>%02f</band_5>\n" +
+              "<band_6 type=\"double\">>%02f</band_6>\n" +
+              "<band_7 type=\"double\">>%02f</band_7>\n" +
+              "<band_8 type=\"double\">>%02f</band_8>\n" +
+              "<band_9 type=\"double\">>%02f</band_9>\n" +
+              "</EqualizerSettings>\n").printf(
+              base.name,
+              base.bands[0],
+              base.bands[1],
+              base.bands[2],
+              base.bands[3],
+              base.bands[4],
+              base.bands[5],
+              base.bands[6],
+              base.bands[7],
+              base.bands[8],
+              base.bands[9]
+            );
+    }
+    public void from_xml_string(Xml.Node* node) {}
+  }
+  
+  public bool deserialize_from_string(string data) {
+    bool good = false;
+    // call parser to build an array of string of parts
+    string[] values = CdlParser.ParseLine(data);
+    // check length of array
+    if (13 == values.length) {
+      // parse parts into this
+      name = values[0];
+      for (int i = 0; i < 10; ++i) {
+        bands[i] = values[i + 1].to_double();
+      }
+      good = true;
+    }
+    return good;
+  }
+  
+}
+
+}
diff --git a/src/FirstRunWizard.vala b/src/FirstRunWizard.vala
new file mode 100644 (file)
index 0000000..93f03b2
--- /dev/null
@@ -0,0 +1,277 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+namespace IdWorks {
+
+public class FirstRunWizard : Hildon.WizardDialog {
+
+  public ApplicationSettings app_settings {public get; public set;}
+  Gtk.Notebook notebook;
+  Hildon.Button directory_btn;
+  Hildon.TouchSelector on_open_selector;
+  Hildon.TouchSelector on_close_selector;
+  Hildon.TouchSelector on_add_selector;
+  Hildon.TouchSelector on_bounce_selector;
+  Hildon.TouchSelector on_remove_selector;
+  Hildon.TouchSelector on_mixdown_selector;
+  private enum WizardPage {
+    WORKING_FOLDER = 0,
+    ON_OPEN,
+    ON_CLOSE,
+    ON_ADD,
+    ON_BOUNCE,
+    ON_REMOVE,
+    ON_MIXDOWN
+  }
+  
+  public FirstRunWizard(Gtk.Window parent, string wizard_name) {
+    app_settings = new ApplicationSettings();
+    this.set_parent(parent);
+    this.wizard_name = wizard_name;
+    this.autotitle = true;
+    this.wizard_notebook = get_notebook(app_settings);
+    //Hildon.WizardDialogPageFunc func_delegate = forward_page_function;
+    this.set_forward_page_func(forward_page_function);
+    this.show_all();
+  }
+  
+  private void on_open_updated(string val) {
+    this.app_settings.open_action = ApplicationSettings.OpenAction.from_label(val);
+  }
+  private void on_close_updated(string val) {
+    this.app_settings.close_action = ApplicationSettings.CloseAction.from_label(val);
+  }
+  private void on_add_updated(string val) {
+    this.app_settings.add_track_action = ApplicationSettings.AddTrackAction.from_label(val);
+  }
+  private void on_bounce_updated(string val) {
+    this.app_settings.bounce_tracks_action = ApplicationSettings.BounceTracksAction.from_label(val);
+  }
+  private void on_remove_updated(string val) {
+    this.app_settings.remove_track_action = ApplicationSettings.RemoveTrackAction.from_label(val);
+  }
+  private void on_mixdown_updated(string val) {
+    this.app_settings.mixdown_action = ApplicationSettings.MixdownAction.from_label(val);
+  }
+  
+  private Gtk.Notebook get_notebook(ApplicationSettings settings) {
+    notebook = new Gtk.Notebook();
+    
+    // page 1 - WORKING_FOLDER
+    Gtk.VBox page_1 = new Gtk.VBox(false, 4);
+    Gtk.Label msg_1 = new Gtk.Label("Pick a working directory, this is where all your projects will be stored.\n\nFor better performance try using an SD card.");
+    msg_1.set_line_wrap(true);
+    msg_1.set_alignment(0, 0);
+    directory_btn = new Hildon.Button.with_text(Hildon.SizeType.FINGER_HEIGHT | Hildon.SizeType.HALFSCREEN_WIDTH, Hildon.ButtonArrangement.VERTICAL, "Working folder", settings.working_directory);
+    directory_btn.set_style(Hildon.ButtonStyle.PICKER);
+    directory_btn.set_alignment(0, 0, 1, 1);
+    directory_btn.set_value(settings.working_directory);
+    directory_btn.clicked.connect(select_directory);
+    page_1.pack_start(directory_btn, false, false, 4);
+    page_1.add(msg_1);
+    
+    notebook.append_page(page_1, null);
+    
+    // page 2 - ON_OPEN
+    Gtk.VBox page_2 = new Gtk.VBox(false, 4);
+    Gtk.Label msg_2 = new Gtk.Label("What should happen at application start?\nIf you are unsure accept the defaults.");
+    msg_2.set_line_wrap(true);
+    msg_2.set_alignment(0, 0);
+    on_open_selector = new Hildon.TouchSelector.text();
+    on_open_selector.append_text(ApplicationSettings.OpenAction.to_label(ApplicationSettings.OpenAction.LAST_PROJECT));
+    on_open_selector.append_text(ApplicationSettings.OpenAction.to_label(ApplicationSettings.OpenAction.RECENT_PROJECTS));
+    on_open_selector.append_text(ApplicationSettings.OpenAction.to_label(ApplicationSettings.OpenAction.NEW_PROJECT));
+    on_open_selector.append_text(ApplicationSettings.OpenAction.to_label(ApplicationSettings.OpenAction.NAMED_PROJECT));
+    on_open_selector.set_active(0, (int)settings.open_action);
+    Hildon.PickerButton on_open_picker = new Hildon.PickerButton(Hildon.SizeType.FINGER_HEIGHT | Hildon.SizeType.HALFSCREEN_WIDTH, Hildon.ButtonArrangement.VERTICAL);
+    on_open_picker.set_title("Application start behaviour");
+    on_open_picker.set_selector(on_open_selector);
+    on_open_picker.set_alignment(0, 0, 1, 1);
+    on_open_picker.value_changed.connect( (s) => { on_open_updated(s.get_selector().get_current_text()); } );
+    page_2.pack_start(on_open_picker, false, false, 4);
+    page_2.add(msg_2);
+    
+    notebook.append_page(page_2, null);
+    
+    // page 3 - ON_CLOSE
+    Gtk.VBox page_3 = new Gtk.VBox(false, 4);
+    Gtk.Label msg_3 = new Gtk.Label("What should happen at application end?\nIf you are unsure accept the defaults.");
+    msg_3.set_line_wrap(true);
+    msg_3.set_alignment(0, 0);
+    on_close_selector = new Hildon.TouchSelector.text();
+    on_close_selector.append_text(ApplicationSettings.CloseAction.to_label(ApplicationSettings.CloseAction.AUTO_SAVE_PROJECT));
+    on_close_selector.append_text(ApplicationSettings.CloseAction.to_label(ApplicationSettings.CloseAction.PROMPT_SAVE_PROJECT));
+    on_close_selector.append_text(ApplicationSettings.CloseAction.to_label(ApplicationSettings.CloseAction.DO_NOTHING));
+    on_close_selector.set_active(0, (int)settings.close_action);
+    Hildon.PickerButton on_close_picker = new Hildon.PickerButton(Hildon.SizeType.FINGER_HEIGHT | Hildon.SizeType.HALFSCREEN_WIDTH, Hildon.ButtonArrangement.VERTICAL);
+    on_close_picker.set_title("Application end behaviour");
+    on_close_picker.set_selector(on_close_selector);
+    on_close_picker.set_alignment(0, 0, 1, 1);
+    on_close_picker.value_changed.connect( (s) => { on_close_updated(s.get_selector().get_current_text()); } );
+    page_3.pack_start(on_close_picker, false, false, 4);
+    page_3.add(msg_3);
+    
+    notebook.append_page(page_3, null);
+    
+    // page 4 - ON_ADD
+    Gtk.VBox page_4 = new Gtk.VBox(false, 4);
+    Gtk.Label msg_4 = new Gtk.Label("What should happen when you add a track?\nIf you are unsure accept the defaults.");
+    msg_4.set_line_wrap(true);
+    msg_4.set_alignment(0, 0);
+    on_add_selector = new Hildon.TouchSelector.text();
+    on_add_selector.append_text(ApplicationSettings.AddTrackAction.to_label(ApplicationSettings.AddTrackAction.SET_ACTIVE));
+    on_add_selector.append_text(ApplicationSettings.AddTrackAction.to_label(ApplicationSettings.AddTrackAction.SET_SOLO));
+    on_add_selector.append_text(ApplicationSettings.AddTrackAction.to_label(ApplicationSettings.AddTrackAction.SET_INACTIVE));
+    on_add_selector.append_text(ApplicationSettings.AddTrackAction.to_label(ApplicationSettings.AddTrackAction.SET_MUTE));
+    on_add_selector.set_active(0, (int)settings.add_track_action);
+    Hildon.PickerButton on_add_picker = new Hildon.PickerButton(Hildon.SizeType.FINGER_HEIGHT | Hildon.SizeType.HALFSCREEN_WIDTH, Hildon.ButtonArrangement.VERTICAL);
+    on_add_picker.set_title("Add track behaviour");
+    on_add_picker.set_selector(on_add_selector);
+    on_add_picker.set_alignment(0, 0, 1, 1);
+    on_add_picker.value_changed.connect( (s) => { on_add_updated(s.get_selector().get_current_text()); } );
+    page_4.pack_start(on_add_picker, false, false, 4);
+    page_4.add(msg_4);
+    
+    notebook.append_page(page_4, null);
+    
+    // page 5 - ON_BOUNCE
+    Gtk.VBox page_5 = new Gtk.VBox(false, 4);
+    Gtk.Label msg_5 = new Gtk.Label("What should happen when you bounce tracks?\nIf you are unsure accept the defaults.");
+    msg_5.set_line_wrap(true);
+    msg_5.set_alignment(0, 0);
+    on_bounce_selector = new Hildon.TouchSelector.text();
+    on_bounce_selector.append_text(ApplicationSettings.BounceTracksAction.to_label(ApplicationSettings.BounceTracksAction.SET_SOURCE_INACTIVE));
+    on_bounce_selector.append_text(ApplicationSettings.BounceTracksAction.to_label(ApplicationSettings.BounceTracksAction.SET_SOURCE_MUTE));
+    on_bounce_selector.append_text(ApplicationSettings.BounceTracksAction.to_label(ApplicationSettings.BounceTracksAction.SET_TARGET_SOLO));
+    on_bounce_selector.append_text(ApplicationSettings.BounceTracksAction.to_label(ApplicationSettings.BounceTracksAction.SET_TARGET_INACTIVE));
+    on_bounce_selector.append_text(ApplicationSettings.BounceTracksAction.to_label(ApplicationSettings.BounceTracksAction.SET_TARGET_MUTE));
+    on_bounce_selector.set_active(0, (int)settings.bounce_tracks_action);
+    Hildon.PickerButton on_bounce_picker = new Hildon.PickerButton(Hildon.SizeType.FINGER_HEIGHT | Hildon.SizeType.HALFSCREEN_WIDTH, Hildon.ButtonArrangement.VERTICAL);
+    on_bounce_picker.set_title("Bounce tracks behaviour");
+    on_bounce_picker.set_selector(on_bounce_selector);
+    on_bounce_picker.set_alignment(0, 0, 1, 1);
+    on_bounce_picker.value_changed.connect( (s) => { on_bounce_updated(s.get_selector().get_current_text()); } );
+    page_5.pack_start(on_bounce_picker, false, false, 4);
+    page_5.add(msg_5);
+    
+    notebook.append_page(page_5, null);
+    
+    // page 6 - ON_REMOVE
+    Gtk.VBox page_6 = new Gtk.VBox(false, 4);
+    Gtk.Label msg_6 = new Gtk.Label("What should happen when you remove a track?\nIf you are unsure accept the defaults.");
+    msg_6.set_line_wrap(true);
+    msg_6.set_alignment(0, 0);
+    on_remove_selector = new Hildon.TouchSelector.text();
+    on_remove_selector.append_text(ApplicationSettings.RemoveTrackAction.to_label(ApplicationSettings.RemoveTrackAction.PROMPT));
+    on_remove_selector.append_text(ApplicationSettings.RemoveTrackAction.to_label(ApplicationSettings.RemoveTrackAction.DELETE_FILE));
+    on_remove_selector.append_text(ApplicationSettings.RemoveTrackAction.to_label(ApplicationSettings.RemoveTrackAction.PRESERVE_FILE));
+    on_remove_selector.set_active(0, (int)settings.remove_track_action);
+    Hildon.PickerButton on_remove_picker = new Hildon.PickerButton(Hildon.SizeType.FINGER_HEIGHT | Hildon.SizeType.HALFSCREEN_WIDTH, Hildon.ButtonArrangement.VERTICAL);
+    on_remove_picker.set_title("Remove tracks behaviour");
+    on_remove_picker.set_selector(on_remove_selector);
+    on_remove_picker.set_alignment(0, 0, 1, 1);
+    on_remove_picker.value_changed.connect( (s) => { on_remove_updated(s.get_selector().get_current_text()); } );
+    page_6.pack_start(on_remove_picker, false, false, 4);
+    page_6.add(msg_6);
+    
+    notebook.append_page(page_6, null);
+    
+    // page 7 - ON_MIXDOWN
+    Gtk.VBox page_7 = new Gtk.VBox(false, 4);
+    Gtk.Label msg_7 = new Gtk.Label("What should happen when you mixdown your tracks?\nIf you are unsure accept the defaults.");
+    msg_7.set_line_wrap(true);
+    msg_7.set_alignment(0, 0);
+    on_mixdown_selector = new Hildon.TouchSelector.text();
+    on_mixdown_selector.append_text(ApplicationSettings.MixdownAction.to_label(ApplicationSettings.MixdownAction.NONE));
+    on_mixdown_selector.append_text(ApplicationSettings.MixdownAction.to_label(ApplicationSettings.MixdownAction.PLAY_TARGET));
+    on_mixdown_selector.set_active(0, (int)settings.mixdown_action);
+    Hildon.PickerButton on_mixdown_picker = new Hildon.PickerButton(Hildon.SizeType.FINGER_HEIGHT | Hildon.SizeType.HALFSCREEN_WIDTH, Hildon.ButtonArrangement.VERTICAL);
+    on_mixdown_picker.set_title("Mixdown behaviour");
+    on_mixdown_picker.set_selector(on_mixdown_selector);
+    on_mixdown_picker.set_alignment(0, 0, 1, 1);
+    on_mixdown_picker.value_changed.connect( (s) => { on_mixdown_updated(s.get_selector().get_current_text()); } );
+    page_7.pack_start(on_mixdown_picker, false, false, 4);
+    page_7.add(msg_7);
+    
+    notebook.append_page(page_7, null);
+    
+    // page 8 - Thank You
+    Gtk.VBox page_8 = new Gtk.VBox(false, 4);
+    Gtk.Label msg_8 = new Gtk.Label("That's all for now. \n\nYou can change these settings at any time from the settings menu option.\n\nEnjoy!");
+    msg_8.set_line_wrap(true);
+    msg_8.set_alignment(0, 0);
+    page_8.add(msg_8);
+    
+    notebook.append_page(page_8, null);
+    notebook.show_all();
+    
+    return notebook;
+  }
+  
+  public bool forward_page_function(Gtk.Notebook notebook, int current_page) {
+    bool good = false;
+    // current page is page about to be left!
+    switch (current_page) {
+      case WizardPage.WORKING_FOLDER:
+        good = true;
+        break;
+      case WizardPage.ON_OPEN:
+        this.directory_btn.value = this.app_settings.working_directory;
+        good = true;
+        break;
+      case WizardPage.ON_CLOSE:
+        good = true;
+        break;
+      case WizardPage.ON_ADD:
+        good = true;
+        break;
+      case WizardPage.ON_BOUNCE:
+        good = true;
+        break;
+      case WizardPage.ON_REMOVE:
+        good = true;
+        break;
+      case WizardPage.ON_MIXDOWN:
+        good = true;
+        break;
+      default:
+        this.directory_btn.value = this.app_settings.working_directory;
+        good = true;
+        break;
+    }
+    return good;
+  }
+  
+  private void select_directory() {
+    // get a filename
+    Hildon.FileChooserDialog file_chooser = new Hildon.FileChooserDialog(this, Gtk.FileChooserAction.SELECT_FOLDER);
+    file_chooser.set_show_upnp(false);
+    Gtk.ResponseType ret = (Gtk.ResponseType) file_chooser.run();
+    if (Gtk.ResponseType.OK == ret) {
+      this.directory_btn.value = file_chooser.get_filename();
+      this.app_settings.working_directory = file_chooser.get_filename();
+    }
+    file_chooser.destroy ();
+  }
+  
+  /*public void destroy_notify(ApplicationSettings* data) {
+    /// TODO
+  }*/
+
+}
+
+}
diff --git a/src/GraphicEqualizer.vala b/src/GraphicEqualizer.vala
new file mode 100644 (file)
index 0000000..633a22d
--- /dev/null
@@ -0,0 +1,304 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+using Gtk;
+
+namespace IdWorks {
+  
+  public class GraphicEqualizer : Gtk.VBox {
+    
+    SettingsStructures.EqualizerSettings preset;
+    SettingsStructures.EqualizerSettings preset_prev;
+    SimpleHashMap<SettingsStructures.EqualizerSettings?> presets;
+    VScale band0_slider;
+    VScale band1_slider;
+    VScale band2_slider;
+    VScale band3_slider;
+    VScale band4_slider;
+    VScale band5_slider;
+    VScale band6_slider;
+    VScale band7_slider;
+    VScale band8_slider;
+    VScale band9_slider;
+    Label name_label;
+    Hildon.TouchSelector eq_presets;
+    
+    
+    public signal void eq_updated (int band, double val);
+    public signal void name_updated (string val);
+    
+    public GraphicEqualizer() {
+      preset_prev = SettingsStructures.EqualizerSettings();
+      preset = SettingsStructures.EqualizerSettings();
+      preset.bands = new double[] {0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0};
+      preset.name = "Default";
+      init();
+    }
+    
+    public GraphicEqualizer.with_preset(SettingsStructures.EqualizerSettings gep)
+    requires (null != gep.bands) {
+      preset_prev.bands = null;
+      preset = gep;
+      init();
+    }
+    
+    public GraphicEqualizer.with_values(double[] bands, string name)
+    requires (bands.length == 10)
+    requires (null != name) {
+      preset_prev.bands = null;
+      //preset = new SettingsStructures.EqualizerSettings();
+      preset.bands = bands;
+      preset.name = name;
+      init();
+    }
+    
+    private void init() {
+      load_presets();
+      construct_interface();    
+    }
+    
+    private void load_presets() {
+      presets = new SimpleHashMap<SettingsStructures.EqualizerSettings?>();
+      //SettingsStructures.EqualizerSettings pst = new SettingsStructures.EqualizerSettings();
+      // do hardcoded ones.
+      //pst.name = "Default";
+      //pst.bands = new double[] {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
+      presets.set("Default", SettingsStructures.EqualizerSettings.with_values("Default", new double[] {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}));
+      presets.set("Bass Boost", SettingsStructures.EqualizerSettings.with_values("Bass Boost", new double[] {-8.0, 9.6, 9.6, 5.6, 1.6, -4.0, -8.0, -10.4, -11.2, -11.2}));
+      presets.set("Classical", SettingsStructures.EqualizerSettings.with_values("Classical", new double[] {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -7.2, -7.2, -7.2, -9.6}));
+      presets.set("Club", SettingsStructures.EqualizerSettings.with_values("Club", new double[] {0.0, 0.0, 8.0, 5.6, 5.6, 5.6, 3.2, 0.0, 0.0, 0.0}));
+      presets.set("Dance", SettingsStructures.EqualizerSettings.with_values("Dance", new double[] {9.6, 7.2, 2.4, 0.0, 0.0, -5.6, -7.2, -7.2, 0.0, 0.0}));
+      presets.set("Flat Response", SettingsStructures.EqualizerSettings.with_values("Flat Response", new double[] {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}));
+      presets.set("Folk / Acoustic", SettingsStructures.EqualizerSettings.with_values("Folk / Acoustic", new double[] {0.0, 2.0, 4.0, 2.0, 0.0, 0.0, 0.0, -2.0, -4.0, -6.0}));
+      presets.set("Live", SettingsStructures.EqualizerSettings.with_values("Live", new double[] {-4.8, 0.0, 4.0, 5.6, 5.6, 5.6, 4.0, 2.4, 2.4, 2.4}));
+      presets.set("Pop", SettingsStructures.EqualizerSettings.with_values("Pop", new double[] {-1.6, 4.8, 7.2, 8.0, 5.6, 0.0, -2.4, -2.4, -1.6, -1.6}));
+      presets.set("Rock", SettingsStructures.EqualizerSettings.with_values("Rock", new double[] {8.0, 4.8, -5.6, -8.0, -3.2, 4.0, 8.8, 11.2, 11.2, 11.2}));
+      presets.set("Soft Rock", SettingsStructures.EqualizerSettings.with_values("Soft Rock", new double[] {4.0, 4.0, 2.4, 0.0, -4.0, -5.6, -3.2, 0.0, 2.4, 8.8}));
+      presets.set("Spoken Word", SettingsStructures.EqualizerSettings.with_values("Spoken Word", new double[] {-4.0, -2.0, 0.0, 2.0, 4.0, 4.0, 2.0, 0.0, -2.0, -4.0}));
+      // TODO - Load users settings from storage
+    }
+    
+    private void save_eq_settings() {
+      // TODO
+      string name = "";
+      // prompt for name
+      Dialog prompt = new Dialog.with_buttons("Save EQ Settings", null, DialogFlags.MODAL | DialogFlags.DESTROY_WITH_PARENT, STOCK_OK, ResponseType.ACCEPT);
+      var dialog_area = (VBox)prompt.get_content_area();
+      var label_prompt = new Label("Enter a name for this Preset.");
+      dialog_area.pack_start(label_prompt, false, false, 4);
+      label_prompt.show();
+      var text_prompt = new Entry();
+      dialog_area.pack_start(text_prompt, false, false, 4);
+      text_prompt.show();
+      if (ResponseType.ACCEPT == prompt.run()) {
+        name = text_prompt.get_text();
+        if (name == "") name = "Unnamed";
+        set_name(name);
+        // For now temporarily add it to selector
+        // need to check to see if we are doing an overwrite I guess!
+        bool found = false;
+        foreach (var k in presets.keys) found = found || (k == name);
+        if (!found) eq_presets.append_text(preset.name);
+        // this will update if it exists or create new if not
+        presets.set(preset.name, preset);
+        /*stdout.printf("Presets contains:\n");
+        foreach (var p in presets.values)
+        {
+          string tmp = p.serialize_to_string();
+          stdout.printf("%s\n", tmp);
+          if (p.deserialize_from_string(tmp)) {
+            stdout.printf("%s\n", p.serialize_to_string());
+          }
+        }*/
+      }
+      prompt.destroy();
+      // add to presets    
+    }
+    
+    private void set_eq_preset(string name) {
+      preset_prev = preset;
+      preset = get_eq_preset(name);
+      for (int i = 0; i < preset.bands.length; ++i) {
+        set_eq(i, preset.bands[i]);
+      }
+      set_name(preset.name);
+    }
+    
+    private SettingsStructures.EqualizerSettings get_eq_preset(string name) {
+      return presets.get(name);
+    }
+    
+    private void construct_interface() {
+      //var vbox = new VBox(false, 4);
+      this.set_size_request(600, 400);
+      
+      var eq_label_hbox = new HBox(false, 2);
+      
+      eq_presets = new Hildon.TouchSelector.text();
+      foreach (var p in presets.values) {
+        eq_presets.append_text(p.name);
+      }
+      //eq_presets.append_text("Default");
+      //eq_presets.append_text("Flat Response");
+      //eq_presets.append_text("Folk / Acoustic");
+      //eq_presets.append_text("Spoken Word");
+      //eq_presets.append_text("Bass Boost");
+      
+      Hildon.PickerButton eq_presets_picker = new Hildon.PickerButton(Hildon.SizeType.FINGER_HEIGHT, Hildon.ButtonArrangement.VERTICAL);
+      eq_presets_picker.set_title("Slider presets:");
+      eq_presets_picker.set_selector(eq_presets);
+      eq_presets_picker.value_changed.connect( (s) => { set_eq_preset(s.get_selector().get_current_text()); } );
+      eq_label_hbox.pack_start(eq_presets_picker, false, false, 2);
+      
+      var eq_save_btn = new Button.with_label("Save settings");
+      eq_save_btn.clicked.connect(save_eq_settings);
+      eq_label_hbox.pack_start(eq_save_btn, false, false, 2);
+      
+      this.pack_start(eq_label_hbox, false, false, 2);
+      
+      name_label = new Label(preset.name);
+      eq_label_hbox.pack_end(name_label, false, false, 2);
+      
+      
+      var eq_hbox = new HBox(true, 2);
+      
+      band0_slider = new VScale.with_range(-24, 12, 0.5);
+      band0_slider.set_inverted(true);
+      band0_slider.set_value_pos(PositionType.BOTTOM);
+      band0_slider.set_value(preset.bands[0]);
+      band0_slider.value_changed.connect( (s) => { preset.bands[0] = s.get_value(); eq_updated(0, s.get_value()); } );
+      eq_hbox.pack_start(band0_slider, true, true, 2);
+      band1_slider = new VScale.with_range(-24, 12, 0.5);
+      band1_slider.set_inverted(true);
+      band1_slider.set_value_pos(PositionType.BOTTOM);
+      band1_slider.set_value(preset.bands[1]);
+      band1_slider.value_changed.connect( (s) => { preset.bands[1] = s.get_value(); eq_updated(1, s.get_value()); } );
+      eq_hbox.pack_start(band1_slider, true, true, 2);
+      band2_slider = new VScale.with_range(-24, 12, 0.5);
+      band2_slider.set_inverted(true);
+      band2_slider.set_value_pos(PositionType.BOTTOM);
+      band2_slider.set_value(preset.bands[2]);
+      band2_slider.value_changed.connect( (s) => { preset.bands[2] = s.get_value(); eq_updated(2, s.get_value()); } );
+      eq_hbox.pack_start(band2_slider, true, true, 2);
+      band3_slider = new VScale.with_range(-24, 12, 0.5);
+      band3_slider.set_inverted(true);
+      band3_slider.set_value_pos(PositionType.BOTTOM);
+      band3_slider.set_value(preset.bands[3]);
+      band3_slider.value_changed.connect( (s) => { preset.bands[3] = s.get_value(); eq_updated(3, s.get_value()); } );
+      eq_hbox.pack_start(band3_slider, true, true, 2);
+      band4_slider = new VScale.with_range(-24, 12, 0.5);
+      band4_slider.set_inverted(true);
+      band4_slider.set_value_pos(PositionType.BOTTOM);
+      band4_slider.set_value(preset.bands[4]);
+      band4_slider.value_changed.connect( (s) => { preset.bands[4] = s.get_value(); eq_updated(4, s.get_value()); } );
+      eq_hbox.pack_start(band4_slider, true, true, 2);
+      band5_slider = new VScale.with_range(-24, 12, 0.5);
+      band5_slider.set_inverted(true);
+      band5_slider.set_value_pos(PositionType.BOTTOM);
+      band5_slider.set_value(preset.bands[5]);
+      band5_slider.value_changed.connect( (s) => { preset.bands[5] = s.get_value(); eq_updated(5, s.get_value()); } );
+      eq_hbox.pack_start(band5_slider, true, true, 2);
+      band6_slider = new VScale.with_range(-24, 12, 0.5);
+      band6_slider.set_inverted(true);
+      band6_slider.set_value_pos(PositionType.BOTTOM);
+      band6_slider.set_value(preset.bands[6]);
+      band6_slider.value_changed.connect( (s) => { preset.bands[6] = s.get_value(); eq_updated(6, s.get_value()); } );
+      eq_hbox.pack_start(band6_slider, true, true, 2);
+      band7_slider = new VScale.with_range(-24, 12, 0.5);
+      band7_slider.set_inverted(true);
+      band7_slider.set_value_pos(PositionType.BOTTOM);
+      band7_slider.set_value(preset.bands[7]);
+      band7_slider.value_changed.connect( (s) => { preset.bands[7] = s.get_value(); eq_updated(7, s.get_value()); } );
+      eq_hbox.pack_start(band7_slider, true, true, 2);
+      band8_slider = new VScale.with_range(-24, 12, 0.5);
+      band8_slider.set_inverted(true);
+      band8_slider.set_value_pos(PositionType.BOTTOM);
+      band8_slider.set_value(preset.bands[8]);
+      band8_slider.value_changed.connect( (s) => { preset.bands[8] = s.get_value(); eq_updated(8, s.get_value()); } );
+      eq_hbox.pack_start(band8_slider, true, true, 2);
+      band9_slider = new VScale.with_range(-24, 12, 0.5);
+      band9_slider.set_inverted(true);
+      band9_slider.set_value_pos(PositionType.BOTTOM);
+      band9_slider.set_value(preset.bands[9]);
+      band9_slider.value_changed.connect( (s) => { preset.bands[9] = s.get_value(); eq_updated(9, s.get_value()); } );
+      eq_hbox.pack_start(band9_slider, true, true, 2);
+      
+      this.pack_start(eq_hbox, true, true, 0);
+      
+      //this.add(vbox);
+      
+      //this.show_all();
+    }
+    
+    public double get_eq(int band) {
+      return preset.bands[band];
+    }
+    
+    public void set_eq (int band, double val)
+    requires (band > -1 && band < 10)
+    requires (val >= -24.0 && val <= 12.0) {
+      switch (band) {
+        case 0:
+          band0_slider.set_value (val);
+          break;
+        case 1:
+          band1_slider.set_value (val);
+          break;
+        case 2:
+          band2_slider.set_value (val);
+          break;
+        case 3:
+          band3_slider.set_value (val);
+          break;
+        case 4:
+          band4_slider.set_value (val);
+          break;
+        case 5:
+          band5_slider.set_value (val);
+          break;
+        case 6:
+          band6_slider.set_value (val);
+          break;
+        case 7:
+          band7_slider.set_value (val);
+          break;
+        case 8:
+          band8_slider.set_value (val);
+          break;
+        case 9:
+          band9_slider.set_value (val);
+          break;
+        default:
+          break;
+      }
+      preset.bands[band] = val;
+      eq_updated (band, val);
+    }
+    
+    public void set_name(string val) {
+      name_label.set_text(val);
+      name_updated(val);
+      preset.name = val;
+    }
+    
+    public string get_name() {
+      return preset.name;
+    }  
+    
+  }
+  
+}
diff --git a/src/IXmlSerializable.vala b/src/IXmlSerializable.vala
new file mode 100644 (file)
index 0000000..9b6168c
--- /dev/null
@@ -0,0 +1,26 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+namespace IdWorks {
+
+public interface IXmlSerializable {
+  public abstract string to_xml_string();
+  public abstract void from_xml_node(Xml.Node* node);
+  public abstract Xml.Node* to_xml();
+}
+
+}
\ No newline at end of file
diff --git a/src/ImportBin.vala b/src/ImportBin.vala
new file mode 100644 (file)
index 0000000..f93f177
--- /dev/null
@@ -0,0 +1,107 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+namespace IdWorks {
+
+public class ImportBin : Gst.Bin {
+  
+  Gst.Element decoder;
+  Gst.Element converter;
+  //Gst.Element queue;
+  Gst.GhostPad sink_pad;
+  Gst.GhostPad src_pad;
+  
+  public ImportBin(string name) {
+    this.set_name(name);
+    
+    decoder = Gst.ElementFactory.make("uridecodebin", "decoder-" + name);
+    decoder.connect("swapped-object-signal::autoplug-continue", autoplug_continue, this);
+    decoder.connect("swapped-object-signal::drained", drained, this);
+    this.add(decoder);
+    
+    converter = Gst.ElementFactory.make("audioconvert", "converter-" + name);
+    this.add(converter);
+    
+    //queue = Gst.ElementFactory.make("queue2", "queue");
+    //this.add(queue);
+    
+    //this.converter.link(this.queue);
+    
+    
+    sink_pad = new Gst.GhostPad("sink", decoder.get_static_pad("sink"));
+    this.add_pad(sink_pad);
+    src_pad = new Gst.GhostPad("src", converter.get_static_pad("src"));
+    this.add_pad(src_pad);
+    
+  }
+  
+  private bool autoplug_continue(Gst.Pad decodebin, Gst.Caps arg1, void* data) {
+    stdout.printf("autoplug continue called: %s\n", this.get_name());
+    stdout.flush();
+    Gst.PadLinkReturn ret = decodebin.link(converter.get_static_pad("sink"));
+    if (Gst.PadLinkReturn.OK != ret) {
+      stdout.printf("link failed: %s %s\n", this.get_name(), ret.to_string());
+      stdout.flush();
+    }
+    else {
+      stdout.printf("link succeded: %s\n", this.get_name());
+      stdout.flush();
+    }
+    return true;
+  }
+  
+  private void drained(Gst.Pad decodebin, void* data) {
+  }
+  
+  public new Gst.PadTemplate get_pad_template(string name) {
+    switch (name) {
+      case "src":
+        return this.converter.get_pad_template("src");
+      case "sink":
+        return this.decoder.get_pad_template("sink");
+      default:
+        return this.get_pad_template(name);
+    }
+  }
+  
+  public void play() {
+    this.set_state(Gst.State.PLAYING);
+  }
+  
+  public void pause() {
+    this.set_state(Gst.State.PAUSED);
+  }
+  
+  public void stop() {
+    this.set_state(Gst.State.READY);
+  }
+  
+  public void set_uri(string uri) {
+    stdout.printf("set_uri: %s\n", uri);
+    stdout.flush();
+    this.set_state(Gst.State.NULL);
+    decoder.set_property("uri", uri);
+  }
+  public string get_uri() {
+    GLib.Value ret = 0.0;
+    decoder.get_property("uri", ref ret);
+    return ret.get_string();
+  }
+
+}
+
+}
\ No newline at end of file
diff --git a/src/InputBin.vala b/src/InputBin.vala
new file mode 100644 (file)
index 0000000..c938362
--- /dev/null
@@ -0,0 +1,177 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+namespace IdWorks {
+
+public class InputBin : Gst.Bin {
+  
+  Gst.Element src;
+  Gst.Element converter;
+  Gst.Element pregain;
+  Gst.Element dynamics;
+  Gst.Element volume;
+  Gst.GhostPad src_pad;
+  Gst.GhostPad sink_pad;
+  
+  public InputBin(string name) {
+    this.set_name(name);
+    construct_pipeline(SettingsStructures.RecordingSettings());
+  }  
+  public InputBin.with_settings(string name, SettingsStructures.RecordingSettings settings) {
+    this.name = name;
+    construct_pipeline(settings);
+  }
+  
+  private string get_source_device(string name) {
+    switch (name) {
+      case "Microphone":
+        return "source.hw0";
+      case "Monitor":
+        return "sink.hw0.monitor";
+      case "Bluetooth":
+        return "source.hw1";
+      default:
+        return "source.hw0";
+    }
+  }
+  private string get_source_name(string device) {
+    switch (device) {
+      case "source.hw0":
+        return "Microphone";
+      case "sink.hw0.monitor":
+        return "Monitor";
+      case "source.hw1":
+        return "Bluetooth";
+      default:
+        return "Microphone";
+    }
+  }
+  
+  private void construct_pipeline(SettingsStructures.RecordingSettings settings) {
+    
+    this.src = Gst.ElementFactory.make("pulsesrc", "capture");
+    this.src.set_property("device", get_source_device(settings.source)); //"source.hw0"); /// TODO
+    this.add(this.src);
+    
+    this.converter = Gst.ElementFactory.make("audioconvert", "converter");
+    this.add(this.converter);
+    
+    this.pregain = Gst.ElementFactory.make("volume", "pregain");
+    this.pregain.set_property("volume", settings.pregain);
+    this.add(this.pregain);
+    
+    this.dynamics = Gst.ElementFactory.make("audiodynamic", "dynamics");
+    this.add(this.dynamics);
+    this.dynamics.set_property("mode", settings.dynamics.mode); // compressor or expander
+    this.dynamics.set_property("characteristics", settings.dynamics.characteristics); // hard-knee or soft-knee
+    this.dynamics.set_property("threshold", settings.dynamics.threshold); // [0, 1]
+    this.dynamics.set_property("ratio", settings.dynamics.ratio); // >= 0
+    
+    this.volume = Gst.ElementFactory.make("volume", "volume");
+    this.volume.set_property("volume", settings.volume);
+    this.add(this.volume);
+    
+    this.src.link(converter);
+    this.converter.link(pregain);
+    this.pregain.link(dynamics);
+    this.dynamics.link(volume);
+    
+    sink_pad = new Gst.GhostPad("sink", src.get_static_pad("sink"));
+    this.add_pad(sink_pad);
+    src_pad = new Gst.GhostPad("src", volume.get_static_pad("src"));
+    this.add_pad(src_pad);
+  }
+  
+  public new Gst.PadTemplate get_pad_template(string name) {
+    switch (name) {
+      case "src":
+        return this.volume.get_pad_template("src");
+      case "sink":
+        return this.src.get_pad_template("sink");
+      default:
+        return base.get_pad_template(name);
+    }
+  }
+  
+  public void set_dynamics(string mode, string characteristics, double threshold, double ratio) {
+    this.dynamics.set_property("mode", mode); // compressor or expander
+    this.dynamics.set_property("characteristics", characteristics); // hard-knee or soft-knee
+    this.dynamics.set_property("threshold", threshold); // [0, 1]
+    this.dynamics.set_property("ratio", ratio); // >= 0
+  }
+  public void set_dynamics_mode(string mode) {
+    this.dynamics.set_property("mode", mode); // compressor or expander
+  }
+  public void set_dynamics_characteristics(string characteristics) {
+    this.dynamics.set_property("characteristics", characteristics); // hard-knee or soft-knee
+  }
+  public void set_dynamics_threshold(double threshold) {
+    this.dynamics.set_property("threshold", threshold); // [0, 1]
+  }
+  public void set_dynamics_ratio(double ratio) {
+    this.dynamics.set_property("ratio", ratio); // >= 0
+  }
+  
+  public void set_source(string name) {
+    Gst.State state;
+    Gst.ClockTime time = Gst.util_get_timestamp ();
+    this.get_state (out state, null, time);
+    this.set_state(Gst.State.NULL);
+    this.src.set_property("device", get_source_device(name));
+    this.set_state(state);
+  }
+  public string get_source() {
+    GLib.Value val = 0;
+    this.src.get_property("device", ref val);
+    return get_source_name(val.get_string());
+  }
+  
+  public void set_pregain(double val)
+  requires (val >= 0.0 && val <= 10.0) {
+    this.pregain.set_property("volume", val);
+  }
+  public double get_pregain()
+  ensures (result >= 0.0 && result <= 10.0) {
+    GLib.Value val = 0;
+    this.pregain.get_property("volume", ref val);
+    return val.get_double();
+  }
+  
+  public void set_volume(double val)
+  requires (val >= 0.0 && val <= 10.0) {
+    this.volume.set_property("volume", val);
+  }
+  public double get_volume()
+  ensures (result >= 0.0 && result <= 10.0) {
+    GLib.Value val = 0;
+    this.volume.get_property("volume", ref val);
+    return val.get_double();
+  }
+  
+  public void set_mute(bool val) {
+    this.volume.set_property("mute", val);
+  }
+  public bool get_mute() {
+    GLib.Value val = 0;
+    this.volume.get_property("mute", ref val);
+    return val.get_boolean();
+  }
+  
+
+}
+
+}
\ No newline at end of file
diff --git a/src/MixdownPopUp.vala b/src/MixdownPopUp.vala
new file mode 100644 (file)
index 0000000..4e3d8ce
--- /dev/null
@@ -0,0 +1,217 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+namespace IdWorks {
+
+public class MixdownPopUp : Hildon.Dialog {
+  
+  Hildon.TouchSelector encoder_selector;
+  Hildon.TouchSelector quality_selector;
+  Hildon.Entry title_entry;
+  Hildon.Entry artist_entry;
+  Hildon.Entry album_entry;
+  //Hildon.Entry description_entry;
+  Hildon.Button location_btn;
+  
+  public SettingsStructures.MixdownSettings get_settings_structure() {
+    SettingsStructures.MixdownSettings settings = SettingsStructures.MixdownSettings();
+    settings.location = location_btn.get_value();
+    settings.encoder = encoder_selector.get_current_text();
+    settings.quality = quality_selector.get_current_text();
+    settings.title = title_entry.get_text();
+    settings.album = album_entry.get_text();
+    settings.artist = artist_entry.get_text();
+    return settings;
+  }
+  public void set_settings_structure(SettingsStructures.MixdownSettings settings) {
+    location_btn.set_value(settings.location);
+    this.set_encoder(settings.encoder);
+    this.set_quality(settings.quality);
+    title_entry.set_text(settings.title);
+    album_entry.set_text(settings.album);
+    artist_entry.set_text(settings.artist);
+  }
+  
+  private void set_encoder(string encoder) {
+    int idx = 0;
+    switch (encoder) {
+      case "vorbisenc":
+        idx = 0;
+        break;
+      case "wavenc":
+        idx = 1;
+        break;
+      case "nokiaaacenc":
+        idx = 2;
+        break;
+      case "mpegaudio":
+        idx = 3;
+        break;
+      case "lame":
+        idx = 4;
+        break;
+      default:
+        idx = 0;
+        break;
+    }
+    encoder_selector.set_active(0, idx);
+  }
+  
+  private void set_quality(string quality) {
+    int idx = 0;
+    switch (quality) {
+      case "high":
+        idx = 0;
+        break;
+      case "medium":
+        idx = 1;
+        break;
+      case "low":
+        idx = 2;
+        break;
+      default:
+        idx = 0;
+        break;
+    }
+    quality_selector.set_active(0, idx);
+  }
+  
+  public MixdownPopUp(string title, Gtk.Widget parent) {
+    this.set_title(title);
+    this.set_parent(parent);    
+    construct_interface(SettingsStructures.MixdownSettings());
+  }
+  public MixdownPopUp.with_settings(string title, Gtk.Widget parent, SettingsStructures.MixdownSettings settings) {
+    this.set_title(title);
+    this.set_parent(parent);
+    construct_interface(settings);
+  }
+  
+  private void construct_interface(SettingsStructures.MixdownSettings settings) {
+    this.set_default_response(Gtk.ResponseType.CANCEL);
+    this.add_button(Gtk.STOCK_OK, Gtk.ResponseType.OK);
+    this.add_button(Gtk.STOCK_CLOSE, Gtk.ResponseType.CANCEL);
+    
+    Gtk.VBox control_area = (Gtk.VBox)this.get_content_area();
+    
+    // location
+    location_btn = new Hildon.Button.with_text(Hildon.SizeType.FINGER_HEIGHT | Hildon.SizeType.HALFSCREEN_WIDTH, Hildon.ButtonArrangement.VERTICAL, "Location", settings.location);
+    location_btn.set_style(Hildon.ButtonStyle.PICKER);
+    location_btn.set_alignment(0, 0, 1, 1);
+    location_btn.clicked.connect(select_location);
+    control_area.pack_start(location_btn, true, true, 2);
+    
+    Gtk.HButtonBox button_bar = new Gtk.HButtonBox();
+    button_bar.set_layout(Gtk.ButtonBoxStyle.START);
+    button_bar.set_homogeneous(false);
+    button_bar.set_spacing(4);
+    
+    // encoder
+    encoder_selector = new Hildon.TouchSelector.text();
+    encoder_selector.append_text("vorbisenc");
+    encoder_selector.append_text("wavenc");
+    encoder_selector.append_text("nokiaaacenc");
+    encoder_selector.append_text("mpegaudio");
+    encoder_selector.append_text("lame");
+    this.set_encoder(settings.encoder);
+    Hildon.PickerButton encoder_picker = new Hildon.PickerButton(Hildon.SizeType.FINGER_HEIGHT | Hildon.SizeType.AUTO_WIDTH, Hildon.ButtonArrangement.VERTICAL);
+    encoder_picker.set_title("Encoder");
+    encoder_picker.set_selector(encoder_selector);
+    //encoder_picker.value_changed.connect( (s) => { this.settings.encoder = s.get_selector().get_current_text(); } );
+    button_bar.pack_start(encoder_picker, true, true, 2);
+    
+    // quality
+    quality_selector = new Hildon.TouchSelector.text();
+    quality_selector.append_text("high");
+    quality_selector.append_text("medium");
+    quality_selector.append_text("low");
+    this.set_quality(settings.quality);
+    Hildon.PickerButton quality_picker = new Hildon.PickerButton(Hildon.SizeType.FINGER_HEIGHT | Hildon.SizeType.AUTO_WIDTH, Hildon.ButtonArrangement.VERTICAL);
+    quality_picker.set_title("Quality");
+    quality_picker.set_selector(quality_selector);
+    //quality_picker.value_changed.connect( (s) => { this.settings.quality = s.get_selector().get_current_text(); } );
+    button_bar.pack_start(quality_picker, true, true, 2);
+    
+    control_area.pack_start(button_bar, true, true, 2);
+    // artist
+    
+    Gtk.HBox artist_bar = new Gtk.HBox(false, 4);
+    
+    Gtk.Label artist_label = new Gtk.Label("Artist");
+    artist_bar.pack_start(artist_label, false, false, 2);
+    artist_entry = new Hildon.Entry(Hildon.SizeType.HALFSCREEN_WIDTH);
+    artist_entry.set_text(settings.artist);
+    artist_entry.set_placeholder("Unknown Artist");
+    artist_bar.pack_start(artist_entry, true, true, 2);
+    
+    control_area.pack_start(artist_bar, true, true, 2);
+        
+    // title
+    Gtk.HBox title_bar = new Gtk.HBox(false, 4);
+    
+    Gtk.Label title_label = new Gtk.Label("Title");
+    title_bar.pack_start(title_label, false, false, 2);
+    title_entry = new Hildon.Entry(Hildon.SizeType.HALFSCREEN_WIDTH);
+    title_entry.set_text(settings.title);
+    title_entry.set_placeholder("Untitled");
+    title_bar.pack_start(title_entry, true, true, 2);
+    
+    control_area.pack_start(title_bar, true, true, 2);
+    
+    // album
+    Gtk.HBox album_bar = new Gtk.HBox(false, 4);
+    
+    Gtk.Label album_label = new Gtk.Label("Album");
+    album_bar.pack_start(album_label, false, false, 2);
+    album_entry = new Hildon.Entry(Hildon.SizeType.HALFSCREEN_WIDTH);
+    album_entry.set_text(settings.album);
+    album_entry.set_placeholder("Untitled");
+    album_bar.pack_start(album_entry, true, true, 2);
+    
+    control_area.pack_start(album_bar, true, true, 2);
+    
+    
+    // description
+    /*Gtk.HBox description_bar = new Gtk.HBox(false, 4);
+    
+    Gtk.Label description_label = new Gtk.Label("Description");
+    description_bar.pack_start(description_label, false, false, 2);
+    description_entry = new Hildon.Entry();
+    description_entry.truncate_multiline = false;
+    description_entry.text = "";
+    description_bar.pack_start(description_entry, true, true, 2);
+    
+    control_area.pack_start(description_bar, true, true, 2);
+    */
+    
+    this.show_all();
+  }
+  
+  private void select_location() {
+    // get a filename
+    Hildon.FileChooserDialog file_chooser = new Hildon.FileChooserDialog(this, Gtk.FileChooserAction.SAVE);
+    file_chooser.set_show_upnp(false);
+    Gtk.ResponseType ret = (Gtk.ResponseType) file_chooser.run();
+    if (Gtk.ResponseType.OK == ret) {
+      this.location_btn.value = file_chooser.get_filename();
+    }
+    file_chooser.destroy ();
+  }
+
+}
+
+}
\ No newline at end of file
diff --git a/src/MixerBin.vala b/src/MixerBin.vala
new file mode 100644 (file)
index 0000000..e8af28d
--- /dev/null
@@ -0,0 +1,126 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+namespace IdWorks {
+
+public class MixerBin : Gst.Bin {
+
+  Gst.Element adder;
+  Gst.Element equalizer;
+  Gst.Element panorama;
+  Gst.Element volume;
+  List<TrackBin> tracks;
+  Gst.GhostPad sink_pad;
+  Gst.GhostPad src_pad;
+  
+  public MixerBin(string name) {
+    this.name = name;
+    tracks = new List<TrackBin>();
+    
+    adder      = Gst.ElementFactory.make("adder", "adder_source");
+    this.add(adder);
+    equalizer  = Gst.ElementFactory.make("equalizer-10bands", "eq");
+    this.add(equalizer);
+    panorama = Gst.ElementFactory.make("audiopanorama", "panorama");
+    this.add(panorama);
+    volume    = Gst.ElementFactory.make("volume", "vol");
+    this.add(volume);
+    
+    adder.link(equalizer);
+    equalizer.link(panorama);
+    panorama.link(volume);
+    //volume.link(sink);
+    
+    sink_pad = new Gst.GhostPad("sink", adder.get_static_pad("sink"));
+    this.add_pad(sink_pad);
+    src_pad = new Gst.GhostPad("src", volume.get_static_pad("src"));
+    this.add_pad(src_pad);
+  }
+  
+  public bool add_track(TrackBin bin) {
+    this.add(bin);
+    bin.link(adder);
+    if (null == tracks) {
+      tracks = new List<TrackBin>();
+    }
+    tracks.append(bin);
+    return true;
+  }
+  
+  public void remove_all_tracks() {
+    while (null != tracks.first()) {
+      TrackBin track = tracks.first().data as TrackBin;
+      if (null != track) {
+        track.unlink(adder);
+        this.remove(track);
+      }
+      tracks.remove_link(tracks.first());
+    }
+  }
+  
+  public bool remove_track(TrackBin track) {
+    track.unlink(adder);
+    this.remove(track);
+    tracks.remove(track);
+    return true;
+  }
+  
+  public new Gst.PadTemplate get_pad_template(string name) {
+    switch (name) {
+      case "src":
+        return this.volume.get_pad_template("src");
+      case "sink":
+        return this.adder.get_pad_template("sink");
+      default:
+        return this.get_pad_template(name);
+    }
+  }
+  
+  public void set_volume(double val)
+  requires (val >= 0.0 && val <= 10.0) {
+    volume.set_property("volume", val);
+  }
+  public double get_volume() {
+    GLib.Value ret = 0.0;
+    volume.get_property("volume", ref ret);
+    return ret.get_double();
+  }
+  
+  public void set_panorama(double val) 
+  requires (val >= -1 && val <= 1) {
+    panorama.set_property("panorama", val);
+  }
+  public double get_panorama() {
+    GLib.Value ret = 0.0;
+    panorama.get_property("panorama", ref ret);
+    return ret.get_double();
+  }
+  
+  public void set_eq(int band, double val)
+  requires (band > -1 && band < 10)
+  requires (val >= -24 && val <= 12) {
+    equalizer.set_property("band" + band.to_string(), val);
+  }
+  public double get_eq(int band) {
+    GLib.Value ret = 0.0;
+    equalizer.get_property("band" + band.to_string(), ref ret);
+    return ret.get_double();
+  }
+  
+}
+
+}
\ No newline at end of file
diff --git a/src/PlayPipeline.vala b/src/PlayPipeline.vala
new file mode 100644 (file)
index 0000000..4e9e206
--- /dev/null
@@ -0,0 +1,164 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+namespace IdWorks {
+
+public class PlayPipeline : Gst.Pipeline {
+
+  Gst.Element src;
+  Gst.Element sink_element;
+  Gst.Query duration_query;
+  Gst.Query position_query;
+  int64 position;
+  int64 duration;
+  
+  public signal void position_duration(int64 position, int64 duration);
+  public signal void end_of_stream();
+  public signal void stream_error(string msg);
+  
+  public PlayPipeline(string name, Gst.Element src) {
+    //set the tag handling delegate
+    //set some default values
+    this.name = name;
+    this.src = src;
+    this.position = 0;
+    this.duration = 0;
+    //this.name = name;
+    //define the queries
+    duration_query = new Gst.Query.duration(Gst.Format.TIME);
+    position_query = new Gst.Query.position(Gst.Format.TIME);
+    
+    construct_pipeline();
+  }
+  
+  public bool set_src(MixerBin src) {
+    this.set_state(Gst.State.NULL);
+    if (null != this.src) {
+      clear_src();
+    }
+    this.src = src;
+    this.add(this.src);
+    this.src.link(this.sink_element);
+    return true;
+  }
+  
+  public void clear_src() {
+    if (null != this.src) {
+      this.set_state(Gst.State.NULL);
+      this.src.unlink(this.sink_element);
+      this.remove(this.src);
+      this.src = null;
+    }
+  }
+  
+  private void construct_pipeline() {
+    if (null != this.src) {
+      this.add(src);
+    }
+    sink_element     = Gst.ElementFactory.make("autoaudiosink", "output");
+    this.add(sink_element);
+    
+    if (null != this.src) {
+      if (!src.link(sink_element)) {
+        stdout.printf("failed to link src to sink\n");
+        stdout.flush();}
+    }
+    Gst.Bus bus = this.src.get_bus();
+    bus.add_signal_watch();
+    bus.connect("message:eos", bus_callback, this);
+    bus.connect("message:error", bus_callback, this);
+    bus.connect("message:state-changed", bus_callback, this);
+    uint watch_id = bus.add_watch(bus_callback);
+  }
+  
+  public bool bus_callback (Gst.Bus bus, Gst.Message message) {
+    stdout.printf("bus_callback\n");
+    stdout.flush();
+    switch (message.type) {
+      case Gst.MessageType.ERROR:
+        GLib.Error err;
+        string debug;
+        message.parse_error (out err, out debug);
+        stdout.printf("%s\n", err.message);
+        stdout.flush();
+        stream_error(err.message);
+        break;
+      case Gst.MessageType.EOS:
+        stdout.printf ("end of stream\n");
+        stdout.flush();
+        end_of_stream();
+        break;
+      case Gst.MessageType.STATE_CHANGED:
+        Gst.State oldstate;
+        Gst.State newstate;
+        Gst.State pending;
+        message.parse_state_changed (out oldstate, out newstate, out pending);
+        stdout.printf ("%s->%s:%s\n", oldstate.to_string(), newstate.to_string(), pending.to_string());
+        stdout.flush();
+        break;
+      default:
+        break;
+    }
+    return false;
+  }
+  
+  public void get_duration_info() {
+    bool duration_result,position_result;
+    Gst.Format format = Gst.Format.TIME;
+    duration_result = this.query(this.duration_query);
+    position_result = this.query(this.position_query);
+    if ( duration_result && position_result  ) {
+      this.duration_query.parse_duration(out format, out this.duration);
+      this.position_query.parse_position(out format, out this.position);
+      this.position_duration(this.position,this.duration);
+    }
+  }
+  
+  public void play() {
+    this.set_state(Gst.State.PLAYING);
+  }
+  
+  public void pause() {
+    this.set_state(Gst.State.PAUSED);
+  }
+  
+  public void stop() {
+    this.set_state(Gst.State.READY);
+    clear_src();
+  }
+  /*
+  public void move_to(int64 newloc) {
+    if (0 > newloc) newloc = 0;
+    //this.src.move_to(newloc);
+    //this.play();
+  }
+  
+  public void seek(int percent) {
+    move_to((int64) this.position + (this.duration * percent / 100));
+  }
+  
+  public void seek_forward(int percent) {
+    seek(percent);
+  }
+  
+  public void seek_backward(int percent) {
+    seek(-percent);
+  }
+  */
+}
+
+}
\ No newline at end of file
diff --git a/src/PlayerTransport.vala b/src/PlayerTransport.vala
new file mode 100644 (file)
index 0000000..9aa22f5
--- /dev/null
@@ -0,0 +1,205 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+namespace IdWorks {
+
+public class PlayerTransport : Gtk.HBox {
+  
+  public Gtk.Window window;
+  
+  //Gtk.HScale progress;
+  Gtk.Label label_position;
+  Gtk.Label label_duration;
+  Gtk.ToggleButton record;
+  Gtk.Button play;
+  Gtk.ToggleButton pause;
+  Gtk.Button stop;
+  
+  double volume;
+  public double get_volume() {
+    return volume;
+  }
+  public void set_volume(double volume) {
+    this.volume = volume;
+  }
+  
+  double panorama;
+  public double get_panorama() {
+    return panorama;
+  }
+  public void set_panorama(double panorama) {
+    this.panorama = panorama;
+  }
+  /*
+  bool record_state;
+  public bool get_record_state() {
+    return record_state;
+  }
+  public void set_record_state(bool state) {
+    this.record_state = state;
+  }
+  
+  bool pause_state;
+  public bool get_pause_state() {
+    return pause_state;
+  }
+  public void set_pause_state(bool state) {
+    this.pause_state = state;
+  }
+  */
+  public signal void record_toggled(bool val);
+  public signal void play_clicked();
+  public signal void stop_clicked();
+  public signal void pause_toggled(bool val);
+  public signal void volume_updated(double val);
+  public signal void panorama_updated(double val);
+  public signal void eq_updated(int band, double val);
+  
+  private void show_volume_and_pan() {
+    VolumeAndPanPopUp dlg = new VolumeAndPanPopUp("Mixer Volume", this);
+    dlg.set_transient_for(this.window);
+    dlg.set_volume(this.volume);
+    dlg.set_panorama(this.panorama);
+    dlg.volume_updated.connect(volume_updated_callback);
+    dlg.panorama_updated.connect(panorama_updated_callback);
+    dlg.run();
+    dlg.destroy();
+    dlg = null;
+  }
+  
+  private void show_equalizer() {
+    EqualizerPopUp dlg = new EqualizerPopUp("Mixer EQ", this);
+    dlg.set_transient_for(this.window);
+    dlg.eq_updated.connect(eq_updated_callback);
+    dlg.run();
+    dlg.destroy();
+    dlg = null;
+  }
+  
+  private void volume_updated_callback(VolumeAndPanPopUp sender, double volume)
+  {
+    this.volume = volume;
+    volume_updated(this.volume);
+  }
+  
+  private void panorama_updated_callback(VolumeAndPanPopUp sender, double panorama)
+  {
+    this.panorama = panorama;
+    panorama_updated(this.panorama);
+  }
+  
+  private void eq_updated_callback(EqualizerPopUp sender, int band, double val)
+  {
+    eq_updated(band, val);
+  }
+  
+  /*private static string time_to_string(int64 t) {
+    uint64 ut = (uint64)t / Time.Nanoseconds.MILLISECOND;
+    return Time.CLOCK_FORMAT.printf((ut / Time.Milliseconds.MINUTE), (ut / Time.Milliseconds.SECOND) % Time.Seconds.MINUTE, (ut % Time.Milliseconds.SECOND) / 100);
+  }*/
+  public void position_duration_callback(int64 position, int64 duration) {
+    label_position.set_text("%s\n%s".printf(Time.time_to_string(position), Time.time_to_string(duration)));
+  }
+  
+  construct {
+    //record_state = false;
+    //pause_state = false;
+    volume = 1.0;
+    panorama = 0.0;
+    Gtk.HBox transport = new Gtk.HBox(true, 0);
+    //transport.add(new Gtk.Label("Test"));
+    label_position = new Gtk.Label((Time.CLOCK_FORMAT + "\n" + Time.CLOCK_FORMAT).printf((uint64)0,(uint64)0,(uint64)0,(uint64)0,(uint64)0,(uint64)0));
+    transport.pack_end(label_position, false, false, 0);
+    /*progress = new Gtk.HScale.with_range(0.0, 100.0, 0.1);
+    progress.set_draw_value(false);
+    progress.value_changed.connect((s) => { label_position.set_text("%02.2f".printf(s.get_value())); });
+    transport.pack_start(progress, true, true, 2);
+    label_duration = new Gtk.Label("100.0");
+    transport.pack_start(label_duration, false, false, 0);*/
+    Gtk.HButtonBox buttons = new Gtk.HButtonBox();
+    record = new Gtk.ToggleButton(); //.with_label("Re");
+    record.set_image(new Gtk.Image.from_icon_name("camera_video_recording", Gtk.IconSize.BUTTON));
+    record.toggled.connect((b) => { record_toggled(b.active); set_recording(); });
+    buttons.add(record);
+    play = new Gtk.Button(); //.with_label("Pl");
+    play.set_image(new Gtk.Image.from_icon_name("camera_playback", Gtk.IconSize.BUTTON));
+    play.clicked.connect((b) => { play_clicked(); set_playing(); });
+    buttons.add(play);
+    pause = new Gtk.ToggleButton(); //.with_label("Pa");
+    pause.set_image(new Gtk.Image.from_icon_name("camera_video_pause", Gtk.IconSize.BUTTON));
+    pause.toggled.connect((b) => { pause_toggled(b.active); set_paused(); });
+    buttons.add(pause);
+    stop = new Gtk.Button(); //.with_label("St");
+    stop.set_image(new Gtk.Image.from_icon_name("camera_video_stop", Gtk.IconSize.BUTTON));
+    stop.clicked.connect((b) => { stop_clicked(); set_idle(); });
+    buttons.add(stop);
+    Gtk.Button vol = new Gtk.Button(); //.with_label("Vo");
+    //Gtk.Image vol_img = new Gtk.Image.from_icon_name("general_gtalk", Gtk.IconSize.BUTTON);
+    vol.set_image(new Gtk.Image.from_icon_name("statusarea_volumelevel4", Gtk.IconSize.BUTTON));
+    vol.clicked.connect(show_volume_and_pan);
+    buttons.add(vol);
+    Gtk.Button eq = new Gtk.Button.with_label("EQ");
+    eq.clicked.connect(show_equalizer);
+    buttons.add(eq);
+    this.pack_start(buttons, false, false, 0);
+    this.pack_start(transport, true, true, 0);
+    set_idle();
+  }
+  
+  private void set_recording() {
+    play.set_sensitive(true);
+    record.set_sensitive(true);
+    pause.set_sensitive(false);
+    stop.set_sensitive(false);
+  }
+  private void set_playing() {
+    play.set_sensitive(false);
+    record.set_sensitive(false);
+    pause.set_sensitive(true);
+    stop.set_sensitive(true);
+  }
+  private void set_idle() {
+    play.set_sensitive(true);
+    record.set_sensitive(true);
+    record.set_active(false);
+    pause.set_sensitive(false);
+    stop.set_sensitive(false);
+  }
+  private void set_paused() {
+    play.set_sensitive(false);
+    record.set_sensitive(false);
+    pause.set_sensitive(true);
+    stop.set_sensitive(true);
+  }
+
+  private override void add(Gtk.Widget w) {
+    base.add(w);
+  }
+
+  private new void pack_start(Gtk.Widget w, bool expand, bool fill, uint padding = 0) {
+    base.pack_start(w, expand, fill, padding);
+  }
+  
+  private new void pack_end(Gtk.Widget w, bool expand, bool fill, uint padding = 0) {
+    base.pack_start(w, expand, fill, padding);
+  }
+  
+  
+
+}
+
+}
diff --git a/src/Program.vala b/src/Program.vala
new file mode 100644 (file)
index 0000000..d1d0ef8
--- /dev/null
@@ -0,0 +1,26 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+extern void exit(int exit_code);
+
+public static int main(string[] args) {
+  Gtk.init(ref args);
+  Gst.init(ref args);
+  IdWorks.DemoRecorder recorder = new IdWorks.DemoRecorder();
+  recorder.run();
+  return 0;
+}
\ No newline at end of file
diff --git a/src/ProgressPopUp.vala b/src/ProgressPopUp.vala
new file mode 100644 (file)
index 0000000..ae48d1b
--- /dev/null
@@ -0,0 +1,67 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+namespace IdWorks {
+
+public class ProgressPopUp : Hildon.Dialog {
+  
+  Gtk.ProgressBar progress_bar;
+  Gtk.Label message;
+
+  public ProgressPopUp(string title, Gtk.Widget parent) {
+    this.set_title(title);
+    this.set_parent(parent);
+    this.set_default_response(Gtk.ResponseType.ACCEPT);
+    
+    construct_interface();
+  }
+  
+  private void construct_interface() {
+    //this.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL);
+    
+    Gtk.VBox control_area = (Gtk.VBox)this.get_content_area();
+    
+    message = new Gtk.Label("");
+    control_area.pack_start(message, true, true, 4);
+    
+    progress_bar = new Gtk.ProgressBar();
+    control_area.pack_end(progress_bar, false, false, 4);
+    
+    this.show_all();
+  }
+  
+  public void set_progress(double percent)
+  requires (percent <= 1.0 && percent >= 0.0) {
+    this.progress_bar.set_fraction(percent);
+  }
+  
+  public void set_message(string message) {
+    this.message.set_text(message);
+  }
+  
+  public void set_progress_text(string text) {
+    this.progress_bar.set_text(text);
+  }
+  
+  public void close_me() {
+    this.response(Gtk.ResponseType.ACCEPT);
+    this.close();
+  }
+
+}
+
+}
\ No newline at end of file
diff --git a/src/ProjectSettings.vala b/src/ProjectSettings.vala
new file mode 100644 (file)
index 0000000..e82fd50
--- /dev/null
@@ -0,0 +1,40 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+namespace IdWorks {
+
+protected struct ProjectSettings {
+
+  public string working_folder;
+  public string name;
+  public double volume;
+  public double pan;
+  public unowned List<Track?> tracks;
+  
+  public string serialize_to_string() {
+    string ret = "";
+    /// TODO
+    return ret;
+  }
+  
+  public void deserialize_from_string(string data) {
+    /// TODO
+  }
+  
+}
+
+}
diff --git a/src/ProjectSettingsDialog.vala b/src/ProjectSettingsDialog.vala
new file mode 100644 (file)
index 0000000..a8245c7
--- /dev/null
@@ -0,0 +1,166 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+namespace IdWorks {
+
+public class ProjectSettingsDialog : Hildon.Dialog {
+
+  Hildon.Entry name_entry;
+  Hildon.Button directory_btn;
+  Hildon.Button filename_btn;
+  string root_folder;
+  bool user_set;
+  
+  public string get_project_name() {
+    return name_entry.get_text();
+  }
+  public void set_project_name(string project_name) {
+    name_entry.set_text(project_name);
+    validate();
+  }
+  
+  public string get_project_directory() {
+    return directory_btn.get_value();
+  }
+  public void set_project_directory(string project_directory) {
+    directory_btn.set_value(project_directory);
+    validate();
+  }
+  
+  public string get_project_filename() {
+    return filename_btn.get_value();
+  }
+  public void set_project_filename(string project_filename) {
+    filename_btn.set_value(project_filename);
+    validate();
+  }
+  
+  public ProjectSettingsDialog(Gtk.Widget parent, string title) {
+    this.user_set = false;
+    this.set_parent(parent);
+    this.set_title(title);
+    this.set_default_response(Gtk.ResponseType.CANCEL);
+    construct_interface("", "", "");
+  }
+  public ProjectSettingsDialog.with_values(Gtk.Widget parent, string title, string project_name, string project_directory, string project_filename, string root_folder) {
+    user_set = (0 < project_directory.length && 0 < project_filename.length);
+    this.root_folder = root_folder;
+    this.set_parent(parent);
+    this.set_title(title);
+    this.set_default_response(Gtk.ResponseType.CANCEL);
+    construct_interface(project_name, project_directory, project_filename);
+  }
+  
+  private void construct_interface(string name, string dir, string filename) {
+    this.add_button(Gtk.STOCK_OK, Gtk.ResponseType.OK);
+    
+    Gtk.VBox control_area = (Gtk.VBox)this.get_content_area();
+    
+    Gtk.HBox name_bar = new Gtk.HBox(false, 4);
+    
+    Gtk.Label name_label = new Gtk.Label("Name");
+    name_bar.pack_start(name_label, false, false, 2);
+    name_entry = new Hildon.Entry(Hildon.SizeType.HALFSCREEN_WIDTH);
+    name_entry.set_text(name);
+    name_entry.set_placeholder("Project name");
+    name_entry.changed.connect(name_entry_changed);
+    name_bar.pack_start(name_entry, true, true, 2);
+    
+    control_area.pack_start(name_bar, true, true, 4);
+    
+    directory_btn = new Hildon.Button.with_text(Hildon.SizeType.FINGER_HEIGHT | Hildon.SizeType.HALFSCREEN_WIDTH, Hildon.ButtonArrangement.VERTICAL, "Working folder", dir);
+    directory_btn.set_style(Hildon.ButtonStyle.PICKER);
+    directory_btn.set_alignment(0, 0, 1, 1);
+    directory_btn.set_value(dir);
+    directory_btn.clicked.connect(select_directory);
+    
+    control_area.pack_start(directory_btn, true, true, 4);
+    
+    filename_btn = new Hildon.Button.with_text(Hildon.SizeType.FINGER_HEIGHT | Hildon.SizeType.HALFSCREEN_WIDTH, Hildon.ButtonArrangement.VERTICAL, "Settings file", dir);
+    filename_btn.set_style(Hildon.ButtonStyle.PICKER);
+    filename_btn.set_alignment(0, 0, 1, 1);
+    filename_btn.set_value(filename);
+    filename_btn.clicked.connect(select_filename);
+    
+    control_area.pack_start(filename_btn, true, true, 4);
+    
+    validate();
+    
+    this.show_all();
+  }
+  
+  private void name_entry_changed() {
+    if (!user_set) {
+      this.directory_btn.value = root_folder + "/" + get_project_name();
+      this.filename_btn.value = this.directory_btn.value + "/project.xml";
+    }
+    validate();
+  }
+  
+  private void validate() {
+    bool good = true;
+    good &= (0 < get_project_name().length);
+    good &= (0 < get_project_directory().length);
+    good &= (0 < get_project_filename().length);
+    this.set_response_sensitive(Gtk.ResponseType.OK, good);
+  }
+  
+  private void select_directory() {
+    // get a filename
+    Hildon.FileChooserDialog file_chooser = new Hildon.FileChooserDialog(this, Gtk.FileChooserAction.SELECT_FOLDER);
+    file_chooser.set_show_upnp(false);
+    file_chooser.set_safe_folder(this.root_folder);
+    file_chooser.set_current_folder(this.root_folder);
+    if (0 < this.directory_btn.value.length) {
+      // if it exists?
+      //file_chooser.set_current_folder(this.directory_btn.value);
+      // else
+    }
+    Gtk.ResponseType ret = (Gtk.ResponseType) file_chooser.run();
+    if (Gtk.ResponseType.OK == ret) {
+      user_set = true;
+      this.directory_btn.value = file_chooser.get_filename();
+      if ("" == this.filename_btn.value) {
+        this.filename_btn.value = this.directory_btn.value + "/project.xml";
+      }
+    }
+    file_chooser.destroy ();
+    validate();
+  }
+  
+  private void select_filename() {
+    // get a filename
+    Hildon.FileChooserDialog file_chooser = new Hildon.FileChooserDialog(this, Gtk.FileChooserAction.SAVE);
+    file_chooser.set_show_upnp(false);
+    file_chooser.set_current_folder(this.root_folder);
+    file_chooser.set_current_name("project.xml");
+    if (0 < this.filename_btn.value.length) {
+      //file_chooser.set_filename(this.filename_btn.value);
+      file_chooser.set_safe_folder(root_folder);
+      /// TODO // file_chooser.set_current_folder(this.filename_btn.value); 
+    }
+    Gtk.ResponseType ret = (Gtk.ResponseType) file_chooser.run();
+    if (Gtk.ResponseType.OK == ret) {
+      user_set = true;
+      this.filename_btn.value = file_chooser.get_filename();
+    }
+    file_chooser.destroy ();
+    validate();
+  }
+}
+
+}
\ No newline at end of file
diff --git a/src/RecordPipeline.vala b/src/RecordPipeline.vala
new file mode 100644 (file)
index 0000000..c90593f
--- /dev/null
@@ -0,0 +1,179 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+namespace IdWorks {
+
+public class RecordPipeline : Gst.Pipeline {
+
+  Gst.Element src;
+  Gst.Element queue;
+  Gst.Element resampler;
+  Gst.Element encoder;
+  Gst.Element converter;
+  Gst.Element filesink;
+  //string name;
+  string location;
+  string encoder_element;
+  Gst.Caps caps;
+  int64 start_time;
+  Gst.Query duration_query;
+  Gst.Query position_query;
+  int64 position;
+  int64 duration;
+  
+  public bool bouncing;
+  
+  public signal void position_duration(int64 position, int64 duration);
+  public signal void end_of_stream();
+  public signal void stream_error(string msg);
+  
+  public RecordPipeline(string name, string location, Gst.Element src, Gst.Caps caps, string encoder_element) {
+    //this.bouncing = false;
+    this.name = name;
+    this.location = location;
+    this.caps = caps;
+    this.src = src;
+    this.encoder_element = encoder_element;
+    //set the tag handling delegate
+    //this.handle_tags_delegate = this.handle_tags;
+    //set some default values
+    this.start_time = 0;
+    //define the queries
+    duration_query = new Gst.Query.duration(Gst.Format.TIME);
+    position_query = new Gst.Query.position(Gst.Format.TIME);
+    this.construct_pipeline();
+  }
+  
+  public bool set_src(Gst.Element src) {
+    this.set_state(Gst.State.NULL);
+    if (null != this.src) {
+      clear_src();
+    }
+    this.src = src;
+    this.add(this.src);
+    this.src.link(this.queue);
+    return true;
+  }
+  
+  public void clear_src() {
+    if (null != this.src) {
+      this.set_state(Gst.State.NULL);
+      this.src.unlink(this.queue);
+      this.remove(this.src);
+      this.src = null;
+    }
+  }
+  
+  public bool set_location(string location) {
+    this.set_state(Gst.State.NULL);
+    this.location = location;
+    this.filesink.set_property("location", location);
+    return true;
+  }
+  
+  public string get_location() {
+    return this.location;
+  }
+  
+  public void get_duration_info() {
+    if (bouncing) {
+      bool duration_result,position_result;
+      Gst.Format format = Gst.Format.TIME;
+      duration_result = this.query(this.duration_query);
+      position_result = this.query(this.position_query);
+      if ( duration_result && position_result  ) {
+        this.duration_query.parse_duration(out format, out this.duration);
+        this.position_query.parse_position(out format, out this.position);
+        this.position_duration(this.position,this.duration);
+        if (this.position >= this.duration) {
+          this.end_of_stream();
+        }
+      }
+    }
+    else {
+      this.position_duration(Time.get_current_time() - start_time, 0);
+    }
+  }
+  
+  public void record() {
+    start_time = Time.get_current_time();
+    this.set_state(Gst.State.PLAYING);
+  }
+  
+  public void stop() {
+    //this.end_of_stream();
+    this.set_state(Gst.State.READY);
+  }
+  
+  private void construct_pipeline() {
+    //this.name= name;
+    
+    if (null != this.src) {
+      this.add(this.src);
+    }
+    
+    this.converter = Gst.ElementFactory.make("audioconvert", "converter");
+    this.add(this.converter);
+    
+    this.queue = Gst.ElementFactory.make("queue", "buffer");
+    this.add(this.queue);
+    
+    this.resampler = Gst.ElementFactory.make("audioresample", "resampler");
+    this.resampler.set_property("quality", 10);
+    this.add(resampler);
+    this.encoder = Gst.ElementFactory.make(encoder_element, "encoder");
+    this.add(this.encoder);
+    if (null != this.encoder) {
+//       stdout.printf("Encoder added!\n");
+//       stdout.flush();
+    }
+    this.filesink = Gst.ElementFactory.make("filesink", "fileoutput");
+    if (null != this.filesink) {
+      this.filesink.set_property("location", location);
+//       stdout.printf("Location set!\n");
+//       stdout.flush();
+      this.add(this.filesink);
+//       stdout.printf("Filesink added!\n");
+//       stdout.flush();
+    }
+    
+    if (null != this.src && this.src.link(this.queue)) {
+//       stdout.printf("Source linked!\n");
+//       stdout.flush();
+    }
+    if (this.queue.link(this.converter)) {
+//       stdout.printf("Queue linked!\n");
+//       stdout.flush();
+    }
+    if (this.converter.link(this.resampler)) {
+//       stdout.printf("Converter linked!\n");
+//       stdout.flush();
+    }
+    if (this.resampler.link_filtered(this.encoder, caps)) {
+//       stdout.printf("Resampler linked!\n");
+//       stdout.flush();
+    }
+    if (this.encoder.link(this.filesink)) {
+//       stdout.printf("Encoder linked!\n");
+//       stdout.flush();
+    }
+  }
+  
+  
+}
+
+}
\ No newline at end of file
diff --git a/src/RecordPipeline_Old.vala b/src/RecordPipeline_Old.vala
new file mode 100644 (file)
index 0000000..8d04703
--- /dev/null
@@ -0,0 +1,132 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+namespace IdWorks {
+
+public class RecordPipeline_Old : Object {
+
+  Gst.Pipeline pipeline;
+  Gst.Element src;
+  Gst.Element decoder;
+  Gst.Element converter;
+  Gst.Element queue;
+  Gst.Element sink;
+  Gst.Element resampler;
+  Gst.Element encoder;
+  Gst.Element filesink;
+  string name;
+  string location;
+  Gst.Caps caps;
+  Gst.Query duration_query;
+  Gst.Query position_query;
+  int64 position;
+  int64 duration;
+  
+  public signal void position_duration(int64 position, int64 duration);
+  public signal void tag_parsed(string tag, string val);
+  public signal void end_of_stream();
+  public signal void stream_error(string msg);
+  
+  public RecordPipeline_Old(string name, string location, Gst.Caps caps) {
+    this.name = name;
+    this.location = location;
+    this.caps = caps;
+    //set the tag handling delegate
+    //this.handle_tags_delegate = this.handle_tags;
+    //set some default values
+    this.position=0;
+    this.duration=0;
+    this.construct_pipeline(this.name, this.location, this.caps);
+    //define the queries
+    //duration_query = new Gst.Query.duration(Gst.Format.TIME);
+    //position_query = new Gst.Query.position(Gst.Format.TIME);
+  }
+  
+  public string get_location() {
+    return this.location;
+  }
+  
+  public void record() {
+    this.pipeline.set_state(Gst.State.PLAYING);
+  }
+  
+  public void stop() {
+    this.end_of_stream();
+    this.pipeline.set_state(Gst.State.READY);
+  }
+  
+  private void construct_pipeline(string name, string location, Gst.Caps caps) {
+    pipeline = new Gst.Pipeline(name);
+    
+    this.src = Gst.ElementFactory.make("pulsesrc", "input");
+    src.set_property("device", "source.hw0");
+    this.pipeline.add(this.src);
+    
+    this.converter = Gst.ElementFactory.make("audioconvert", "converter");
+    this.pipeline.add(this.converter);
+    
+    this.queue = Gst.ElementFactory.make("queue", "buffer");
+    this.pipeline.add(this.queue);
+    
+    this.resampler = Gst.ElementFactory.make("audioresample", "resampler");
+    this.resampler.set_property("quality", 10);
+    this.pipeline.add(resampler);
+    this.encoder = Gst.ElementFactory.make("wavenc", "encoder");
+    this.pipeline.add(this.encoder);
+    if (null != this.encoder) {
+      stdout.printf("Encoder added!\n");
+      stdout.flush();
+    }
+    this.filesink = Gst.ElementFactory.make("filesink", "fileoutput");
+    if (null != this.filesink) {
+      this.filesink.set_property("location", location);
+      stdout.printf("Location set!\n");
+      stdout.flush();
+      this.pipeline.add(this.filesink);
+      stdout.printf("Filesink added!\n");
+      stdout.flush();
+    }
+    /*if (this.converter.link(this.resampler)) {
+      stdout.printf("Converter linked!\n");
+      stdout.flush();
+    }*/
+    if (this.src.link(this.queue)) {
+      stdout.printf("Source linked!\n");
+      stdout.flush();
+    }
+    if (this.queue.link(this.converter)) {
+      stdout.printf("Queue linked!\n");
+      stdout.flush();
+    }
+    if (this.converter.link(this.resampler)) {
+      stdout.printf("Converter linked!\n");
+      stdout.flush();
+    }
+    if (this.resampler.link_filtered(this.encoder, caps)) {
+      stdout.printf("Resampler linked!\n");
+      stdout.flush();
+    }
+    if (this.encoder.link(this.filesink)) {
+      stdout.printf("Encoder linked!\n");
+      stdout.flush();
+    }
+  }
+  
+  
+}
+
+}
\ No newline at end of file
diff --git a/src/SettingsStructures.vala b/src/SettingsStructures.vala
new file mode 100644 (file)
index 0000000..1b67831
--- /dev/null
@@ -0,0 +1,1283 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+namespace IdWorks {
+  
+  namespace SettingsStructures {
+
+  public struct VolumeSettings {
+    public double volume;
+    public bool mute;
+    
+    public VolumeSettings() {
+      this.volume = 1.0;
+      this.mute = false;
+    }
+    public VolumeSettings.with_values(double volume, bool mute) {
+      this.volume = volume;
+      this.mute = mute;
+    }
+    public string to_xml_string() {
+      return ("<VolumeSettings>\n" +
+             "<volume type=\"double\">%02f</volume>\n" +
+             "<mute type=\"bool\">%s</mute>\n" +
+             "</VolumeSettings>\n").printf(
+                 volume
+               , mute.to_string()
+             );
+    }
+    public void from_xml_node_helper(Xml.Node* node) {
+      // VolumeSettings is below root
+      // Loop over the passed node's children
+      for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {
+        // Spaces between tags are also nodes, discard them
+        if (iter->type != Xml.ElementType.ELEMENT_NODE) {
+          continue;
+        }
+        // Get the node's name
+        if ("VolumeSettings" == iter->name) {
+          from_xml_node(iter);
+          break;
+        }
+      }
+    }
+    public void from_xml_node(Xml.Node* node) {
+      // VolumeSettings is root
+      // Loop over the passed node's children
+      for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {
+        // Spaces between tags are also nodes, discard them
+        if (iter->type != Xml.ElementType.ELEMENT_NODE) {
+          continue;
+        }
+        // Get the node's name
+        string node_name = iter->name;
+        // Get the nodes type
+        string? type = XmlHelpers.get_node_attribute(iter, "type");
+        // Get the node's content with <tags> stripped
+        if (null != type) {
+          // check the type
+          switch(type) {
+            case "bool":
+              switch(node_name) {
+                case "mute":
+                  this.mute = iter->get_content().to_bool();
+                  break;
+                default:
+                  break;
+              }
+              break;
+            case "double":
+              switch(node_name) {
+                case "volume":
+                  this.volume = iter->get_content().to_double();
+                  break;
+                default:
+                  break;
+              }
+              break;
+            default:
+              continue;
+          }
+        }
+        
+      }
+    }
+  }
+
+  public struct PanoramaSettings {
+    public double panorama;
+    
+    public PanoramaSettings() {
+      this.panorama = 0.0;
+    }
+    public PanoramaSettings.with_values(double panorama) {
+      this.panorama = panorama;
+    }
+    public string to_xml_string() {
+      return ("<PanoramaSettings>\n" +
+              "<panorama type=\"double\">%02f</panorama>\n" +
+              "</PanoramaSettings>\n").printf(
+                panorama
+              );
+    }
+    public void from_xml_node_helper(Xml.Node* node) {
+      // PanoramaSettings is below root
+      // Loop over the passed node's children
+      for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {
+        // Spaces between tags are also nodes, discard them
+        if (iter->type != Xml.ElementType.ELEMENT_NODE) {
+          continue;
+        }
+        // Get the node's name
+        if ("PanoramaSettings" == iter->name) {
+          from_xml_node(iter);
+          break;
+        }
+      }
+    }
+    public void from_xml_node(Xml.Node* node) {
+      // PanoramaSettings is root
+      // Loop over the passed node's children
+      for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {
+        // Spaces between tags are also nodes, discard them
+        if (iter->type != Xml.ElementType.ELEMENT_NODE) {
+          continue;
+        }
+        // Get the node's name
+        string node_name = iter->name;
+        // Get the nodes type
+        string? type = XmlHelpers.get_node_attribute(iter, "type");
+        // Get the node's content with <tags> stripped
+        if (null != type) {
+          // check the type
+          switch(type) {
+            case "double":
+              switch(node_name) {
+                case "panorama":
+                  this.panorama = iter->get_content().to_double();
+                  break;
+                default:
+                  break;
+              }
+              break;
+            default:
+              continue;
+          }
+        }
+        
+      }
+    }
+  }
+
+  public struct DynamicsSettings {
+    public bool active;
+    public string mode;
+    public string characteristics;
+    public double ratio;
+    public double threshold;
+    
+    public DynamicsSettings() {
+      this.active = false;
+      this.mode = "compressor";
+      this.characteristics = "soft-knee";
+      this.ratio = 1.0;
+      this.threshold = 1.0;
+    }
+    public DynamicsSettings.with_values(bool active, string mode, string characteristics, double ratio, double threshold) {
+      this.active = false;
+      this.mode = mode;
+      this.characteristics = characteristics;
+      this.ratio = ratio;
+      this.threshold = threshold;
+    }
+    public string to_xml_string() {
+      return ("<DynamicsSettings>\n" +
+              "<active type=\"bool\">%s</active>\n" +
+              "<mode type=\"string\"><![CDATA[%s]]></mode>\n" +
+              "<characteristics type=\"string\"><![CDATA[%s]]></characteristics>\n" +
+              "<ratio type=\"double\">%02f</ratio>\n" +
+              "<threshold type=\"double\">%02f</threshold>\n" +
+              "</DynamicsSettings>\n").printf(
+                active.to_string(),
+                mode,
+                characteristics,
+                ratio,
+                threshold
+              );
+    }
+    public void from_xml_node_helper(Xml.Node* node) {
+      // DynamicsSettings is below root
+      // Loop over the passed node's children
+      for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {
+        // Spaces between tags are also nodes, discard them
+        if (iter->type != Xml.ElementType.ELEMENT_NODE) {
+          continue;
+        }
+        // Get the node's name
+        if ("DynamicsSettings" == iter->name) {
+          from_xml_node(iter);
+          break;
+        }
+      }
+    }
+    public void from_xml_node(Xml.Node* node) {
+      // Loop over the passed node's children
+      for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {
+        // Spaces between tags are also nodes, discard them
+        if (iter->type != Xml.ElementType.ELEMENT_NODE) {
+          continue;
+        }
+        // Get the node's name
+        string node_name = iter->name;
+        // Get the nodes type
+        string? type = XmlHelpers.get_node_attribute(iter, "type");
+        // Get the node's content with <tags> stripped
+        if (null != type) {
+          // check the type
+          switch(type) {
+            case "bool":
+              switch(node_name) {
+                case "active":
+                  this.active = iter->get_content().to_bool();
+                  break;
+                default:
+                  break;
+              }
+              break;
+            case "double":
+              switch(node_name) {
+                case "ratio":
+                  this.ratio = iter->get_content().to_double();
+                  break;
+                case "threshold":
+                  this.threshold = iter->get_content().to_double();
+                  break;
+                default:
+                  break;
+              }
+              break;
+            case "string":
+              switch(node_name) {
+                case "mode":
+                  this.mode = iter->get_content();
+                  break;
+                case "characteristics":
+                  this.characteristics = iter->get_content();
+                  break;
+                default:
+                  break;
+              }
+              break;
+            default:
+              continue;
+          }
+        }
+        
+      }
+    }
+  }
+
+  public struct EchoSettings {
+    public bool active;
+    public uint64 max_delay;
+    public uint64 delay;
+    public double feedback;
+    public double intensity;
+    
+    public EchoSettings() {
+      this.active = false;
+      this.max_delay = 1 * Time.Nanoseconds.SECOND;
+      this.delay = 0;
+      this.feedback = 0;
+      this.intensity = 0;
+    }
+    public EchoSettings.with_values(bool active, uint64 max_delay, uint64 delay, double feedback, double intensity) {
+      this.active = active;
+      this.max_delay = max_delay;
+      this.delay = delay;
+      this.feedback = feedback;
+      this.intensity = intensity;
+    }
+    public string to_xml_string() {
+      return ("<EchoSettings>\n" +
+              "<active type=\"bool\">%s</active>\n" +
+              "<max_delay type=\"uint64\">%llu</max_delay>\n" +
+              "<delay type=\"uint64\">%llu</delay>\n" +
+              "<feedback type=\"double\">%02f</feedback>\n" +
+              "<intensity type=\"double\">%02f</intensity>\n" +
+              "</EchoSettings>\n").printf(
+                active.to_string(),
+                max_delay,
+                delay,
+                feedback,
+                intensity
+                );
+    }
+    public void from_xml_node_helper(Xml.Node* node) {
+      // EchoSettings is below root
+      // Loop over the passed node's children
+      for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {
+        // Spaces between tags are also nodes, discard them
+        if (iter->type != Xml.ElementType.ELEMENT_NODE) {
+          continue;
+        }
+        // Get the node's name
+        if ("EchoSettings" == iter->name) {
+          from_xml_node(iter);
+          break;
+        }
+      }
+    }
+    public void from_xml_node(Xml.Node* node) {
+      // EchoSettings is root
+      // Loop over the passed node's children
+      for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {
+        // Spaces between tags are also nodes, discard them
+        if (iter->type != Xml.ElementType.ELEMENT_NODE) {
+          continue;
+        }
+        // Get the node's name
+        string node_name = iter->name;
+        // Get the nodes type
+        string? type = XmlHelpers.get_node_attribute(iter, "type");
+        // Get the node's content with <tags> stripped
+        if (null != type) {
+          // check the type
+          switch(type) {
+            case "bool":
+              switch(node_name) {
+                case "active":
+                  this.active = iter->get_content().to_bool();
+                  break;
+                default:
+                  break;
+              }
+              break;
+            case "double":
+              switch(node_name) {
+                case "feedback":
+                  this.feedback = iter->get_content().to_double();
+                  break;
+                case "intensity":
+                  this.intensity = iter->get_content().to_double();
+                  break;
+                default:
+                  break;
+              }
+              break;
+            case "uint64":
+              switch(node_name) {
+                case "max_delay":
+                  this.max_delay = iter->get_content().to_uint64();
+                  break;
+                case "delay":
+                  this.delay = iter->get_content().to_uint64();
+                  break;
+                default:
+                  break;
+              }
+              break;
+            default:
+              continue;
+          }
+        }
+        
+      }
+    }
+  }
+  
+  public struct EqualizerSettings {
+    public bool active;
+    public double[] bands;
+    public string name;
+    
+    public EqualizerSettings() {
+      this.active = true;
+      this.name = "";
+      this.bands = new double[] {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
+    }
+    public EqualizerSettings.with_name(string val) {
+      this.active = true;
+      this.name = val;
+      this.bands = new double[] {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
+    }
+    public EqualizerSettings.with_values(string name, double[] bands) {
+      this.active = true;
+      this.name = name;
+      this.bands = bands;
+    }
+    public string to_xml_string() {
+      return ("<EqualizerSettings>\n" +
+              "<active type=\"bool\">%s></active>\n" +
+              "<name type=\"string\"><![CDATA[%s]]></name>\n" +
+              "<band_0 type=\"double\">>%02f</band_0>\n" +
+              "<band_1 type=\"double\">>%02f</band_1>\n" +
+              "<band_2 type=\"double\">>%02f</band_2>\n" +
+              "<band_3 type=\"double\">>%02f</band_3>\n" +
+              "<band_4 type=\"double\">>%02f</band_4>\n" +
+              "<band_5 type=\"double\">>%02f</band_5>\n" +
+              "<band_6 type=\"double\">>%02f</band_6>\n" +
+              "<band_7 type=\"double\">>%02f</band_7>\n" +
+              "<band_8 type=\"double\">>%02f</band_8>\n" +
+              "<band_9 type=\"double\">>%02f</band_9>\n" +
+              "</EqualizerSettings>\n").printf(
+                active.to_string(),
+                name,
+                bands[0],
+                bands[1],
+                bands[2],
+                bands[3],
+                bands[4],
+                bands[5],
+                bands[6],
+                bands[7],
+                bands[8],
+                bands[9]
+              );
+    }
+    public void from_xml_node_helper(Xml.Node* node) {
+      // Loop over the passed node's children
+      for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {
+        // Spaces between tags are also nodes, discard them
+        if (iter->type != Xml.ElementType.ELEMENT_NODE) {
+          continue;
+        }
+        // Get the node's name
+        if ("EqualizerSettings" == iter->name) {
+          from_xml_node(iter);
+          break;
+        }
+      }
+    }
+    public void from_xml_node(Xml.Node* node) {
+      // Loop over the passed node's children
+      for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {
+        // Spaces between tags are also nodes, discard them
+        if (iter->type != Xml.ElementType.ELEMENT_NODE) {
+          continue;
+        }
+        // Get the node's name
+        string node_name = iter->name;
+        // Get the nodes type
+        string? type = XmlHelpers.get_node_attribute(iter, "type");
+        // Get the node's content with <tags> stripped
+        if (null != type) {
+          // check the type
+          switch(type) {
+            case "bool":
+              switch(node_name) {
+                case "active":
+                  this.active = iter->get_content().to_bool();
+                  break;
+                default:
+                  break;
+              }
+              break;
+            case "double":
+              switch(node_name) {
+                case "band_0":
+                  this.bands[0] = iter->get_content().to_double();
+                  break;
+                case "band_1":
+                  this.bands[1]= iter->get_content().to_double();
+                  break;
+                case "band_2":
+                  this.bands[2] = iter->get_content().to_double();
+                  break;
+                case "band_3":
+                  this.bands[3] = iter->get_content().to_double();
+                  break;
+                case "band_4":
+                  this.bands[4] = iter->get_content().to_double();
+                  break;
+                case "band_5":
+                  this.bands[5] = iter->get_content().to_double();
+                  break;
+                case "band_6":
+                  this.bands[6] = iter->get_content().to_double();
+                  break;
+                case "band_7":
+                  this.bands[7] = iter->get_content().to_double();
+                  break;
+                case "band_8":
+                  this.bands[8] = iter->get_content().to_double();
+                  break;
+                case "band_9":
+                  this.bands[9] = iter->get_content().to_double();
+                  break;
+                default:
+                  break;
+              }
+              break;
+            case "string":
+              switch(node_name) {
+                case "name":
+                  this.name = iter->get_content();
+                  break;
+                default:
+                  break;
+              }
+              break;
+            default:
+              continue;
+          }
+        }
+        
+      }
+    }
+    
+    public string to_string() {
+      return "%s: bands:{%02f %02f %02f %02f %02f %02f %02f %02f %02f %02f}\n".printf(
+      name
+      , bands[0]
+      , bands[1]
+      , bands[2]
+      , bands[3]
+      , bands[4]
+      , bands[5]
+      , bands[6]
+      , bands[7]
+      , bands[8]
+      , bands[9]);
+    }
+    
+    public string serialize_to_string() {
+      return "EqualizerPreset:%s,%02f,%02f,%02f,%02f,%02f,%02f,%02f,%02f,%02f,%02f\n".printf(
+      name.replace(",", "\\,")
+      , bands[0]
+      , bands[1]
+      , bands[2]
+      , bands[3]
+      , bands[4]
+      , bands[5]
+      , bands[6]
+      , bands[7]
+      , bands[8]
+      , bands[9]);
+    }
+    
+    public bool deserialize_from_string(string data) {
+      bool good = false;
+      // call parser to build an array of string of parts
+      string[] values = CdlParser.ParseLine(data);
+      // check length of array
+      if (13 == values.length) {
+        // parse parts into this
+        name = values[0];
+        for (int i = 0; i < 10; ++i) {
+          bands[i] = values[i + 1].to_double();
+        }
+        good = true;
+      }
+      return good;
+    }
+  }
+
+  public struct RecordingSettings {
+    public string source;
+    public double pregain;
+    public double volume;
+    public DynamicsSettings dynamics;
+    
+    public RecordingSettings() {
+      this.source = "microphone";
+      this.pregain = 1.0;
+      this.volume = 1.0;
+      this.dynamics = DynamicsSettings();
+    }
+    public RecordingSettings.with_values(string source, double pregain, double volume, DynamicsSettings dynamics) {
+      this.source = source;
+      this.pregain = pregain;
+      this.volume = volume;
+      this.dynamics = dynamics;
+    }
+    public string to_xml_string() {
+      return ("<RecordingSettings>\n" +
+              "<source type=\"string\"><![CDATA[%s]]></source>\n" +
+              "<pregain type=\"double\">%02f</pregain>\n" +
+              "<volume type=\"double\">%02f</volume>\n" +
+              "<dynamics type=\"DynamicsSettings\">\n%s</dynamics>\n" +
+              "</RecordingSettings>\n").printf(
+                source,
+                pregain,
+                volume,
+                dynamics.to_xml_string()
+              );
+    }
+    public void from_xml_node_helper(Xml.Node* node) {
+      // RecordingSettings is below root
+      // Loop over the passed node's children
+      for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {
+        // Spaces between tags are also nodes, discard them
+        if (iter->type != Xml.ElementType.ELEMENT_NODE) {
+          continue;
+        }
+        // Get the node's name
+        if ("RecordingSettings" == iter->name) {
+          from_xml_node(iter);
+          break;
+        }
+      }
+    }
+    public void from_xml_node(Xml.Node* node) {
+      // RecordingSettings is root
+      // Loop over the passed node's children
+      for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {
+        // Spaces between tags are also nodes, discard them
+        if (iter->type != Xml.ElementType.ELEMENT_NODE) {
+          continue;
+        }
+        // Get the node's name
+        string node_name = iter->name;
+        // Get the nodes type
+        string? type = XmlHelpers.get_node_attribute(iter, "type");
+        // Get the node's content with <tags> stripped
+        if (null != type) {
+          // check the type
+          switch(type) {
+            case "string":
+              switch(node_name) {
+                case "source":
+                  this.source = iter->get_content();
+                  break;
+                default:
+                  break;
+              }
+              break;
+            case "double":
+              switch(node_name) {
+                case "pregain":
+                  this.pregain = iter->get_content().to_double();
+                  break;
+                case "volume":
+                  this.volume = iter->get_content().to_double();
+                  break;
+                default:
+                  break;
+              }
+              break;
+            case "DynamicsSettings":
+              switch(node_name) {
+                case "dynamics":
+                  this.dynamics.from_xml_node_helper(iter);
+                  break;
+                default:
+                  break;
+              }
+              break;
+            default:
+              continue;
+          }
+        }
+        
+      }
+    }
+  }
+  
+  public struct MonitorSettings {
+    public bool output_audio;
+    public RecordingSettings input;
+    
+    public MonitorSettings() {
+      this.output_audio = false;
+      this.input = RecordingSettings();
+    }
+    public MonitorSettings.with_values(bool output_audio, RecordingSettings input) {
+      this.output_audio = output_audio;
+      this.input = input;
+    }
+    public string to_xml_string() {
+      return ("<MonitorSettings>\n" +
+              "<output_audio type=\"bool\">%s</output_audio>\n" +
+              "<input type=\"RecordingSettings\">\n%s</input>\n" +
+              "</MonitorSettings>\n").printf(
+                output_audio.to_string(),
+                input.to_xml_string()
+              );
+    }
+    public void from_xml_node_helper(Xml.Node* node) {
+      // MonitorSettings is below root
+      // Loop over the passed node's children
+      for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {
+        // Spaces between tags are also nodes, discard them
+        if (iter->type != Xml.ElementType.ELEMENT_NODE) {
+          continue;
+        }
+        // Get the node's name
+        if ("MonitorSettings" == iter->name) {
+          from_xml_node(iter);
+          break;
+        }
+      }
+    }
+    public void from_xml_node(Xml.Node* node) {
+      // MonitorSettings is root
+      // Loop over the passed node's children
+      for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {
+        // Spaces between tags are also nodes, discard them
+        if (iter->type != Xml.ElementType.ELEMENT_NODE) {
+          continue;
+        }
+        // Get the node's name
+        string node_name = iter->name;
+        // Get the nodes type
+        string? type = XmlHelpers.get_node_attribute(iter, "type");
+        // Get the node's content with <tags> stripped
+        if (null != type) {
+          // check the type
+          switch(type) {
+            case "bool":
+              switch(node_name) {
+                case "output_audio":
+                  this.output_audio = iter->get_content().to_bool();
+                  break;
+                default:
+                  break;
+              }
+              break;
+            case "RecordingSettings":
+              switch(node_name) {
+                case "input":
+                  this.input.from_xml_node_helper(iter);
+                  break;
+                default:
+                  break;
+              }
+              break;
+            default:
+              continue;
+          }
+        }
+        
+      }
+    }
+  }
+
+  public struct TrackSettings {
+    public bool active;
+    public string uri;
+    public string name;
+    public string notes;
+    public VolumeSettings volume;
+    public PanoramaSettings panorama;
+    public DynamicsSettings dynamics;
+    public EchoSettings echo;
+    public EqualizerSettings equalizer;
+    
+    public TrackSettings() {
+      this.active = false;
+      this.uri = "";
+      this.name = "Unnamed";
+      this.notes = "";
+      this.volume = VolumeSettings();
+      this.panorama = PanoramaSettings();
+      this.dynamics = DynamicsSettings();
+      this.echo = EchoSettings();
+      this.equalizer = EqualizerSettings();
+    }
+    public TrackSettings.with_values(string uri, string name, string notes, bool active, VolumeSettings volume, PanoramaSettings panorama, DynamicsSettings dynamics, EchoSettings echo, EqualizerSettings equalizer) {
+      this.active = active;
+      this.uri = uri;
+      this.name = name;
+      this.notes = notes;
+      this.volume = volume;
+      this.panorama = panorama;
+      this.dynamics = dynamics;
+      this.echo = echo;
+      this.equalizer = equalizer;
+    }
+    public string to_xml_string() {
+      return ("<TrackSettings>\n" +
+              "<active type=\"bool\">%s</active>\n" +
+              "<uri type=\"string\"><![CDATA[%s]]></uri>\n" +
+              "<name type=\"string\"><![CDATA[%s]]></name>\n" +
+              "<notes type=\"string\"><![CDATA[%s]]></notes>\n" +
+              "<volume type=\"VolumeSettings\">\n%s</volume>\n" +
+              "<panorama type=\"PanoramaSettings\">\n%s</panorama>\n" +
+              "<dynamics type=\"DynamicsSettings\">\n%s</dynamics>\n" +
+              "<echo type=\"EchoSettings\">\n%s</echo>\n" +
+              "<equalizer type=\"EqualizerSettings\">\n%s</equalizer>\n" +
+              "</TrackSettings>\n").printf(
+                active.to_string(),
+                uri,
+                name,
+                notes,
+                volume.to_xml_string(),
+                panorama.to_xml_string(),
+                dynamics.to_xml_string(),
+                echo.to_xml_string(),
+                equalizer.to_xml_string()
+              );
+    }
+    public void from_xml_node_helper(Xml.Node* node) {
+      // TrackSettings is below root
+      // Loop over the passed node's children
+      for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {
+        // Spaces between tags are also nodes, discard them
+        if (iter->type != Xml.ElementType.ELEMENT_NODE) {
+          continue;
+        }
+        // Get the node's name
+        if ("TrackSettings" == iter->name) {
+          from_xml_node(iter);
+          break;
+        }
+      }
+    }
+    public void from_xml_node(Xml.Node* node) {
+      // TrackSettings is root
+      // Loop over the passed node's children
+      for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {
+        // Spaces between tags are also nodes, discard them
+        if (iter->type != Xml.ElementType.ELEMENT_NODE) {
+          continue;
+        }
+        // Get the node's name
+        string node_name = iter->name;
+        // Get the nodes type
+        string? type = XmlHelpers.get_node_attribute(iter, "type");
+        // Get the node's content with <tags> stripped
+        if (null != type) {
+          // check the type
+          switch(type) {
+            case "string":
+              switch(node_name) {
+                case "name":
+                  this.name = iter->get_content();
+                  break;
+                case "uri":
+                  this.uri = iter->get_content();
+                  break;
+                case "notes":
+                  this.notes = iter->get_content();
+                  break;
+                default:
+                  break;
+              }
+              break;
+            case "bool":
+              switch(node_name) {
+                case "active":
+                  this.active = iter->get_content().to_bool();
+                  break;
+                default:
+                  break;
+              }
+              break;
+            case "VolumeSettings":
+              switch(node_name) {
+                case "volume":
+                  this.volume.from_xml_node_helper(iter);
+                  break;
+                default:
+                  break;
+              }
+              break;
+            case "PanoramaSettings":
+              switch(node_name) {
+                case "panorama":
+                  this.panorama.from_xml_node_helper(iter);
+                  break;
+                default:
+                  break;
+              }
+              break;
+            case "DynamicsSettings":
+              switch(node_name) {
+                case "dynamics":
+                  this.dynamics.from_xml_node_helper(iter);
+                  break;
+                default:
+                  break;
+              }
+              break;
+            case "EchoSettings":
+              switch(node_name) {
+                case "echo":
+                  this.echo.from_xml_node_helper(iter);
+                  break;
+                default:
+                  break;
+              }
+              break;
+            case "EqualizerSettings":
+              switch(node_name) {
+                case "equalizer":
+                  this.equalizer.from_xml_node_helper(iter);
+                  break;
+                default:
+                  break;
+              }
+              break;
+            default:
+              continue;
+          }
+        }
+        
+      }
+    }
+  }
+
+  public struct MixerSettings {
+    public VolumeSettings volume;
+    public PanoramaSettings panorama;
+    public EqualizerSettings equalizer;
+    
+    public MixerSettings() {
+      this.volume = VolumeSettings();
+      this.panorama = PanoramaSettings();
+      this.equalizer = EqualizerSettings();
+    }
+    public MixerSettings.with_values(VolumeSettings volume, PanoramaSettings panorama, EqualizerSettings equalizer) {
+      this.volume = volume;
+      this.panorama = panorama;
+      this.equalizer = equalizer;
+    }
+    public string to_xml_string() {
+      return ("<MixerSettings>\n" +
+              "<volume type=\"VolumeSettings\">\n%s</volume>\n" +
+              "<panorama type=\"PanoramaSettings\">\n%s</panorama>\n" +
+              "<equalizer type=\"EqualizerSettings\">\n%s</equalizer>\n" +
+              "</MixerSettings>\n").printf(
+                volume.to_xml_string(),
+                panorama.to_xml_string(),
+                equalizer.to_xml_string()
+              );
+    }
+    public void from_xml_node_helper(Xml.Node* node) {
+      // MixerSettings is below root
+      // Loop over the passed node's children
+      for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {
+        // Spaces between tags are also nodes, discard them
+        if (iter->type != Xml.ElementType.ELEMENT_NODE) {
+          continue;
+        }
+        // Get the node's name
+        if ("MixerSettings" == iter->name) {
+          from_xml_node(iter);
+          break;
+        }
+      }
+    }
+    public void from_xml_node(Xml.Node* node) {
+      // MixerSettings is root
+      // Loop over the passed node's children
+      for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {
+        // Spaces between tags are also nodes, discard them
+        if (iter->type != Xml.ElementType.ELEMENT_NODE) {
+          continue;
+        }
+        // Get the node's name
+        string node_name = iter->name;
+        // Get the nodes type
+        string? type = XmlHelpers.get_node_attribute(iter, "type");
+        // Get the node's content with <tags> stripped
+        if (null != type) {
+          // check the type
+          switch(type) {
+              case "VolumeSettings":
+                switch(node_name) {
+                  case "volume":
+                    this.volume.from_xml_node_helper(iter);
+                    break;
+                  default:
+                    break;
+                }
+                break;
+              case "PanoramaSettings":
+                switch(node_name) {
+                  case "panorama":
+                    this.panorama.from_xml_node_helper(iter);
+                    break;
+                  default:
+                    break;
+                }
+                break;
+              case "EqualizerSettings":
+                switch(node_name) {
+                  case "equalizer":
+                    this.equalizer.from_xml_node_helper(iter);
+                    break;
+                  default:
+                    break;
+                }
+                break;
+              default:
+                continue;
+          }
+        }
+        
+      }
+    }
+  }
+  
+  public struct MixdownSettings {
+    public string encoder;
+    public string quality;
+    public string location;
+    public string artist;
+    public string title;
+    public string album;
+    public unowned List<Tagging.Tag?> tags;
+    
+    public MixdownSettings() {
+      this.encoder = "vorbisenc";
+      this.quality = "high";
+      this.location = "";
+      this.artist = "Unknown Artist";
+      this.title = "Untitled";
+      this.album = "Untitled";
+      this.tags = null;
+    }
+    public MixdownSettings.with_values(string encoder, string quality, string location, string artist, string title, string album, List<Tagging.Tag?> tags) {
+      this.encoder = encoder;
+      this.quality = quality;
+      this.location = location;
+      this.artist = artist;
+      this.title = title;
+      this.album = album;
+      this.tags = tags;
+    }
+    public string to_xml_string() {
+      return ("<MixdownSettings>\n" +
+              "<encoder type=\"string\"><![CDATA[%s]]></encoder>\n" +
+              "<quality type=\"string\"><![CDATA[%s]]></quality>\n" +
+              "<location type=\"string\"><![CDATA[%s]]></location>\n" +
+              "<artist type=\"string\"><![CDATA[%s]]></artist>\n" +
+              "<title type=\"string\"><![CDATA[%s]]></title>\n" +
+              "<album type=\"string\"><![CDATA[%s]]></album>\n" +
+              "<tags type=\"TagList\">\n%s</tags>\n" +
+              "</MixdownSettings>\n").printf(
+                encoder,
+                quality,
+                location,
+                artist,
+                title,
+                album,
+                Tagging.Tag.taglist_to_xml_string(tags)
+              );
+    }
+    public void from_xml_node_helper(Xml.Node* node) {
+      // MixdownSettings is below root
+      // Loop over the passed node's children
+      for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {
+        // Spaces between tags are also nodes, discard them
+        if (iter->type != Xml.ElementType.ELEMENT_NODE) {
+          continue;
+        }
+        // Get the node's name
+        if ("MixdownSettings" == iter->name) {
+          from_xml_node(iter);
+          break;
+        }
+      }
+    }
+    public void from_xml_node(Xml.Node* node) {
+      // MixdownSettings is root
+      // Loop over the passed node's children
+      for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {
+        // Spaces between tags are also nodes, discard them
+        if (iter->type != Xml.ElementType.ELEMENT_NODE) {
+          continue;
+        }
+        // Get the node's name
+        string node_name = iter->name;
+        // Get the nodes type
+        string? type = XmlHelpers.get_node_attribute(iter, "type");
+        // Get the node's content with <tags> stripped
+        if (null != type) {
+          // check the type
+          switch(type) {
+            case "string":
+              switch(node_name) {
+                case "encoder":
+                  this.encoder = iter->get_content();
+                  break;
+                case "quality":
+                  this.quality = iter->get_content();
+                  break;
+                case "location":
+                  this.location = iter->get_content();
+                  break;
+                case "artist":
+                  this.artist = iter->get_content();
+                  break;
+                case "title":
+                  this.title = iter->get_content();
+                  break;
+                case "album":
+                  this.album = iter->get_content();
+                  break;
+                default:
+                  break;
+              }
+              break;
+            case "TagList":
+              switch(node_name) {
+                case "tags":
+                  Tagging.Tag.taglist_from_xml_node_helper(iter, ref this.tags);
+                  break;
+                default:
+                  break;
+              }
+              break;
+            default:
+              continue;
+          }
+        }
+        
+      }
+    }
+  }
+
+  public struct ProjectSettings {
+    public string location;
+    public string name;
+    public string working_directory;
+    public int last_track_number;
+    public MonitorSettings monitor;
+    public MixerSettings mixer;
+    public MixdownSettings mixdown;
+    public unowned List<Track?> tracks;
+    
+    public ProjectSettings() {
+      this.name = "New Project";
+      this.location = "";
+      this.working_directory = "";
+      this.last_track_number = 0;
+      this.monitor = MonitorSettings();
+      this.mixer = MixerSettings();
+      this.mixdown = MixdownSettings();
+      this.tracks = null;
+    }
+    public ProjectSettings.with_values(string name, string location, string working_directory, int last_track_number, MonitorSettings monitor, MixerSettings mixer, MixdownSettings mixdown, List<Track?> tracks) {
+      this.name = name;
+      this.location = location;
+      this.working_directory = working_directory;
+      this.last_track_number = last_track_number;
+      this.monitor = monitor;
+      this.mixer = mixer;
+      this.mixdown = mixdown;
+      this.tracks = tracks;
+    }
+    public string to_xml_string() {
+      return ("<ProjectSettings version=\"1.0\">\n" +
+              "<name type=\"string\"><![CDATA[%s]]></name>\n" +
+              "<location type=\"string\"><![CDATA[%s]]></location>\n" +
+              "<working_directory type=\"string\"><![CDATA[%s]]></working_directory>\n" +
+              "<last_track_number type=\"int\">%i</last_track_number>\n" +
+              "<monitor type=\"MonitorSettings\">\n%s</monitor>\n" +
+              "<mixer type=\"MixerSettings\">\n%s</mixer>\n" +
+              "<mixdown type=\"MixdownSettings\">\n%s</mixdown>\n" +
+              "<tracks type=\"TrackList\">\n%s</tracks>\n" +
+              "</ProjectSettings>\n").printf(
+                name,
+                location,
+                working_directory,
+                last_track_number,
+                monitor.to_xml_string(),
+                mixer.to_xml_string(),
+                mixdown.to_xml_string(),
+                Track.tracklist_to_xml_string(tracks)
+              );
+    }
+    public void from_xml_node(Xml.Node* node) {
+      // ProjectSettings is root
+      // Loop over the passed node's children
+      for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {
+        // Spaces between tags are also nodes, discard them
+        if (iter->type != Xml.ElementType.ELEMENT_NODE) {
+          continue;
+        }
+        // Get the node's name
+        string node_name = iter->name;
+        // Get the nodes type
+        string? type = XmlHelpers.get_node_attribute(iter, "type");
+        // Get the node's content with <tags> stripped
+        if (null != type) {
+          // check the type
+          switch(type) {
+            case "string":
+              switch(node_name) {
+                case "name":
+                  this.name = iter->get_content();
+                  break;
+                case "location":
+                  this.location = iter->get_content();
+                  break;
+                case "working_directory":
+                  this.working_directory = iter->get_content();
+                  break;
+                default:
+                  break;
+              }
+              break;
+            case "int":
+              switch(node_name) {
+                case "last_track_number":
+                  this.last_track_number = iter->get_content().to_int();
+                  break;
+                default:
+                  break;
+              }
+              break;
+            case "bool":
+              switch(node_name) {
+                default:
+                  break;
+              }
+              break;
+            case "double":
+              switch(node_name) {
+                default:
+                  break;
+              }
+              break;
+            case "uint64":
+              switch(node_name) {
+                default:
+                  break;
+              }
+              break;
+            case "MonitorSettings":
+              switch(node_name) {
+                case "monitor":
+                    this.monitor.from_xml_node_helper(iter);
+                    break;
+                  default:
+                    break;
+                }
+              break;
+            case "MixerSettings":
+                switch(node_name) {
+                  case "mixer":
+                    this.mixer.from_xml_node_helper(iter);
+                    break;
+                  default:
+                    break;
+                }
+              break;
+            case "MixdownSettings":
+                switch(node_name) {
+                  case "mixdown":
+                    this.mixdown.from_xml_node_helper(iter);
+                    break;
+                  default:
+                    break;
+                }
+              break;
+            case "TrackList":
+                switch(node_name) {
+                  case "tracks":
+                    Track.tracklist_from_xml_node_helper(iter, ref this.tracks);
+                    break;
+                  default:
+                    break;
+                }
+              break;
+            default:
+              continue;
+          }
+        }
+        
+      }
+    }
+  }
+
+  }
+
+}
\ No newline at end of file
diff --git a/src/SimpleHashMap.vala b/src/SimpleHashMap.vala
new file mode 100644 (file)
index 0000000..f8445be
--- /dev/null
@@ -0,0 +1,67 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+namespace IdWorks {
+
+public class SimpleHashMap<T> : Object {
+
+  List<string> _keys;
+  List<T> _values;
+  
+  public List<string> keys {
+    get {
+      return _keys;
+    }
+  }
+  
+  public List<T> values {
+    get {
+      return _values;
+    }
+  }
+  
+  construct {
+    _keys = new List<string>();
+    _values = new List<T>();
+  }
+  
+  public new void set(string key, T val) {
+    for (int idx = 0; idx < _keys.length(); ++idx) {
+      if (key == _keys.nth_data(idx)) {
+        // delete value at idx
+        _values.delete_link(_values.nth(idx));
+        // insert value at idx
+        _values.insert(val, idx);
+        return;
+      }
+    }
+    _keys.append(key);
+    _values.append(val);
+  }
+  
+  public new T get(string key) {
+    for (int idx = 0; idx < _keys.length(); ++idx) {
+      if (key == _keys.nth_data(idx)) {
+        return _values.nth_data(idx);
+      }
+    }
+    return null;
+  }
+  
+}
+
+}
\ No newline at end of file
diff --git a/src/Tagging.vala b/src/Tagging.vala
new file mode 100644 (file)
index 0000000..9947fa5
--- /dev/null
@@ -0,0 +1,56 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+namespace IdWorks {
+
+  namespace Tagging {
+  
+    public struct Tag {
+      string tag;
+      GLib.Value value;
+      public string to_xml_string() {
+        return ("<Tag>\n" +
+                "<tag type=\"%s\">%s</tag>\n" +
+                "<value type=\"Value\">%s</value>\n" +
+                "</Tag>\n").printf(
+                  value.type_name(), tag
+                  , value.strdup_contents()
+                );
+      }
+      public void from_xml_node(Xml.Node* node) {
+        // Tag is root
+      }
+      public void from_xml_node_helper(Xml.Node* node) {
+        // Tag is below root
+      }
+      public static string taglist_to_xml_string(List<Tag?> taglist) {
+        string ret = "<TagList>\n";
+        for (int idx = 0; idx < taglist.length(); ++idx) {
+          ret += ((Tagging.Tag)taglist.nth_data(idx)).to_xml_string();
+        }
+        return ret + "</TagList>\n";
+      }
+      public static void taglist_from_xml_node_helper(Xml.Node* node, ref unowned List<Tag?> taglist) {
+      }
+      
+      public static void taglist_from_xml_node(Xml.Node* node, ref unowned List<Tag?> taglist) {
+      }
+    }
+  
+  }
+
+}
\ No newline at end of file
diff --git a/src/Track.vala b/src/Track.vala
new file mode 100644 (file)
index 0000000..d10a7fc
--- /dev/null
@@ -0,0 +1,130 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+namespace IdWorks {
+
+protected struct Track {
+
+  public string uri;
+  public string label;
+  public bool active;
+  public bool mute;
+  public double volume;
+  public double pan;
+  public TrackBin bin;
+  //public TrackTransport transport;
+  
+  public Track() {
+    this.uri = "";
+    this.label = "";
+    this.active = false;
+    this.mute = false;
+    this.volume = 0.0;
+    this.pan = 0.0;
+    this.bin = null;
+    //this.transport = null;
+  }
+  
+  public Track.with_uri(string uri, string label, bool active, bool mute, double volume, double pan) {
+    this.uri = uri;
+    this.label = label;
+    this.active = active;
+    this.mute = mute;
+    this.volume = volume;
+    this.pan = pan;
+    this.bin = new TrackBin(label);
+    this.bin.set_uri(uri);
+    //this.transport = null;
+  }
+  
+  //public void set_transport(TrackTransport transport) {
+    //this.transport = transport;
+    //this.transport.active_toggled.connect(active_toggled_callback);
+  //}
+  
+  public SettingsStructures.TrackSettings get_settings_structure() {
+    SettingsStructures.TrackSettings ret = SettingsStructures.TrackSettings();
+    ret.uri = uri;
+    ret.name = label;
+    ret.notes = "";
+    ret.active = active;
+    ret.volume = SettingsStructures.VolumeSettings.with_values(volume, mute);
+    ret.panorama = SettingsStructures.PanoramaSettings.with_values(pan);
+    ret.dynamics = SettingsStructures.DynamicsSettings();
+    ret.echo = SettingsStructures.EchoSettings();
+    ret.equalizer = SettingsStructures.EqualizerSettings();
+    return ret;
+  }
+  public void from_settings_structure(SettingsStructures.TrackSettings settings) {
+    this.uri = settings.uri;
+    this.label = settings.name;
+    this.active = settings.active;
+    this.mute = settings.volume.mute;
+    this.volume = settings.volume.volume;
+    this.pan = settings.panorama.panorama;
+    this.bin = new TrackBin(this.label);
+    this.bin.set_uri(this.uri);
+  }
+  
+  public static string tracklist_to_xml_string(List<Track?> tracklist) {
+    string ret = "<TrackList>\n";
+    for (int idx = 0; idx < tracklist.length(); ++idx) {
+      ret += ((Track)tracklist.nth_data(idx)).get_settings_structure().to_xml_string();
+    }
+    return ret + "</TrackList>\n";
+  }
+  
+  public static void tracklist_from_xml_node_helper(Xml.Node* node, ref unowned List<Track?> tracks) {
+    // Loop over the passed node's children
+    for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {
+      // Spaces between tags are also nodes, discard them
+      if (iter->type != Xml.ElementType.ELEMENT_NODE) {
+        continue;
+      }
+      // Get the node's name
+      if ("TrackList" == iter->name) {
+        tracklist_from_xml_node(iter, ref tracks);
+        break;
+      }
+    }
+  }
+  
+  public static void tracklist_from_xml_node(Xml.Node* node, ref unowned List<Track?> tracks) {
+    // Loop over the passed node's children
+    for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {
+      // Spaces between tags are also nodes, discard them
+      if (iter->type != Xml.ElementType.ELEMENT_NODE) {
+        continue;
+      }
+      // Get the node's name
+      string node_name = iter->name;
+      // Get the nodes type
+      //string? type = XmlHelpers.get_node_attribute(iter, "type");
+      if ("TrackSettings" == node_name)
+      {
+        SettingsStructures.TrackSettings track = SettingsStructures.TrackSettings();
+        track.from_xml_node(iter);
+        Track t = Track();
+        t.from_settings_structure(track);
+        tracks.append(t);
+      }
+    }
+  }
+
+}
+
+}
diff --git a/src/TrackBin.vala b/src/TrackBin.vala
new file mode 100644 (file)
index 0000000..9720df0
--- /dev/null
@@ -0,0 +1,268 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+namespace IdWorks {
+
+public class TrackBin: Gst.Bin {
+
+  Gst.Element decoder;
+  Gst.Element converter;
+  Gst.Element dynamics;
+  Gst.Element equalizer;
+  Gst.Element echo;
+  Gst.Element volume;
+  Gst.Element panorama;
+  Gst.GhostPad sink_pad;
+  Gst.GhostPad src_pad;
+  
+  public TrackBin(string name) {
+    this.set_name(name);
+    
+    decoder = Gst.ElementFactory.make("uridecodebin", "decoder-" + name);
+    decoder.connect("swapped-object-signal::autoplug-continue", autoplug_continue, this);
+    decoder.connect("swapped-object-signal::drained", drained, this);
+    this.add(decoder);
+    
+    converter = Gst.ElementFactory.make("audioconvert", "converter-" + name);
+    this.add(converter);
+    
+    dynamics = Gst.ElementFactory.make("audiodynamic", "dynamics");
+    dynamics.set_property("mode", "compressor"); // compression
+    dynamics.set_property("characteristics", "soft-knee"); // compression
+    dynamics.set_property("threshold", 1); // shouldn't kick in
+    dynamics.set_property("ratio", 100); // shouldn't be applied
+    this.add(dynamics);
+    
+    equalizer = Gst.ElementFactory.make("equalizer-10bands", "equalizer-" + name);
+    this.add(equalizer);
+    
+    echo = Gst.ElementFactory.make("audioecho", "echo");
+    echo.set_property("delay", 0);
+    echo.set_property("feedback", 0);
+    echo.set_property("intensity", 0);
+    echo.set_property("max-delay", 1 * Time.Nanoseconds.SECOND);
+    this.add(echo);
+    
+    volume = Gst.ElementFactory.make("volume", "volume-" + name);
+    volume.set_property("volume", 1);
+    this.add(volume);
+    
+    panorama = Gst.ElementFactory.make("audiopanorama", "panorama-" + name);
+    panorama.set_property("panorama", 0);
+    this.add(panorama);
+    
+    converter.link(dynamics);
+    dynamics.link(equalizer);
+    equalizer.link(echo);
+    echo.link(panorama);
+    panorama.link(volume);
+    
+    sink_pad = new Gst.GhostPad("sink", decoder.get_static_pad("sink"));
+    this.add_pad(sink_pad);
+    src_pad = new Gst.GhostPad("src", volume.get_static_pad("src"));
+    this.add_pad(src_pad);
+    
+  }
+  /*
+  public void volume_updated_callback(TrackTransport sender, double volume) {
+    this.set_volume(volume);
+  }
+  
+  public void panorama_updated_callback(TrackTransport sender, double panorama) {
+    this.set_panorama(panorama);
+  }
+  
+  public void eq_updated_callback(TrackTransport sender, int band, double val) {
+    this.set_eq(band, val);
+  }
+  */
+  private bool autoplug_continue(Gst.Pad decodebin, Gst.Caps arg1, void* data) {
+    Gst.PadLinkReturn ret = decodebin.link(converter.get_static_pad("sink"));
+    if (Gst.PadLinkReturn.OK != ret) {
+      //stdout.printf("link failed: %s %s\n", this.get_name(), ret.to_string());
+      //stdout.flush();
+    }
+    return true;
+  }
+  
+  private void drained(Gst.Pad decodebin, void* data) {
+  }
+  
+  public new Gst.PadTemplate get_pad_template(string name) {
+    switch (name) {
+      case "src":
+        return this.volume.get_pad_template("src");
+      case "sink":
+        return this.decoder.get_pad_template("sink");
+      default:
+        return this.get_pad_template(name);
+    }
+  }
+  
+  public void play() {
+    this.set_state(Gst.State.PLAYING);
+  }
+  
+  public void pause() {
+    this.set_state(Gst.State.PAUSED);
+  }
+  
+  public void stop() {
+    this.set_state(Gst.State.READY);
+  }
+  
+  public void set_uri(string uri) {
+    this.set_state(Gst.State.NULL);
+    decoder.set_property("uri", uri);
+  }
+  public string get_uri() {
+    GLib.Value ret = 0.0;
+    decoder.get_property("uri", ref ret);
+    return ret.get_string();
+  }
+  
+  public void set_volume(double val)
+  requires (val >= 0.0 && val <= 10.0) {
+    volume.set_property("volume", val);
+  }
+  public double get_volume() {
+    GLib.Value ret = 0.0;
+    volume.get_property("volume", ref ret);
+    return ret.get_double();
+  }
+  
+  public void set_panorama(double val) 
+  requires (val >= -1 && val <= 1) {
+    panorama.set_property("panorama", val);
+  }
+  public double get_panorama() {
+    GLib.Value ret = 0.0;
+    panorama.get_property("panorama", ref ret);
+    return ret.get_double();
+  }
+  
+  public void set_eq(int band, double val)
+  requires (band > -1 && band < 10)
+  requires (val >= -24 && val <= 12) {
+    equalizer.set_property("band" + band.to_string(), val);
+  }
+  public double get_eq(int band) {
+    GLib.Value ret = 0.0;
+    equalizer.get_property("band" + band.to_string(), ref ret);
+    return ret.get_double();
+  }
+  
+  public void set_echo_delay(uint64 val) {
+    echo.set_property("delay", val);
+  }
+  public uint64 get_echo_delay() {
+    GLib.Value ret = 0.0;
+    echo.get_property("delay", ref ret);
+    return ret.get_uint64();
+  }
+  
+  public void set_echo_max_delay(uint64 val) {
+    echo.set_property("max-delay", val);
+  }
+  public uint64 get_echo_max_delay() {
+    GLib.Value ret = 0.0;
+    echo.get_property("max-delay", ref ret);
+    return ret.get_uint64();
+  }
+  
+  public void set_echo_feedback(double val) {
+    echo.set_property("feedback", val);
+  }
+  public double get_echo_feedback() {
+    GLib.Value ret = 0.0;
+    echo.get_property("feedback", ref ret);
+    return ret.get_double();
+  }
+  
+  public void set_echo_intensity(double val) {
+    echo.set_property("intensity", val);
+  }
+  public double get_echo_intensity() {
+    GLib.Value ret = 0.0;
+    echo.get_property("intensity", ref ret);
+    return ret.get_double();
+  }
+  
+  public void set_echo_active(bool val) {
+    if (val) {}
+    else {
+      set_echo_delay(0);
+      set_echo_feedback(0);
+      set_echo_intensity(0);
+    }
+  }
+  public bool get_echo_active() {
+    return true;
+  }
+  
+  public void set_dynamics_mode(string val) {
+    dynamics.set_property("mode", val);
+  }
+  public string get_dynamics_mode() {
+    GLib.Value ret = 0.0;
+    dynamics.get_property("mode", ref ret);
+    return ret.get_string();
+  }
+  
+  public void set_dynamics_characteristics(string val) {
+    dynamics.set_property("characteristics", val);
+  }
+  public string get_dynamics_characteristics() {
+    GLib.Value ret = 0.0;
+    dynamics.get_property("characteristics", ref ret);
+    return ret.get_string();
+  }
+  
+  public void set_dynamics_threshold(double val) {
+    dynamics.set_property("threshold", val);
+  }
+  public double get_dynamics_threshold() {
+    GLib.Value ret = 0.0;
+    dynamics.get_property("threshold", ref ret);
+    return ret.get_double();
+  }
+  
+  public void set_dynamics_ratio(double val) {
+    dynamics.set_property("ratio", val);
+  }
+  public double get_dynamics_ratio() {
+    GLib.Value ret = 0.0;
+    dynamics.get_property("ratio", ref ret);
+    return ret.get_double();
+  }
+  
+  public void set_dynamics_active(bool val) {
+    if (val) {}
+    else {
+      set_dynamics_mode("compressor");
+      set_dynamics_characteristics("soft-knee");
+      set_dynamics_ratio(1);
+      set_dynamics_threshold(0);
+    }
+  }
+  public bool get_dynamics_active() {
+    return true;
+  }
+  
+}
+
+}
\ No newline at end of file
diff --git a/src/TrackEffectsPopUp.vala b/src/TrackEffectsPopUp.vala
new file mode 100644 (file)
index 0000000..ec4cdf7
--- /dev/null
@@ -0,0 +1,179 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+namespace IdWorks {
+
+public class TrackEffectsPopUp : Hildon.Dialog {
+
+  SettingsStructures.EchoSettings echo_settings;
+  SettingsStructures.DynamicsSettings dynamics_settings;
+  
+  Hildon.Button dynamics_settings_btn;
+  Hildon.CheckButton dynamics_btn;
+  Hildon.Button echo_settings_btn;
+  Hildon.CheckButton echo_btn;
+  
+  public signal void dynamics_toggled(bool state);
+  public signal void dynamics_mode_updated(string val);
+  public signal void dynamics_characteristics_updated(string val);
+  public signal void dynamics_ratio_updated(double val);
+  public signal void dynamics_threshold_updated(double val);
+  
+  public signal void echo_toggled(bool state);
+  public signal void echo_max_delay_updated(uint64 val);
+  public signal void echo_delay_updated(uint64 val);
+  public signal void echo_feedback_updated(double val);
+  public signal void echo_intensity_updated(double val);
+  
+  public TrackEffectsPopUp(string title, Gtk.Widget parent) {
+    this.set_title(title);
+    this.set_parent(parent);
+    this.echo_settings = SettingsStructures.EchoSettings();
+    this.dynamics_settings = SettingsStructures.DynamicsSettings();
+    this.construct_interface();
+  }
+  
+  public TrackEffectsPopUp.with_settings(string title, Gtk.Widget parent, SettingsStructures.EchoSettings echo_settings, SettingsStructures.DynamicsSettings dynamics_settings) {
+    this.set_title(title);
+    this.set_parent(parent);
+    this.echo_settings = echo_settings;
+    this.dynamics_settings = dynamics_settings;
+    this.construct_interface();
+  }
+  
+  private void construct_interface() {    
+    this.set_default_response(Gtk.ResponseType.ACCEPT);
+    this.add_button(Gtk.STOCK_CLOSE, Gtk.ResponseType.ACCEPT);
+    
+    Gtk.VBox control_area = (Gtk.VBox)this.get_content_area();
+    
+    Gtk.HBox dynamics_bar = new Gtk.HBox(false, 4);
+    dynamics_btn = new Hildon.CheckButton(Hildon.SizeType.FINGER_HEIGHT | Hildon.SizeType.HALFSCREEN_WIDTH);
+    dynamics_btn.set_label("Dynamics");
+    dynamics_btn.toggled.connect( (s) => {toggle_dynamics(s.get_active());} );
+    dynamics_bar.pack_start(dynamics_btn, true, false, 2);
+    dynamics_settings_btn = new Hildon.Button(Hildon.SizeType.FINGER_HEIGHT | Hildon.SizeType.AUTO_WIDTH, Hildon.ButtonArrangement.HORIZONTAL);
+    dynamics_settings_btn.set_text("Settings", "");
+    dynamics_settings_btn.set_image(new Gtk.Image.from_icon_name("general_settings", Gtk.IconSize.BUTTON));
+    dynamics_settings_btn.set_image_position(Gtk.PositionType.LEFT);
+    dynamics_settings_btn.clicked.connect(do_dynamics_popup);
+    dynamics_bar.pack_start(dynamics_settings_btn, true, false, 2);
+    
+    control_area.pack_start(dynamics_bar, true, true , 2);
+    
+    Gtk.HBox echo_bar = new Gtk.HBox(false, 4);
+    echo_btn = new Hildon.CheckButton(Hildon.SizeType.FINGER_HEIGHT | Hildon.SizeType.HALFSCREEN_WIDTH);
+    echo_btn.set_label("Echo / Reverb");
+    echo_btn.toggled.connect( (s) => {toggle_echo(s.get_active());} );
+    echo_bar.pack_start(echo_btn, true, false, 2);
+    echo_settings_btn = new Hildon.Button(Hildon.SizeType.FINGER_HEIGHT | Hildon.SizeType.AUTO_WIDTH, Hildon.ButtonArrangement.HORIZONTAL);
+    echo_settings_btn.set_text("Settings", "");
+    echo_settings_btn.set_image(new Gtk.Image.from_icon_name("general_settings", Gtk.IconSize.BUTTON));
+    echo_settings_btn.set_image_position(Gtk.PositionType.LEFT);
+    echo_settings_btn.clicked.connect(do_echo_popup);
+    echo_bar.pack_start(echo_settings_btn, true, false, 2);
+    
+    control_area.pack_start(echo_bar, true, true , 2);
+    
+    
+    this.show_all();
+  }
+  
+  private void toggle_dynamics(bool state) {
+    if (!state) {
+      dynamics_settings_btn.set_sensitive(false);
+      // reset to pass through
+      dynamics_toggled(false);
+    }
+    else {
+      dynamics_settings_btn.set_sensitive(true);
+      dynamics_toggled(true);
+    }
+  }
+  
+  private void do_dynamics_popup() {
+    DynamicsPopUp dlg = new DynamicsPopUp.with_settings("Track Dynamics settings", this, this.dynamics_settings);
+    dlg.set_transient_for(this.get_transient_for());
+    dlg.mode_updated.connect(dynamics_update_mode_callback);
+    dlg.characteristics_updated.connect(dynamics_update_characteristics_callback);
+    dlg.ratio_updated.connect(dynamics_update_ratio_callback);
+    dlg.threshold_updated.connect(dynamics_update_threshold_callback);
+    dlg.run();
+    dlg.destroy();
+  }
+  private void dynamics_update_mode_callback(DynamicsPopUp sender, string val) {
+    dynamics_settings.mode = val;
+    // emit signal
+    dynamics_mode_updated(val);
+  }
+  private void dynamics_update_characteristics_callback(DynamicsPopUp sender, string val) {
+    dynamics_settings.characteristics = val;
+    // emit signal
+    dynamics_characteristics_updated(val);
+  }
+  private void dynamics_update_ratio_callback(DynamicsPopUp sender, double val) {
+    dynamics_settings.ratio = val;
+    // emit callback
+    dynamics_ratio_updated(val);
+  }
+  private void dynamics_update_threshold_callback(DynamicsPopUp sender, double val) {
+    dynamics_settings.threshold = val;
+    // emit signal
+    dynamics_threshold_updated(val);
+  }
+  
+  private void toggle_echo(bool state) {
+    if (!state) {
+      echo_settings_btn.set_sensitive(false);
+      // reset to pass through
+      echo_toggled(false);
+      
+    }
+    else {
+      echo_settings_btn.set_sensitive(true);
+      echo_toggled(true);
+    }
+  }
+  
+  private void do_echo_popup() {
+    EchoPopUp dlg = new EchoPopUp.with_settings("Track Echo / Reverb settings", this, this.echo_settings);
+    dlg.set_transient_for(this.get_transient_for());
+    dlg.delay_updated.connect(echo_update_delay_callback);
+    dlg.feedback_updated.connect(echo_update_feedback_callback);
+    dlg.intensity_updated.connect(echo_update_intensity_callback);
+    dlg.run();
+    dlg.destroy();
+  }
+  private void echo_update_delay_callback(EchoPopUp sender, uint64 val) {
+    echo_settings.delay = val;
+    // emit signal
+    echo_delay_updated(val);
+  }
+  private void echo_update_feedback_callback(EchoPopUp sender, double val) {
+    echo_settings.feedback = val;
+    // emit signal
+    echo_feedback_updated(val);
+  }
+  private void echo_update_intensity_callback(EchoPopUp sender, double val) {
+    echo_settings.intensity = val;
+    // emit signal
+    echo_intensity_updated(val);
+  }
+
+}
+
+}
\ No newline at end of file
diff --git a/src/TrackPipeline.vala b/src/TrackPipeline.vala
new file mode 100644 (file)
index 0000000..7870696
--- /dev/null
@@ -0,0 +1,313 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+namespace IdWorks {
+
+public class TrackPipeline : GLib.Object {
+  
+  Gst.Bin bin;
+  Gst.Element src;
+  Gst.Element decoder;
+  Gst.Element converter;
+  Gst.Element equalizer;
+  Gst.Element panorama;
+  Gst.Element volume;
+  Gst.Element sink;
+  string track_title;
+  Gst.Query duration_query;
+  Gst.Query position_query;
+  int64 position;
+  int64 duration;
+  
+  public void set_sink(Gst.Element sink) {
+    this.sink = sink;
+  }
+  
+  public void link_track(Gst.Element adder) {
+    volume.link(adder);
+  }
+  
+  //define a delegate function to handle tags
+  private static Gst.TagForeachFunc handle_tags_delegate;
+  
+  public signal void position_duration(int64 position, int64 duration);
+  public signal void tag_parsed(string tag, string val);
+  public signal void end_of_stream();
+  public signal void stream_error(string msg);
+  
+  public TrackPipeline(string name) {
+    //set the tag handling delegate
+    this.handle_tags_delegate = this.handle_tags;
+    //set some default values
+    this.position=0;
+    this.duration=0;
+    this.construct_bin(name);
+    //define the queries
+    duration_query = new Gst.Query.duration(Gst.Format.TIME);
+    position_query = new Gst.Query.position(Gst.Format.TIME);
+  }
+  
+  private void construct_bin(string name) {
+    bin      = new Gst.Bin(name);
+    src      = Gst.ElementFactory.make("fakesrc", "source");
+    bin.add(src);
+    decoder  = Gst.ElementFactory.make("decodebin", "decoder");
+    decoder.connect ("swapped-object-signal::new-decoded-pad", new_decoded_pad, this);
+    bin.add(decoder);
+    converter = Gst.ElementFactory.make("audioconvert", "converter");
+    bin.add(converter);
+    panorama = Gst.ElementFactory.make("audiopanorama", "panorama");
+    bin.add(panorama);
+    //echo     = ElementFactory.make("audioecho", "echo");
+    //echo.set_property("delay", 500000000);
+    //echo.set_property("feedback", 0.4);
+    //echo.set_property("intensity", 0.5);
+    //echo.set_property("max-delay", 500000000);
+    //pipeline.add(echo);
+    //converter2 = ElementFactory.make("audioconvert", "converter2");
+    //pipeline.add(converter2);
+    equalizer  = Gst.ElementFactory.make("equalizer-10bands", "eq");
+    bin.add(equalizer);
+    volume    = Gst.ElementFactory.make("volume", "vol");
+    bin.add(volume);
+    sink     = Gst.ElementFactory.make("autoaudiosink", "audio-output");
+    /* TODO How does bin handle this */ //pipeline.add(sink);
+    converter.link(equalizer);
+    equalizer.link(panorama);
+    panorama.link(volume);
+    /* TODO How does bin handle this */ //volume.link(sink);
+    //we need to receive signals from the pipelines bus
+    Gst.Bus bus = this.bin.get_bus( );
+    //make sure we are watching the signals on the bus
+    bus.add_signal_watch();
+    //what do we do when a tag is part of the bus signal?
+    bus.message.connect(
+      (bus,message)=> {
+        this.bus_message(message);
+      }
+    );
+  }
+  
+  
+  public void foreach_tag (Gst.TagList list, string tag) {
+    stdout.printf("foreach_tag called\n");
+    stdout.flush();
+    switch (tag) { 
+      case "title":
+        list.get_string (tag, out track_title);
+        stdout.printf ("%s\n", track_title);
+        stdout.flush();
+        //info_label.set_markup("<b>" + track_title + "</b><br>" + track_uri);
+        break;
+      default:
+        break;
+    }
+  }
+  
+  public void handle_tags(Gst.TagList list, string tag) {
+    if (tag != null) {
+      switch (tag) {
+        case "artist":
+        case "title":
+        case "album":
+        case "channel-mode":
+        case "comment":
+        case "audio-codec":
+          string val="Unknown";
+          list.get_string(tag,out val);
+          tag_parsed(tag, val);
+          break;
+        case "track-number":
+        case "bitrate":
+          uint val;
+          list.get_uint(tag,out val);
+          //tag_parsed(tag,(string)val);
+          break;
+        default:
+          //stdout.printf("%s\n",tag);
+          //stdout.flush();
+          break;
+      }
+    }
+  }
+  
+  
+  public void bus_message(Gst.Message message) {
+    Gst.TagList tag_list;
+    switch(message.type) {
+      case Gst.MessageType.ERROR:
+        GLib.Error err;
+        string debug;
+        message.parse_error (out err, out debug);
+        stream_error(err.message);
+        break;
+      case Gst.MessageType.TAG:
+        message.parse_tag(out tag_list);
+        //we need to get the key and value from the tag
+        tag_list.foreach( this.handle_tags_delegate );
+        break;
+      case Gst.MessageType.STATE_CHANGED:
+        //stdout.printf("state changed\n");
+        Gst.State oldstate;
+        Gst.State newstate;
+        Gst.State pending;
+        message.parse_state_changed (out oldstate, out newstate, out pending);
+        //stdout.printf ("%s->%s:%s\n", oldstate.to_string(), newstate.to_string(), pending.to_string());
+        //stdout.flush();
+        break;
+      case Gst.MessageType.EOS:
+        end_of_stream();
+        break;
+      default:
+        break;
+    }
+  }
+  
+  public bool bus_callback (Gst.Bus bus, Gst.Message message) {
+    stdout.printf("state changed\n");
+    stdout.flush();
+    switch (message.type) {
+      case Gst.MessageType.ERROR:
+        GLib.Error err;
+        string debug;
+        message.parse_error (out err, out debug);
+        stdout.printf("%s\n", err.message);
+        stdout.flush();
+        break;
+      case Gst.MessageType.EOS:
+        stdout.printf ("end of stream\n");
+        stdout.flush();
+        break;
+      case Gst.MessageType.STATE_CHANGED:
+        Gst.State oldstate;
+        Gst.State newstate;
+        Gst.State pending;
+        message.parse_state_changed (out oldstate, out newstate, out pending);
+        stdout.printf ("%s->%s:%s\n", oldstate.to_string(), newstate.to_string(), pending.to_string());
+        stdout.flush();
+        break;
+      case Gst.MessageType.TAG:
+        Gst.TagList tag_list;
+        message.parse_tag (out tag_list);
+        tag_list.foreach (foreach_tag);
+        break;
+      default:
+        break;
+    }
+    return true;
+  }
+  
+  public void get_duration_info() {
+    bool duration_result,position_result;
+    Gst.Format format = Gst.Format.TIME;
+    duration_result = this.bin.query(this.duration_query);
+    position_result = this.bin.query(this.position_query);
+    if ( duration_result && position_result  ) {
+      this.duration_query.parse_duration(out format, out this.duration);
+      this.position_query.parse_position(out format, out this.position);
+      this.position_duration(this.position,this.duration);
+    }
+  }
+  
+  public void play() {
+    bin.set_state(Gst.State.PLAYING);
+  }
+  
+  public void pause() {
+    bin.set_state(Gst.State.PAUSED);
+  }
+  
+  public void stop() {
+    bin.set_state(Gst.State.READY);
+  }
+  
+  public void move_to(int64 newloc) {
+    if (0 > newloc) newloc = 0;
+    this.converter.seek_simple(Gst.Format.TIME, Gst.SeekFlags.KEY_UNIT | Gst.SeekFlags.FLUSH, newloc);
+    this.play();
+  }
+  
+  public void seek(int percent) {
+    move_to((int64) this.position + (this.duration * percent / 100));
+  }
+  
+  public void seek_forward(int percent) {
+    seek(percent);
+  }
+  
+  public void seek_backward(int percent) {
+    seek(-percent);
+  }
+  
+  public void set_uri(string uri) {
+    this.bin.set_state(Gst.State.NULL);
+    try {
+      //is there a src pipeline?
+      this.bin.remove(this.src);
+    } finally {
+      //do nothing, the src doesn't exist
+    }
+    this.src = Gst.Element.make_from_uri(Gst.URIType.SRC, uri, "my_src");
+    Gst.Bus bus = this.src.get_bus();
+    bus.add_watch (bus_callback);
+    this.bin.add(this.src);
+    this.src.link(this.decoder);
+  }
+  
+  public void set_volume(double val)
+  requires (val >= 0.0 && val <= 10.0) {
+    volume.set_property("volume",val);
+  }
+  
+  public double get_volume() {
+    GLib.Value ret = 0.0;
+    volume.get_property("volume", ref ret);
+    return ret.get_double();
+  }
+  
+  public void set_panorama(double val) 
+  requires (val >= -1.0 && val <= 1.0) {
+    panorama.set_property("panorama", val);
+  }
+  
+  public double get_panorama() {
+    GLib.Value ret = 0.0;
+    panorama.get_property("panorama", ref ret);
+    return ret.get_double();
+  }
+  
+  public void set_eq(int band, double val)
+  requires (band > -1 && band < 10)
+  requires (val >= -24 && val <= 12) {
+    equalizer.set_property("band" + band.to_string(), val);
+  }
+  
+  public double get_eq(int band) {
+    GLib.Value ret = 0.0;
+    equalizer.get_property("band" + band.to_string(), ref ret);
+    return ret.get_double();
+  }
+  
+  public bool new_decoded_pad(Gst.Pad decodebin, bool arg1, void* data) {
+    //link the pad to the audioconverter
+    decodebin.link( converter.get_static_pad("sink") );
+    return true;
+  }
+
+}
+
+}
diff --git a/src/TrackTransport.vala b/src/TrackTransport.vala
new file mode 100644 (file)
index 0000000..d308bb6
--- /dev/null
@@ -0,0 +1,240 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+namespace IdWorks {
+
+public class TrackTransport : Gtk.HBox {
+  
+  public TrackBin track_bin;
+  public Track track;
+  public Gtk.Window window;
+
+  /*Gst.Element audio_src;
+  public void set_audio_src(Gst.Element src) {
+    audio_src = src;
+  }
+
+  Gst.Element audio_sink;
+  public Gst.Element get_audio_sink()
+  {
+    return audio_sink;
+  }*/
+  
+  /* Widgets */
+  Gtk.Button btn_volume;
+  Gtk.Button btn_eq;
+  Gtk.Button btn_delete;
+  
+  Gtk.Label lbl_title;
+  string _title;
+  public string title {
+    get { return _title; /*lbl_title.get_text();*/ } 
+    set { _title = value; lbl_title.set_markup("<b>" + _title + "</b>"); }
+  }
+  
+  Gtk.ToggleButton btn_active;
+  public void set_active_state(bool state) {
+    btn_active.set_active(state);
+  }
+  public bool get_active_state() {
+    return btn_active.get_active();
+  }
+  
+  Gtk.ToggleButton btn_mute;
+  public void set_mute_state(bool state) {
+    btn_mute.set_active(state);
+  }
+  public bool get_mute_state() {
+    return btn_mute.get_active();
+  }
+  
+  Gtk.Button btn_effects;
+  /*public void set_effects_state(bool state) {
+    btn_effects.set_active(state);
+  }
+  public bool get_effects_state() {
+    return btn_effects.get_active();
+  }*/
+  
+  private double volume;
+  private double panorama;
+  
+  /* Signals */
+  public signal void active_toggled(bool state);
+  //public signal void mute_toggled(bool state);
+  //public signal void effects_toggled(bool state);
+  public signal void eq_updated(int band, double val);
+  public signal void delete_clicked();
+  public signal void volume_updated(double volume);
+  public signal void panorama_updated(double panorama);
+  
+  public signal void dynamics_toggled(bool state);
+  public signal void dynamics_mode_updated(string val);
+  public signal void dynamics_characteristics_updated(string val);
+  public signal void dynamics_ratio_updated(double val);
+  public signal void dynamics_threshold_updated(double val);
+  
+  public signal void echo_toggled(bool val);
+  public signal void echo_max_delay_updated(uint64 val);
+  public signal void echo_delay_updated(uint64 val);
+  public signal void echo_feedback_updated(double val);
+  public signal void echo_intensity_updated(double val);
+  
+  private void show_volume_and_pan() {
+    VolumeAndPanPopUp dlg = new VolumeAndPanPopUp("Mixer Volume", this);
+    dlg.set_transient_for(this.window);
+    dlg.set_volume(this.volume);
+    dlg.set_panorama(this.panorama);
+    dlg.volume_updated.connect(volume_updated_callback);
+    dlg.panorama_updated.connect(panorama_updated_callback);
+    dlg.run();
+    dlg.destroy();
+    dlg = null;
+  }
+  
+  private void show_equalizer() {
+    EqualizerPopUp dlg = new EqualizerPopUp("Track EQ", this);
+    dlg.set_transient_for(this.window);
+    dlg.eq_updated.connect(eq_updated_callback);
+    dlg.run();
+    dlg.destroy();
+    dlg = null;
+  }
+  
+  private void show_effects() {
+    TrackEffectsPopUp dlg = new TrackEffectsPopUp.with_settings("Track Effects", this, SettingsStructures.EchoSettings(), SettingsStructures.DynamicsSettings());
+    dlg.set_transient_for(this.window);
+    // connect up callbacks dlg.blah.connect
+    dlg.echo_toggled.connect((val) => {this.echo_toggled(val);});
+    dlg.echo_max_delay_updated.connect((val) => {this.echo_max_delay_updated(val);});
+    dlg.echo_delay_updated.connect((val) => {this.echo_delay_updated(val);});
+    dlg.echo_feedback_updated.connect((val) => {this.echo_feedback_updated(val);});
+    dlg.echo_intensity_updated.connect((val) => {this.echo_intensity_updated(val);});
+    dlg.dynamics_toggled.connect((val) => {this.dynamics_toggled(val);});
+    dlg.dynamics_mode_updated.connect((val) => {this.dynamics_mode_updated(val);});
+    dlg.dynamics_characteristics_updated.connect((val) => {this.dynamics_characteristics_updated(val);});
+    dlg.dynamics_ratio_updated.connect((val) => {this.dynamics_ratio_updated(val);});
+    dlg.dynamics_threshold_updated.connect((val) => {this.dynamics_threshold_updated(val);});
+    dlg.run();
+    dlg.destroy();
+    dlg = null;
+  }
+  
+  public void playback_starting_callback(Object sender) {
+    btn_active.set_sensitive(false);
+    btn_delete.set_sensitive(false);
+  }
+  
+  public void playback_ending_callback(Object sender) {
+    btn_active.set_sensitive(true);
+    btn_delete.set_sensitive(true);
+  }
+  
+  public void recording_starting_callback(Object sender) {
+    btn_active.set_sensitive(false);
+    btn_delete.set_sensitive(false);
+  }
+  
+  public void recording_ending_callback(Object sender) {
+    btn_active.set_sensitive(true);
+    btn_delete.set_sensitive(true);
+  }
+  
+  private void eq_updated_callback(EqualizerPopUp sender, int band, double val) {
+    eq_updated(band, val);
+  }
+  
+  private void mute_toggled(bool mute_on) {
+    volume_updated((mute_on) ? 0.0 : this.volume);
+  }
+  
+  private void volume_updated_callback(VolumeAndPanPopUp sender, double volume)
+  {
+    this.volume = volume;
+    volume_updated(this.volume);
+  }
+  
+  private void set_track_active(bool active) {
+    btn_mute.set_sensitive(active);
+    btn_volume.set_sensitive(active);
+    btn_eq.set_sensitive(active);
+    btn_effects.set_sensitive(active);
+    btn_active.set_image(new Gtk.Image.from_icon_name((active ? "general_presence_online" : "general_presence_invisible"), Gtk.IconSize.SMALL_TOOLBAR));
+    //track.active = active;
+    active_toggled(active);
+  }
+  
+  private void panorama_updated_callback(VolumeAndPanPopUp sender, double panorama)
+  {
+    this.panorama = panorama;
+    panorama_updated(this.panorama);
+  }
+  
+  /* Constructor */
+  construct {
+    this.volume = 1.0;
+    this.panorama = 0.0;
+    lbl_title = new Gtk.Label("<b>New Track</b>");
+    lbl_title.use_markup = true;
+    lbl_title.set_alignment(0.1f, 0.4f);
+    lbl_title.set_ellipsize(Pango.EllipsizeMode.START);
+    this.pack_start(lbl_title, true, true, 2);
+    Gtk.HButtonBox buttons = new Gtk.HButtonBox();
+    btn_active = new Gtk.ToggleButton(); //.with_label("Active");
+    btn_mute.set_image(new Gtk.Image.from_icon_name("general_presence_online", Gtk.IconSize.SMALL_TOOLBAR));
+    btn_active.toggled.connect((b) => { set_track_active(b.get_active()); });
+    buttons.add(btn_active);
+    btn_mute = new Gtk.ToggleButton(); //.with_label("Mute");
+    btn_mute.set_image(new Gtk.Image.from_icon_name("statusarea_volume_mute", Gtk.IconSize.SMALL_TOOLBAR));
+    btn_mute.toggled.connect((b) => { mute_toggled(b.get_active()); });
+    buttons.add(btn_mute);
+    btn_volume = new Gtk.Button(); //.with_label("Vol");
+    btn_volume.set_image(new Gtk.Image.from_icon_name("statusarea_volumelevel4", Gtk.IconSize.SMALL_TOOLBAR));
+    btn_volume.clicked.connect((b) => { show_volume_and_pan(); });
+    buttons.add(btn_volume);
+    btn_eq = new Gtk.Button.with_label("EQ");
+    btn_eq.clicked.connect((b) => { show_equalizer(); });
+    buttons.add(btn_eq);
+    btn_effects = new Gtk.Button.with_label("FX");
+    btn_effects.clicked.connect((b) => { show_effects(); });
+    buttons.add(btn_effects);
+    btn_delete = new Gtk.Button();    
+    btn_delete.set_image(new Gtk.Image.from_icon_name("camera_delete", Gtk.IconSize.SMALL_TOOLBAR));
+    btn_delete.clicked.connect((b) => { delete_clicked(); });
+    buttons.add(btn_delete);
+    this.pack_start(buttons, false, false, 0);
+  }  
+  
+  /* Overrides */
+  private override void add(Gtk.Widget w) {
+    base.add(w);
+  }
+  
+  /* Hidden inherited methods */
+  private new void pack_start(Gtk.Widget w, bool expand, bool fill, uint padding = 0) {
+    base.pack_start(w, expand, fill, padding);
+  }
+  
+  private new void pack_end(Gtk.Widget w, bool expand, bool fill, uint padding = 0) {
+    base.pack_start(w, expand, fill, padding);
+  }
+  
+  /* Public Methods */
+
+}
+
+}
diff --git a/src/VolumeAndPanPopUp.vala b/src/VolumeAndPanPopUp.vala
new file mode 100644 (file)
index 0000000..e8a31cb
--- /dev/null
@@ -0,0 +1,68 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+namespace IdWorks {
+
+public class VolumeAndPanPopUp : Hildon.Dialog {
+  
+  Gtk.HScale volume_slider;
+  Gtk.HScale panorama_slider;
+
+  public signal void volume_updated(double volume);
+  public signal void panorama_updated(double panorama);
+  
+  public VolumeAndPanPopUp(string title, Gtk.Widget parent) {
+    this.set_title(title);
+    this.set_parent(parent);
+    
+    this.set_default_response(Gtk.ResponseType.ACCEPT);
+    this.add_button(Gtk.STOCK_CLOSE, Gtk.ResponseType.ACCEPT);
+    
+    Gtk.VBox control_area = (Gtk.VBox)this.get_content_area();
+    
+    Gtk.HBox volume_bar = new Gtk.HBox(false, 2);
+    Gtk.Label volume_label = new Gtk.Label("Volume");
+    volume_bar.pack_start(volume_label, false, false , 2);
+    volume_slider = new Gtk.HScale.with_range(0.0, 10.0, 0.05);
+    volume_slider.set_value_pos(Gtk.PositionType.LEFT);
+    volume_slider.value_changed.connect((s) => {volume_updated(s.get_value());});
+    volume_bar.pack_start(volume_slider, true, true , 2);
+    
+    Gtk.HBox panorama_bar = new Gtk.HBox(false, 2);
+    Gtk.Label panorama_label = new Gtk.Label("Pan");
+    panorama_bar.pack_start(panorama_label, false, false , 2);
+    panorama_slider = new Gtk.HScale.with_range(-1.0, 1.0, 0.05);
+    panorama_slider.set_value_pos(Gtk.PositionType.LEFT);
+    panorama_slider.value_changed.connect((s) => {panorama_updated(s.get_value());});
+    panorama_bar.pack_start(panorama_slider, true, true , 2);
+    
+    control_area.pack_start(volume_bar, true, true , 2);
+    control_area.pack_start(panorama_bar, true, true , 2);
+    this.show_all();
+  }
+  
+  public void set_volume(double volume) {
+    volume_slider.set_value(volume);
+  }
+  
+  public void set_panorama(double panorama) {
+    panorama_slider.set_value(panorama);
+  }
+
+}
+
+}
\ No newline at end of file
diff --git a/src/XmlHelpers.vala b/src/XmlHelpers.vala
new file mode 100644 (file)
index 0000000..8c7cb19
--- /dev/null
@@ -0,0 +1,93 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+
+namespace IdWorks {
+
+namespace XmlHelpers {
+  
+  public static bool parse_project_settings_file(string location, string version, ref SettingsStructures.ProjectSettings project, ref string errors) {
+    bool ret = false;
+    project = SettingsStructures.ProjectSettings();
+    errors = "";
+    // prepare the Xml Parser
+    Xml.Parser.init();
+    // load the document
+    Xml.Doc* doc = Xml.Parser.parse_file(location);
+    if (null != doc) {
+      // load the root node
+      Xml.Node* root = doc->get_root_element();
+      if (null != root) {
+        // check element is ProjectSettings
+        if ("ProjectSettings" == root->name) {
+          // check version
+          string? doc_version = get_node_attribute(root, "version");
+          if (null != doc_version && version == doc_version) {
+            // looking good let's get parsing.
+            try {
+              project.from_xml_node(root);
+              ret = true;
+            }
+            catch (GLib.Error ex) {
+              errors = "ProjectSettings parser threw an error.";
+              ret = false;
+            }
+            finally {
+              // no op
+            }
+          }
+          else {
+            errors = "Incorrect version or unversioned document.";
+          }
+        }
+        else {
+          errors = "Not a valid settings file.";
+        }
+      }
+      else {
+        errors = "No root node found.";
+      }
+      // clean up
+      delete doc;
+    }
+    else {
+      errors = "Couldn't parse file as valid XML.";
+    }
+    // clean up the parser
+    Xml.Parser.cleanup();
+    
+    return ret;
+  }
+  
+  private static string? get_node_attribute(Xml.Node* node, string key) {
+    string? ret = null;
+    // Loop over the passed node's attributes
+    for (Xml.Attr* prop = node->properties; prop != null; prop = prop->next) {
+      string attr_name = prop->name;
+      // Notice the ->children which points to a Node*
+      // (Attr doesn't feature content)
+      string attr_content = prop->children->content;
+      if (key == attr_name) {
+        ret = attr_content;
+        break;
+      }
+    }
+    return ret;
+  }
+}
+
+}
\ No newline at end of file
diff --git a/src/YesNoCancelDialog.vala b/src/YesNoCancelDialog.vala
new file mode 100644 (file)
index 0000000..9166764
--- /dev/null
@@ -0,0 +1,43 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+namespace IdWorks {
+
+public class YesNoCancelDialog : Hildon.Dialog {
+
+  public YesNoCancelDialog(Gtk.Widget parent, string title, string message, Gtk.ResponseType default_response) {
+    this.set_title(title);
+    this.set_parent(parent);
+    this.set_default_response(default_response);
+    construct_interface(message);
+  }
+  
+  private void construct_interface(string message) {
+    this.add_button(Gtk.STOCK_YES, Gtk.ResponseType.YES);
+    this.add_button(Gtk.STOCK_NO, Gtk.ResponseType.NO);
+    this.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL);
+    
+    Gtk.VBox control_area = (Gtk.VBox)this.get_content_area();
+    
+    control_area.pack_start(new Gtk.Label(message), true, true, 4);
+    
+    this.show_all();
+  }
+
+}
+
+}
\ No newline at end of file