--- /dev/null
+#! /bin/bash
+if [ ! -d "bin" ] ; then
+ mkdir bin
+fi
+valac \
+--pkg gtk+-2.0 \
+--pkg hildon-1 \
+--pkg hildon-fm-2 \
+--pkg gstreamer-0.10 \
+--pkg libosso \
+--pkg dbus-glib-1 \
+-o bin/decibelmeter \
+src/Program.vala \
+src/AudioCaptureMonitor.vala \
+src/Constants.vala \
+src/DbMonitorWidget.vala \
+src/DecibelMeter.vala \
+src/MeterWidget.vala \
+src/InputBin.vala
--- /dev/null
+/* Decibel Meter for MAEMO 5
+* Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2,
+* or (at your option) any later version, as published by the Free
+* Software Foundation
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details
+*
+* You should have received a copy of the GNU General Public
+* License along with this program; if not, write to the
+* Free Software Foundation, Inc.,
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+namespace IdWorks {
+
+ public class AcmPopUp : Hildon.Dialog {
+
+ SettingsStructures.MonitorSettings settings;
+ AudioCaptureMonitor acm;
+ Gtk.Label lbl_level_1;
+ Gtk.Label lbl_level_2;
+ Gtk.HScale pregain_slider;
+ Gtk.HScale volume_slider;
+ Hildon.Button dynamics_settings_btn;
+ Hildon.CheckButton dynamics_btn;
+ Hildon.TouchSelector source_selector;
+ DbMonitorWidget monitor_1;
+ DbMonitorWidget monitor_2;
+ const string level_format = "%02.1f/%02.1fdB";
+
+ public AcmPopUp(string title, Gtk.Widget parent) {
+ this.set_title(title);
+ this.set_parent(parent);
+ this.settings = SettingsStructures.MonitorSettings();
+ this.construct_interface();
+ update_settings();
+ }
+ public AcmPopUp.with_settings(string title, Gtk.Widget parent, SettingsStructures.MonitorSettings settings) {
+ this.set_title(title);
+ this.set_parent(parent);
+ this.settings = settings;
+ this.construct_interface();
+ update_settings();
+ }
+
+ public void set_settings_structure(SettingsStructures.MonitorSettings settings) {
+ this.settings = settings;
+ update_settings();
+ }
+ public SettingsStructures.MonitorSettings get_settings_structure() {
+ return this.settings;
+ }
+
+ private void update_settings() {
+ set_volume(settings.input.volume);
+ set_pregain(settings.input.pregain);
+ //set_source(settings.input.source);
+ dynamics_btn.set_active(settings.input.dynamics.active);
+ toggle_dynamics(settings.input.dynamics.active);
+ }
+
+ private void set_volume(double val) {
+ volume_slider.set_value(val);
+ settings.input.volume = val;
+ if (null != acm) acm.set_volume(val);
+ }
+ private void set_pregain(double val) {
+ pregain_slider.set_value(val);
+ settings.input.pregain = val;
+ if (null != acm) acm.set_pregain(val);
+ }
+
+ private void set_source(string name) {
+ switch (name) {
+ case "Microphone":
+ source_selector.set_active(0, 0);
+ break;
+ case "Monitor":
+ source_selector.set_active(0, 1);
+ break;
+ case "Bluetooth":
+ source_selector.set_active(0, 2);
+ break;
+ default:
+ source_selector.set_active(0, 0);
+ break;
+ }
+ settings.input.source = name;
+ if (null != acm) acm.set_source(name);
+ }
+ private void set_input_source(string name) {
+ settings.input.source = name;
+ if (null != acm) acm.set_source(name);
+ }
+
+ private void construct_interface() {
+ this.set_default_response(Gtk.ResponseType.CANCEL);
+ this.add_button(Gtk.STOCK_OK, Gtk.ResponseType.OK);
+ this.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL);
+
+ Gtk.VBox control_area = (Gtk.VBox)this.get_content_area();
+
+ acm = new AudioCaptureMonitor("Recorder");
+ acm.update_level.connect(update_level_callback);
+
+ Gtk.HButtonBox input_bar = new Gtk.HButtonBox();
+ input_bar.set_layout(Gtk.ButtonBoxStyle.START);
+ input_bar.set_homogeneous(true);
+ input_bar.set_spacing(2);
+
+ source_selector = new Hildon.TouchSelector.text();
+ source_selector.append_text("Microphone");
+ source_selector.append_text("Monitor");
+ source_selector.append_text("Bluetooth");
+ //source_selector.set_active(0, 0);
+ this.set_source(settings.input.source);
+ Hildon.PickerButton source_picker = new Hildon.PickerButton(Hildon.SizeType.FINGER_HEIGHT | Hildon.SizeType.AUTO_WIDTH, Hildon.ButtonArrangement.VERTICAL);
+ source_picker.set_title("Source:");
+ source_picker.set_selector(source_selector);
+ source_picker.set_alignment(0, 0, 1, 1);
+ source_picker.set_image(new Gtk.Image.from_icon_name("camera_volume_unmuted", Gtk.IconSize.BUTTON));
+ source_picker.value_changed.connect( (s) => { set_input_source(s.get_selector().get_current_text()); } );
+ input_bar.pack_start(source_picker, true, true, 2);
+
+ Hildon.CheckButton audio_btn = new Hildon.CheckButton(Hildon.SizeType.FINGER_HEIGHT | Hildon.SizeType.AUTO_WIDTH);
+ audio_btn.set_label("Enable monitor");
+ //audio_btn.set_image(new Gtk.Image.from_icon_name("statusarea_volume_headset", Gtk.IconSize.BUTTON));
+ audio_btn.toggled.connect( (s) => {toggle_monitor(s.get_active());} );
+ input_bar.pack_start(audio_btn, true, false, 2);
+
+ control_area.pack_start(input_bar, true, true, 4);
+
+ Gtk.HBox monitor_1_bar = new Gtk.HBox(false, 2);
+ lbl_level_1 = new Gtk.Label(level_format.printf(0, 0));
+ monitor_1 = new DbMonitorWidget();
+ monitor_1.set_layout(DbMonitorWidget.Layout.HORIZONTAL);
+ monitor_1.set_peak(1);
+ monitor_1.set_decay(1.5);
+ monitor_1_bar.pack_start(new Gtk.Label("L"), false, false, 2);
+ monitor_1_bar.pack_start(monitor_1, true, true, 2);
+ monitor_1_bar.pack_start(lbl_level_1, false, false, 2);
+ control_area.pack_start(monitor_1_bar, true, true, 2);
+
+ Gtk.HBox monitor_2_bar = new Gtk.HBox(false, 4);
+ lbl_level_2 = new Gtk.Label(level_format.printf(0, 0));
+ monitor_2 = new DbMonitorWidget();
+ monitor_2.set_layout(DbMonitorWidget.Layout.HORIZONTAL);
+ monitor_2.set_peak(1);
+ monitor_2.set_decay(1.5);
+ monitor_2_bar.pack_start(new Gtk.Label("R"), false, false, 2);
+ monitor_2_bar.pack_start(monitor_2, true, true, 2);
+ monitor_2_bar.pack_start(lbl_level_2, false, false, 2);
+ control_area.pack_start(monitor_2_bar, true, true, 2);
+
+ Gtk.HBox pregain_bar = new Gtk.HBox(false, 4);
+ Gtk.Label pregain_label = new Gtk.Label("Pre-gain");
+ pregain_bar.pack_start(pregain_label, false, false , 2);
+ pregain_slider = new Gtk.HScale.with_range(0.0, 10.0, 0.1);
+ pregain_slider.set_value_pos(Gtk.PositionType.LEFT);
+ pregain_slider.value_changed.connect((s) => {this.set_pregain(s.get_value());});
+ pregain_bar.pack_start(pregain_slider, true, true , 2);
+
+ control_area.pack_start(pregain_bar, true, true , 2);
+
+ Gtk.HBox dynamics_bar = new Gtk.HBox(false, 4);
+ dynamics_btn = new Hildon.CheckButton(Hildon.SizeType.FINGER_HEIGHT | Hildon.SizeType.AUTO_WIDTH);
+ dynamics_btn.set_label("Enable dynamics");
+ dynamics_btn.toggled.connect( (s) => {toggle_dynamics(s.get_active());} );
+ dynamics_bar.pack_start(dynamics_btn, true, false, 2);
+ dynamics_settings_btn = new Hildon.Button(Hildon.SizeType.FINGER_HEIGHT | Hildon.SizeType.AUTO_WIDTH, Hildon.ButtonArrangement.HORIZONTAL);
+ dynamics_settings_btn.set_text("Settings", "");
+ dynamics_settings_btn.set_image(new Gtk.Image.from_icon_name("general_settings", Gtk.IconSize.BUTTON));
+ dynamics_settings_btn.set_image_position(Gtk.PositionType.LEFT);
+ dynamics_settings_btn.clicked.connect(do_dynamics_popup);
+ dynamics_bar.pack_start(dynamics_settings_btn, true, false, 2);
+
+ control_area.pack_start(dynamics_bar, true, true , 2);
+
+ Gtk.HBox volume_bar = new Gtk.HBox(false, 4);
+ Gtk.Label volume_label = new Gtk.Label("Volume");
+ volume_bar.pack_start(volume_label, false, false , 2);
+ volume_slider = new Gtk.HScale.with_range(0.0, 10.0, 0.1);
+ volume_slider.set_value_pos(Gtk.PositionType.LEFT);
+ volume_slider.value_changed.connect((s) => {this.set_volume(s.get_value());});
+ volume_bar.pack_start(volume_slider, true, true , 2);
+
+ control_area.pack_start(volume_bar, true, true , 2);
+
+ this.update_settings();
+ this.show_all();
+ }
+
+ public void play() {
+ acm.play();
+ }
+
+ public void pause() {
+ acm.pause();
+ }
+
+ public void stop() {
+ acm.stop();
+ }
+
+ private void toggle_monitor(bool state) {
+ if (null != acm) {
+ acm.set_monitor_mute(!state);
+ }
+ }
+
+ private void toggle_dynamics(bool state) {
+ this.settings.input.dynamics.active = state;
+ this.dynamics_settings_btn.set_sensitive(state);
+ if (null != acm) {
+ if (!state) {
+ SettingsStructures.DynamicsSettings ds = SettingsStructures.DynamicsSettings();
+ acm.set_dynamics(ds.mode, ds.characteristics, ds.threshold, ds.ratio); // shouldn't be applied
+ }
+ else {
+ acm.set_dynamics(settings.input.dynamics.mode, settings.input.dynamics.characteristics, settings.input.dynamics.threshold, settings.input.dynamics.ratio);
+ }
+ }
+ }
+
+ private void do_dynamics_popup() {
+ stdout.printf("dynamics popup new called\n");
+ stdout.flush();
+ DynamicsPopUp dlg = new DynamicsPopUp.with_settings("Monitoring dynamics settings", this, settings.input.dynamics);
+ dlg.set_transient_for(this.get_transient_for());
+ dlg.mode_updated.connect(this.dynamics_update_mode_callback);
+ dlg.characteristics_updated.connect(this.dynamics_update_characteristics_callback);
+ dlg.ratio_updated.connect(this.dynamics_update_ratio_callback);
+ dlg.threshold_updated.connect(this.dynamics_update_threshold_callback);
+ stdout.printf("dynamics connections made\n");
+ stdout.flush();
+ dlg.run();
+ stdout.printf("dynamics back from run\n");
+ stdout.flush();
+ dlg.destroy();
+ }
+ private void dynamics_update_mode_callback(DynamicsPopUp sender, string val) {
+ settings.input.dynamics.mode = val;
+ if (null != acm) {
+ acm.set_dynamics_mode(val);
+ }
+ }
+ private void dynamics_update_characteristics_callback(DynamicsPopUp sender, string val) {
+ settings.input.dynamics.characteristics = val;
+ if (null != acm) {
+ acm.set_dynamics_characteristics(val);
+ }
+ }
+ private void dynamics_update_ratio_callback(DynamicsPopUp sender, double val) {
+ settings.input.dynamics.ratio = val;
+ if (null != acm) {
+ acm.set_dynamics_ratio(val);
+ }
+ }
+ private void dynamics_update_threshold_callback(DynamicsPopUp sender, double val) {
+ settings.input.dynamics.threshold = val;
+ if (null != acm) {
+ acm.set_dynamics_threshold(val);
+ }
+ }
+
+ private void update_level_callback(AudioCaptureMonitor sender, int channels, int channel, double rms_dB, double peak_dB, double decay_dB, double rms) {
+ //string text = level_format.printf(channel.to_string(), rms_dB, peak_dB, decay_dB, rms, Math.pow (10, decay_dB / 20));
+ string text = level_format.printf(peak_dB, decay_dB);
+ if (0 == channel) {
+ lbl_level_1.set_text(text);
+ monitor_1.set_peak(Math.pow (10, peak_dB / 20));
+ monitor_1.set_decay(Math.pow (10, decay_dB / 20));
+ }
+ else if (1 == channel) {
+ lbl_level_2.set_text(text);
+ monitor_2.set_peak(Math.pow (10, peak_dB / 20));
+ monitor_2.set_decay(Math.pow (10, decay_dB / 20));
+ }
+ else {
+ stdout.printf("channel %s found\n", channel.to_string());
+ stdout.flush();
+ }
+ }
+
+ }
+
+}
--- /dev/null
+/* Decibel Meter for MAEMO 5
+* Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2,
+* or (at your option) any later version, as published by the Free
+* Software Foundation
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details
+*
+* You should have received a copy of the GNU General Public
+* License along with this program; if not, write to the
+* Free Software Foundation, Inc.,
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+namespace IdWorks {
+
+public class AudioCaptureMonitor : Gst.Pipeline {
+ InputBin input;
+ Gst.Element level;
+ Gst.Element monitor;
+ Gst.Element sink_element;
+ uint watch_id;
+
+ public AudioCaptureMonitor(string name) {
+ this.name = name;
+ construct_pipeline("Microphone", 1.0);
+ }
+ public AudioCaptureMonitor.with_settings(string name, string source, double volume) {
+ this.name = name;
+ construct_pipeline(source, volume);
+ }
+
+ private void construct_pipeline(string source, double volume) {
+
+ this.input = new InputBin.with_settings("input", source, 1.0);
+ 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);
+
+ 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_source(string name) {
+ this.input.set_source(name);
+ }
+ public string get_source() {
+ return this.input.get_source();
+ }
+
+ 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;
+ }
+
+}
+
+}
--- /dev/null
+/* Decibel Meter for MAEMO 5
+* Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2,
+* or (at your option) any later version, as published by the Free
+* Software Foundation
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details
+*
+* You should have received a copy of the GNU General Public
+* License along with this program; if not, write to the
+* Free Software Foundation, Inc.,
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+namespace IdWorks {
+
+ namespace Time {
+
+ public const string CLOCK_FORMAT = "%02llu:%02llu.%01llu";
+ public static string time_to_string(int64 t) {
+ uint64 ut = (uint64)t / Nanoseconds.MILLISECOND;
+ return CLOCK_FORMAT.printf((ut / Milliseconds.MINUTE), (ut / Milliseconds.SECOND) % Seconds.MINUTE, (ut % Milliseconds.SECOND) / 100);
+ }
+ public static int64 get_current_time() {
+ TimeVal tv = TimeVal();
+ tv.get_current_time();
+ return (((int64)tv.tv_sec * 1000000) + (int64)tv.tv_usec) * 1000;
+ }
+
+ namespace Nanoseconds {
+ public const uint64 NANOSECOND = 1;
+ public const uint64 MILLISECOND = 1000000 * NANOSECOND;
+ public const uint64 SECOND = 1000 * MILLISECOND;
+ public const uint64 MINUTE = 60 * SECOND;
+ public const uint64 HOUR = 60 * MINUTE;
+ public const uint64 DAY = 24 * HOUR;
+ public const uint64 WEEK = 7 * DAY;
+ }
+
+ namespace Milliseconds {
+ public const uint64 MILLISECOND = 1;
+ public const uint64 SECOND = 1000 * MILLISECOND;
+ public const uint64 MINUTE = 60 * SECOND;
+ public const uint64 HOUR = 60 * MINUTE;
+ public const uint64 DAY = 24 * HOUR;
+ public const uint64 WEEK = 7 * DAY;
+ }
+
+ namespace Seconds {
+ public const uint64 SECOND = 1;
+ public const uint64 MINUTE = 60 * SECOND;
+ public const uint64 HOUR = 60 * MINUTE;
+ public const uint64 DAY = 24 * HOUR;
+ public const uint64 WEEK = 7 * DAY;
+ }
+
+ }
+
+}
--- /dev/null
+/* Decibel Meter for MAEMO 5
+* Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2,
+* or (at your option) any later version, as published by the Free
+* Software Foundation
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details
+*
+* You should have received a copy of the GNU General Public
+* License along with this program; if not, write to the
+* Free Software Foundation, Inc.,
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+namespace IdWorks {
+
+public class DbMonitorWidget : Gtk.Widget {
+
+ public static enum Layout {
+ VERTICAL = 0,
+ HORIZONTAL
+ }
+
+ private static const int BORDER_WIDTH = 16;
+ private static const int BORDER_HEIGHT = 8;
+ //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 : 225;
+ requisition.height = (Layout.VERTICAL == this.layout) ? 225 : 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 (BORDER_WIDTH
+ , (this.allocation.height) - ( (this.allocation.height) / (MAX_VALUE - MIN_VALUE) ) * ZERO_VALUE
+ , this.allocation.width - 2 * BORDER_WIDTH
+ , 2
+ );
+ }
+ else {
+ cr.rectangle ((((this.allocation.width) / (MAX_VALUE - MIN_VALUE)) * ZERO_VALUE)
+ , BORDER_HEIGHT
+ , 2
+ , this.allocation.height - 2 * BORDER_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
+ }
+
+}
+
+}
--- /dev/null
+/* Decibel Meter for MAEMO 5
+* Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2,
+* or (at your option) any later version, as published by the Free
+* Software Foundation
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details
+*
+* You should have received a copy of the GNU General Public
+* License along with this program; if not, write to the
+* Free Software Foundation, Inc.,
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+namespace IdWorks {
+
+public class DecibelMeter : Hildon.Program {
+
+ Hildon.StackableWindow window;
+ Osso.Context osso_context;
+ MeterWidget channel_one;
+ MeterWidget channel_two;
+ AudioCaptureMonitor monitor;
+
+ Hildon.Button play_button;
+ bool is_playing = false;
+ Gtk.Image play_icon;
+ Gtk.Image stop_icon;
+ Gtk.Image mic_icon;
+ Gtk.Image audio_icon;
+ Gtk.Image bt_icon;
+ Hildon.TouchSelector source_selector;
+ Hildon.PickerButton source_picker;
+
+ construct {
+ osso_context = new Osso.Context("decibelmeter", "0.1", false, null);
+ window = new Hildon.StackableWindow();
+ window.destroy.connect(Gtk.main_quit);
+ window.set_title("Decibel Meter");
+
+ play_icon = new Gtk.Image.from_icon_name("camera_playback", Gtk.IconSize.BUTTON);
+ stop_icon = new Gtk.Image.from_icon_name("camera_video_stop", Gtk.IconSize.BUTTON);
+ mic_icon = new Gtk.Image.from_icon_name("camera_volume_unmuted", Gtk.IconSize.BUTTON);
+ audio_icon = new Gtk.Image.from_icon_name("mediaplayer_main_button_music_pressed", Gtk.IconSize.BUTTON);
+ bt_icon = new Gtk.Image.from_icon_name("general_bluetooth", Gtk.IconSize.BUTTON);
+
+ channel_one = new MeterWidget();
+ channel_one.title = "Channel 1";
+ channel_one.set_layout(DbMonitorWidget.Layout.VERTICAL);
+ channel_two = new MeterWidget();
+ channel_two.title = "Channel 2";
+
+ Gtk.VBox container = new Gtk.VBox(false, 4);
+
+ Gtk.HBox meters = new Gtk.HBox(true, 4);
+ meters.pack_start(channel_one, true, true, 4);
+ meters.pack_start(channel_two, true, true, 4);
+
+ container.add(meters);
+
+ Gtk.HBox control = new Gtk.HBox(false, 4);
+
+
+ play_button = new Hildon.Button(Hildon.SizeType.THUMB_HEIGHT | Hildon.SizeType.AUTO_WIDTH, Hildon.ButtonArrangement.HORIZONTAL);
+ play_button.set_image(play_icon);
+ play_button.clicked.connect(on_play);
+ control.pack_start(play_button, false, true, 4);
+
+ Gtk.HScale volume_slider = new Gtk.HScale.with_range(0.0, 10.0, 0.05);
+ volume_slider.set_value_pos(Gtk.PositionType.LEFT);
+ volume_slider.set_value(1.0);
+ volume_slider.value_changed.connect( (s) => { monitor.set_volume(s.get_value()); } );
+ control.pack_start(new Gtk.Label("Gain:"), false, true, 4);
+ control.pack_start(volume_slider, true, true, 4);
+
+ 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("Microphone");
+ source_picker = new Hildon.PickerButton(Hildon.SizeType.THUMB_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(mic_icon);
+ source_picker.value_changed.connect( (s) => { set_input_source(s.get_selector().get_current_text()); } );
+ control.pack_start(source_picker, true, true, 4);
+
+
+ container.add(control);
+
+ window.add(container);
+ construct_menu();
+ construct_monitor();
+ }
+
+ private void construct_menu() {
+
+
+ }
+
+ private void set_source(string name) {
+ switch (name) {
+ case "Microphone":
+ source_selector.set_active(0, 0);
+ source_picker.set_image(mic_icon);
+ source_picker.set_text("Source:", name);
+ break;
+ case "Monitor":
+ source_selector.set_active(0, 1);
+ source_picker.set_image(audio_icon);
+ source_picker.set_text("Source:", name);
+ break;
+ case "Bluetooth":
+ source_selector.set_active(0, 2);
+ source_picker.set_image(bt_icon);
+ source_picker.set_text("Source:", name);
+ break;
+ default:
+ source_selector.set_active(0, 0);
+ source_picker.set_image(mic_icon);
+ source_picker.set_text("Source:", name);
+ break;
+ }
+ if (null != monitor) monitor.set_source(name);
+ }
+ private void set_input_source(string name) {
+ switch (name) {
+ case "Microphone":
+ source_picker.set_image(mic_icon);
+ source_picker.set_text("Source:", name);
+ break;
+ case "Monitor":
+ source_picker.set_image(audio_icon);
+ source_picker.set_text("Source:", name);
+ break;
+ case "Bluetooth":
+ source_picker.set_image(bt_icon);
+ source_picker.set_text("Source:", name);
+ break;
+ default:
+ source_picker.set_image(mic_icon);
+ source_picker.set_text("Source:", name);
+ break;
+ }
+ if (null != monitor) monitor.set_source(name);
+ }
+
+ private void on_play() {
+ if (!is_playing) {
+ play_button.set_image(stop_icon);
+ monitor.play();
+ }
+ else {
+ monitor.stop();
+ play_button.set_image(play_icon);
+ }
+ is_playing = !is_playing;
+ }
+
+ private void construct_monitor() {
+ monitor = new AudioCaptureMonitor.with_settings("Monitor", "Microphone", 1.0);
+ monitor.update_level.connect(level_updated);
+ }
+
+ private void level_updated(int channels, int channel, double rms_dB, double peak_dB, double decay_dB, double rms) {
+ switch(channel) {
+ case 0:
+ channel_one.set_peak(peak_dB);
+ channel_one.set_decay(decay_dB);
+ break;
+ case 1:
+ channel_two.set_peak(peak_dB);
+ channel_two.set_decay(decay_dB);
+ break;
+ default:
+ break;
+ }
+ }
+
+ public void run() {
+ window.show_all();
+ Gtk.main();
+ }
+
+}
+
+}
--- /dev/null
+/* Decibel Meter for MAEMO 5
+* Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2,
+* or (at your option) any later version, as published by the Free
+* Software Foundation
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details
+*
+* You should have received a copy of the GNU General Public
+* License along with this program; if not, write to the
+* Free Software Foundation, Inc.,
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+namespace IdWorks {
+
+public class InputBin : Gst.Bin {
+
+ Gst.Element src;
+ Gst.Element converter;
+ Gst.Element volume;
+ Gst.GhostPad src_pad;
+ Gst.GhostPad sink_pad;
+
+ public InputBin(string name) {
+ this.set_name(name);
+ construct_pipeline("Microphone", 1.0);
+ }
+ public InputBin.with_settings(string name, string source, double volume) {
+ this.name = name;
+ construct_pipeline(source, volume);
+ }
+
+ 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(string source, double volume) {
+
+ this.src = Gst.ElementFactory.make("pulsesrc", "capture");
+ this.src.set_property("device", get_source_device(source)); //"source.hw0"); /// TODO
+ this.add(this.src);
+
+ this.converter = Gst.ElementFactory.make("audioconvert", "converter");
+ this.add(this.converter);
+
+ this.volume = Gst.ElementFactory.make("volume", "volume");
+ this.volume.set_property("volume", volume);
+ this.add(this.volume);
+
+ this.src.link(converter);
+ this.converter.link(this.volume);
+
+ this.sink_pad = new Gst.GhostPad("sink", this.src.get_static_pad("sink"));
+ this.add_pad(sink_pad);
+ this.src_pad = new Gst.GhostPad("src", this.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_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_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();
+ }
+
+
+}
+
+}
--- /dev/null
+/* Demo Recorder for MAEMO 5
+* Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2,
+* or (at your option) any later version, as published by the Free
+* Software Foundation
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details
+*
+* You should have received a copy of the GNU General Public
+* License along with this program; if not, write to the
+* Free Software Foundation, Inc.,
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+namespace IdWorks {
+
+public class MeterWidget : Gtk.VBox {
+
+ private Gtk.Label title_label;
+ private Gtk.Label peak_label;
+ private Gtk.Label decay_label;
+ private DbMonitorWidget meter;
+
+ 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.peak_label.set_text("%02.2f".printf(val));
+ meter.set_peak(Math.pow (10, val / 20));
+ }
+ public void set_decay(double val) {
+ this.decay = (MAX_VALUE > val) ? val : MAX_VALUE;
+ this.decay_label.set_text("%02.2f".printf(val));
+ meter.set_decay(Math.pow (10, val / 20));
+ }
+ private DbMonitorWidget.Layout layout = DbMonitorWidget.Layout.VERTICAL;
+ public void set_layout(DbMonitorWidget.Layout layout) {
+ this.layout = layout;
+ meter.set_layout(layout);
+ }
+
+ public string title {
+ get {
+ return title_label.get_text();
+ }
+ set {
+ title_label.set_text(value);
+ }
+ }
+
+ construct {
+ title_label = new Gtk.Label("");
+ title_label.modify_font(Pango.FontDescription.from_string("Sans 20"));
+ title_label.set_padding(4, 2);
+ //title_label.set_alignment(0,0);
+
+ this.pack_start(title_label, false, true, 4);
+
+ meter = new DbMonitorWidget();
+ meter.set_layout(layout);
+ meter.set_peak(0.0);
+ meter.set_decay(0.0);
+ this.pack_start(meter, true, true, 0);
+
+ Gtk.HBox display = new Gtk.HBox(true, 8);
+
+ peak_label = new Gtk.Label("0.0");
+ peak_label.modify_font(Pango.FontDescription.from_string("Sans 40"));
+ peak_label.set_padding(4, 2);
+ //peak_label.set_alignment(0,0);
+ decay_label = new Gtk.Label("0.0");
+ decay_label.modify_font(Pango.FontDescription.from_string("Sans 40"));
+ decay_label.set_padding(4, 2);
+ //decay_label.set_alignment(0,0);
+
+ display.add(peak_label);
+ display.add(decay_label);
+
+ this.pack_start(display, false, true, 0);
+
+ Gtk.HBox display2 = new Gtk.HBox(true, 0);
+
+ Gtk.Label peak_label2 = new Gtk.Label("dB Peak");
+ peak_label2.modify_font(Pango.FontDescription.from_string("Sans 16"));
+ peak_label2.set_padding(0, 0);
+ //peak_label2.set_alignment(0,0);
+ Gtk.Label decay_label2 = new Gtk.Label("dB Decay");
+ decay_label2.modify_font(Pango.FontDescription.from_string("Sans 16"));
+ decay_label2.set_padding(0, 0);
+ //decay_label2.set_alignment(0,0);
+
+ display2.add(peak_label2);
+ display2.add(decay_label2);
+
+ this.pack_start(display2, false, true, 0);
+
+ }
+
+}
+
+}
--- /dev/null
+/* Demo Recorder for MAEMO 5
+* Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2,
+* or (at your option) any later version, as published by the Free
+* Software Foundation
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details
+*
+* You should have received a copy of the GNU General Public
+* License along with this program; if not, write to the
+* Free Software Foundation, Inc.,
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+namespace IdWorks {
+
+//extern void exit(int exit_code);
+
+ public static int main(string[] args) {
+ Gtk.init(ref args);
+ Gst.init(ref args);
+ IdWorks.DecibelMeter meter = new IdWorks.DecibelMeter();
+ meter.run();
+ return 0;
+ }
+
+}
--- /dev/null
+<html>
+<head>
+<title>Decibel Meter for Maemo 5 N900</title>
+</head>
+<body>
+<h1>Decibel Meter</h1>
+<h2>A simple audio meter for Maemo5 and the N900</h2>
+<p>Visita the project page at <a href="https://garage.maemo.org/projects/decibelmeter/" title="Decibel Meter on maemo Garage">https://garage.maemo.org/projects/decibelmeter/</a></p>
+</body>
+<html>