From 804630a4e0d41b182d8540f2aec69cf25ca0acfd Mon Sep 17 00:00:00 2001 From: druid23 Date: Tue, 28 Sep 2010 21:58:25 +0100 Subject: [PATCH] Adding first code drop 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 --- build.sh | 48 ++ src/.directory | 8 + src/AcmPopUp.vala | 291 +++++++++ src/AppSettings.vala | 19 + src/ApplicationSettings.vala | 652 ++++++++++++++++++++ src/AudioCaptureMonitor.vala | 238 ++++++++ src/CdlParser.vala | 70 +++ src/Constants.vala | 62 ++ src/DbMonitorWidget.vala | 218 +++++++ src/DemoRecorder.vala | 934 +++++++++++++++++++++++++++++ src/DynamicsPopUp.vala | 198 +++++++ src/EchoPopUp.vala | 131 ++++ src/EncodePipeline.vala | 208 +++++++ src/EncoderFactory.vala | 126 ++++ src/EqualizerPopUp.vala | 55 ++ src/EqualizerPreset.vala | 114 ++++ src/FirstRunWizard.vala | 277 +++++++++ src/GraphicEqualizer.vala | 304 ++++++++++ src/IXmlSerializable.vala | 26 + src/ImportBin.vala | 107 ++++ src/InputBin.vala | 177 ++++++ src/MixdownPopUp.vala | 217 +++++++ src/MixerBin.vala | 126 ++++ src/PlayPipeline.vala | 164 +++++ src/PlayerTransport.vala | 205 +++++++ src/Program.vala | 26 + src/ProgressPopUp.vala | 67 +++ src/ProjectSettings.vala | 40 ++ src/ProjectSettingsDialog.vala | 166 ++++++ src/RecordPipeline.vala | 179 ++++++ src/RecordPipeline_Old.vala | 132 +++++ src/SettingsStructures.vala | 1283 ++++++++++++++++++++++++++++++++++++++++ src/SimpleHashMap.vala | 67 +++ src/Tagging.vala | 56 ++ src/Track.vala | 130 ++++ src/TrackBin.vala | 268 +++++++++ src/TrackEffectsPopUp.vala | 179 ++++++ src/TrackPipeline.vala | 313 ++++++++++ src/TrackTransport.vala | 240 ++++++++ src/VolumeAndPanPopUp.vala | 68 +++ src/XmlHelpers.vala | 93 +++ src/YesNoCancelDialog.vala | 43 ++ 42 files changed, 8325 insertions(+) create mode 100755 build.sh create mode 100644 src/.directory create mode 100644 src/AcmPopUp.vala create mode 100644 src/AppSettings.vala create mode 100644 src/ApplicationSettings.vala create mode 100644 src/AudioCaptureMonitor.vala create mode 100644 src/CdlParser.vala create mode 100644 src/Constants.vala create mode 100644 src/DbMonitorWidget.vala create mode 100644 src/DemoRecorder.vala create mode 100644 src/DynamicsPopUp.vala create mode 100644 src/EchoPopUp.vala create mode 100644 src/EncodePipeline.vala create mode 100644 src/EncoderFactory.vala create mode 100644 src/EqualizerPopUp.vala create mode 100644 src/EqualizerPreset.vala create mode 100644 src/FirstRunWizard.vala create mode 100644 src/GraphicEqualizer.vala create mode 100644 src/IXmlSerializable.vala create mode 100644 src/ImportBin.vala create mode 100644 src/InputBin.vala create mode 100644 src/MixdownPopUp.vala create mode 100644 src/MixerBin.vala create mode 100644 src/PlayPipeline.vala create mode 100644 src/PlayerTransport.vala create mode 100644 src/Program.vala create mode 100644 src/ProgressPopUp.vala create mode 100644 src/ProjectSettings.vala create mode 100644 src/ProjectSettingsDialog.vala create mode 100644 src/RecordPipeline.vala create mode 100644 src/RecordPipeline_Old.vala create mode 100644 src/SettingsStructures.vala create mode 100644 src/SimpleHashMap.vala create mode 100644 src/Tagging.vala create mode 100644 src/Track.vala create mode 100644 src/TrackBin.vala create mode 100644 src/TrackEffectsPopUp.vala create mode 100644 src/TrackPipeline.vala create mode 100644 src/TrackTransport.vala create mode 100644 src/VolumeAndPanPopUp.vala create mode 100644 src/XmlHelpers.vala create mode 100644 src/YesNoCancelDialog.vala diff --git a/build.sh b/build.sh new file mode 100755 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 index 0000000..4df1ffe --- /dev/null +++ b/src/.directory @@ -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 index 0000000..1a618f0 --- /dev/null +++ b/src/AcmPopUp.vala @@ -0,0 +1,291 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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 index 0000000..415f891 --- /dev/null +++ b/src/AppSettings.vala @@ -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 index 0000000..e352213 --- /dev/null +++ b/src/ApplicationSettings.vala @@ -0,0 +1,652 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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 = "\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 ("\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "%llu\n" + + "%s\n" + + "%s\n" + + "%s\n" + + "%s\n" + + "%s\n" + + "%s\n" + + "%s\n" + + "\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 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 index 0000000..bead983 --- /dev/null +++ b/src/AudioCaptureMonitor.vala @@ -0,0 +1,238 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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 index 0000000..dd394e6 --- /dev/null +++ b/src/CdlParser.vala @@ -0,0 +1,70 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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 index 0000000..703b27f --- /dev/null +++ b/src/Constants.vala @@ -0,0 +1,62 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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 index 0000000..0b97bfa --- /dev/null +++ b/src/DbMonitorWidget.vala @@ -0,0 +1,218 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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 index 0000000..d0172da --- /dev/null +++ b/src/DemoRecorder.vala @@ -0,0 +1,934 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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 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 = "\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 index 0000000..b2b2885 --- /dev/null +++ b/src/DynamicsPopUp.vala @@ -0,0 +1,198 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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 index 0000000..404163b --- /dev/null +++ b/src/EchoPopUp.vala @@ -0,0 +1,131 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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 index 0000000..00e3bdf --- /dev/null +++ b/src/EncodePipeline.vala @@ -0,0 +1,208 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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 index 0000000..f2eec5d --- /dev/null +++ b/src/EncoderFactory.vala @@ -0,0 +1,126 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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 index 0000000..adba54b --- /dev/null +++ b/src/EqualizerPopUp.vala @@ -0,0 +1,55 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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 index 0000000..c4bb1b8 --- /dev/null +++ b/src/EqualizerPreset.vala @@ -0,0 +1,114 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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 ("\n" + + "\n" + + ">%02f\n" + + ">%02f\n" + + ">%02f\n" + + ">%02f\n" + + ">%02f\n" + + ">%02f\n" + + ">%02f\n" + + ">%02f\n" + + ">%02f\n" + + ">%02f\n" + + "\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 index 0000000..93f03b2 --- /dev/null +++ b/src/FirstRunWizard.vala @@ -0,0 +1,277 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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 index 0000000..633a22d --- /dev/null +++ b/src/GraphicEqualizer.vala @@ -0,0 +1,304 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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 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 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 index 0000000..9b6168c --- /dev/null +++ b/src/IXmlSerializable.vala @@ -0,0 +1,26 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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 index 0000000..f93f177 --- /dev/null +++ b/src/ImportBin.vala @@ -0,0 +1,107 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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 index 0000000..c938362 --- /dev/null +++ b/src/InputBin.vala @@ -0,0 +1,177 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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 index 0000000..4e3d8ce --- /dev/null +++ b/src/MixdownPopUp.vala @@ -0,0 +1,217 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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 index 0000000..e8af28d --- /dev/null +++ b/src/MixerBin.vala @@ -0,0 +1,126 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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 tracks; + Gst.GhostPad sink_pad; + Gst.GhostPad src_pad; + + public MixerBin(string name) { + this.name = name; + tracks = new List(); + + 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(); + } + 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 index 0000000..4e9e206 --- /dev/null +++ b/src/PlayPipeline.vala @@ -0,0 +1,164 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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 index 0000000..9aa22f5 --- /dev/null +++ b/src/PlayerTransport.vala @@ -0,0 +1,205 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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 index 0000000..d1d0ef8 --- /dev/null +++ b/src/Program.vala @@ -0,0 +1,26 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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 index 0000000..ae48d1b --- /dev/null +++ b/src/ProgressPopUp.vala @@ -0,0 +1,67 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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 index 0000000..e82fd50 --- /dev/null +++ b/src/ProjectSettings.vala @@ -0,0 +1,40 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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 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 index 0000000..a8245c7 --- /dev/null +++ b/src/ProjectSettingsDialog.vala @@ -0,0 +1,166 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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 index 0000000..c90593f --- /dev/null +++ b/src/RecordPipeline.vala @@ -0,0 +1,179 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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 index 0000000..8d04703 --- /dev/null +++ b/src/RecordPipeline_Old.vala @@ -0,0 +1,132 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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 index 0000000..1b67831 --- /dev/null +++ b/src/SettingsStructures.vala @@ -0,0 +1,1283 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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 ("\n" + + "%02f\n" + + "%s\n" + + "\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 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 ("\n" + + "%02f\n" + + "\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 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 ("\n" + + "%s\n" + + "\n" + + "\n" + + "%02f\n" + + "%02f\n" + + "\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 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 ("\n" + + "%s\n" + + "%llu\n" + + "%llu\n" + + "%02f\n" + + "%02f\n" + + "\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 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 ("\n" + + "%s>\n" + + "\n" + + ">%02f\n" + + ">%02f\n" + + ">%02f\n" + + ">%02f\n" + + ">%02f\n" + + ">%02f\n" + + ">%02f\n" + + ">%02f\n" + + ">%02f\n" + + ">%02f\n" + + "\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 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 ("\n" + + "\n" + + "%02f\n" + + "%02f\n" + + "\n%s\n" + + "\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 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 ("\n" + + "%s\n" + + "\n%s\n" + + "\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 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 ("\n" + + "%s\n" + + "\n" + + "\n" + + "\n" + + "\n%s\n" + + "\n%s\n" + + "\n%s\n" + + "\n%s\n" + + "\n%s\n" + + "\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 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 ("\n" + + "\n%s\n" + + "\n%s\n" + + "\n%s\n" + + "\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 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 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 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 ("\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "<![CDATA[%s]]>\n" + + "\n" + + "\n%s\n" + + "\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 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 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 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 ("\n" + + "\n" + + "\n" + + "\n" + + "%i\n" + + "\n%s\n" + + "\n%s\n" + + "\n%s\n" + + "\n%s\n" + + "\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 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 index 0000000..f8445be --- /dev/null +++ b/src/SimpleHashMap.vala @@ -0,0 +1,67 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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 : Object { + + List _keys; + List _values; + + public List keys { + get { + return _keys; + } + } + + public List values { + get { + return _values; + } + } + + construct { + _keys = new List(); + _values = new List(); + } + + 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 index 0000000..9947fa5 --- /dev/null +++ b/src/Tagging.vala @@ -0,0 +1,56 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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 ("\n" + + "%s\n" + + "%s\n" + + "\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 taglist) { + string ret = "\n"; + for (int idx = 0; idx < taglist.length(); ++idx) { + ret += ((Tagging.Tag)taglist.nth_data(idx)).to_xml_string(); + } + return ret + "\n"; + } + public static void taglist_from_xml_node_helper(Xml.Node* node, ref unowned List taglist) { + } + + public static void taglist_from_xml_node(Xml.Node* node, ref unowned List taglist) { + } + } + + } + +} \ No newline at end of file diff --git a/src/Track.vala b/src/Track.vala new file mode 100644 index 0000000..d10a7fc --- /dev/null +++ b/src/Track.vala @@ -0,0 +1,130 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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 tracklist) { + string ret = "\n"; + for (int idx = 0; idx < tracklist.length(); ++idx) { + ret += ((Track)tracklist.nth_data(idx)).get_settings_structure().to_xml_string(); + } + return ret + "\n"; + } + + public static void tracklist_from_xml_node_helper(Xml.Node* node, ref unowned List 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 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 index 0000000..9720df0 --- /dev/null +++ b/src/TrackBin.vala @@ -0,0 +1,268 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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 index 0000000..ec4cdf7 --- /dev/null +++ b/src/TrackEffectsPopUp.vala @@ -0,0 +1,179 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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 index 0000000..7870696 --- /dev/null +++ b/src/TrackPipeline.vala @@ -0,0 +1,313 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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("" + track_title + "
" + 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 index 0000000..d308bb6 --- /dev/null +++ b/src/TrackTransport.vala @@ -0,0 +1,240 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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("" + _title + ""); } + } + + 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("New Track"); + 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 index 0000000..e8a31cb --- /dev/null +++ b/src/VolumeAndPanPopUp.vala @@ -0,0 +1,68 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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 index 0000000..8c7cb19 --- /dev/null +++ b/src/XmlHelpers.vala @@ -0,0 +1,93 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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 index 0000000..9166764 --- /dev/null +++ b/src/YesNoCancelDialog.vala @@ -0,0 +1,43 @@ +/* Demo Recorder for MAEMO 5 +* Copyright (C) 2010 Dru Moore +* 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 -- 1.7.9.5