Merge branch 'master' into gregoa
authorgregor herrmann <gregoa@debian.org>
Sun, 14 Mar 2010 23:31:13 +0000 (00:31 +0100)
committergregor herrmann <gregoa@debian.org>
Sun, 14 Mar 2010 23:31:13 +0000 (00:31 +0100)
README
src/CMakeLists.txt
src/app.vala
src/app_main.vala
src/app_music.vala [new file with mode: 0644]
src/audio.vala
src/config.vala
src/zavai.vala

diff --git a/README b/README
index cdea9ee1fc4858336cc728a38b21a19d04012ceb..626b1260e38bcab7a4c2cba9b29f3adedd210f7e 100644 (file)
--- a/README
+++ b/README
@@ -111,13 +111,14 @@ Features / guide
  It is possible to set an alarm, which will be shown in the alarm window. When
  the alarm expires, the phone wakes up if it is suspended, and zavai vibrates,
  turns on baclight, shows a big ACK button and flashes the AUX led for 30
- seconds.
+ seconds. It also plays some music.
 
  You can use the big button on screen or the AUX button to acknowledge the
  alarm and stop all the attention-seeking activities.
  
- When I'll implement some audio playing infrastructure, I'll implement playing
- an audio file as well.
+ If you want to change the alarm ringtone, you can set:
+ ringtone_alarm = "file:///some/where/file.ext"
+ in ~/.zavai/config
 
  Alarms (acknowledged or not) are logged in "~/.zavai/log-alarm/". Still
  missing, howerver, is a way to show that an alarm rang and noone noticed.
@@ -183,16 +184,28 @@ TODO list / wish list
     + start frameworkd as a subprocess, configured to only do phone
     + go through the dbus motions of turning on this and that, and entering PIN
       hardcoded in zavai config
-    - GSM status on main screen (with messages while coming online, and
+    + GSM status on main screen (with messages while coming online, and
       operator, power and so on)
     - log and refuse incoming calls and messages
  - alarm
-    - play sound at alarm trigger
+    + play sound at alarm trigger
     - leave expired alarm on screen until acknowledged
       (alarm status icon that also brings to alarm menu)
     - remember alarm names (on request, maybe with an add feature) and how
       often they are triggered, and show them most frequent first
     - show active alarms and allow to delete them
+ - fisheye list music player
+   (-> learn/test gstreamer libs)
+    - player page with seek/skip controls
+    - playlist editor
+    - chosen song start playing if nothing is being played
+    - chosen song moves to playlist editor: play, add to playlist, add dir to playlist
+      (show a standard list multiselectable with all the songs in the dir and
+      only the selected song selected; allow to select others, select all, add
+      selected to playlist)
+    - playlist as reorderable standard list, allow to delete tracks, reorder tracks
+    - save playlist to file to reload later
+    - pause with headset button
  - audio notes
     - record audio notes, logging start and stop so it gets an associated GPX
     - shortcut icon in main screen
@@ -214,18 +227,6 @@ TODO list / wish list
    (-> learn/test libalsamixer something)
    /usr/share/vala/vapi/alsa.vapi
  - space buttons from sliders, to avoid shutting down instead of lowering the volume
- - fisheye list music player
-   (-> learn/test gstreamer libs)
-    - player page with seek/skip controls
-    - playlist editor
-    - chosen song start playing if nothing is being played
-    - chosen song moves to playlist editor: play, add to playlist, add dir to playlist
-      (show a standard list multiselectable with all the songs in the dir and
-      only the selected song selected; allow to select others, select all, add
-      selected to playlist)
-    - playlist as reorderable standard list, allow to delete tracks, reorder tracks
-    - save playlist to file to reload later
-    - pause with headset button
  - battery without devkit: do the parsing via lua
  - contacts: show as a fancy focus+context list (see prefuse)
     - vcard on e-vcard.{h,c}
index 9e03dfdac3fe43fc46dfa325bc5f87ee5945c059..657b95d057efe46d58b2b2ad6138acf9fc446ce6 100644 (file)
@@ -3,7 +3,7 @@ include(../vala.cmake)
 
 set(zavai_version 0.1)
 
-set(packages gtk+-2.0 dbus-glib-1>=0.80 libwnck-1.0>=2.26.0 lua5.1 libomhacks x11 gdk-x11-2.0 libgps)
+set(packages gtk+-2.0 dbus-glib-1>=0.80 libwnck-1.0>=2.26.0 lua5.1 libomhacks x11 gdk-x11-2.0 libgps gstreamer-0.10)
 add_packages(ZAVAI ${packages})
 
 set(VALA_PACKAGES ${packages} posix linux-input dbus-extra gtkfisheyelist)
index 9b4b4a1e825b8d48233d890a4995e1aead3d7db7..5281f56aeccd09531b180d64a53fc9e8372f35e8 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * app - zavai main window
  *
- * Copyright (C) 2009  Enrico Zini <enrico@enricozini.org>
+ * Copyright (C) 2009-2010  Enrico Zini <enrico@enricozini.org>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
index 34b87a18fb9a30f04c3c19fda620a522aad1b6c6..b7752cb423f34168eb3d16d0cf642987c8e5c6fe 100644 (file)
@@ -144,6 +144,7 @@ public class Status : Applet
         pack_start(status_icons, false, false, 0);
         pack_start(clock, false, false, 0);
         pack_start(gsm_status, false, false, 0);
+        // pack_start(music.player, false, false, 0);
         pack_end(menu, false, false, 0);
 
         zavai.gsm.gsm.status_changed += (msg) => { gsm_status.set_text(msg); };
diff --git a/src/app_music.vala b/src/app_music.vala
new file mode 100644 (file)
index 0000000..d066e3c
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * app_music - zavai music player UI functions
+ *
+ * Copyright (C) 2010  Enrico Zini <enrico@enricozini.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+using GLib;
+
+namespace zavai {
+namespace ui {
+namespace music {
+
+public class PlayPauseButton : Gtk.Button
+{
+    protected Gtk.Image img_play;
+    protected Gtk.Image img_pause;
+    protected bool is_play;
+
+    construct
+    {
+        img_play = new Gtk.Image.from_stock(Gtk.STOCK_MEDIA_PLAY, Gtk.IconSize.BUTTON);
+        img_pause = new Gtk.Image.from_stock(Gtk.STOCK_MEDIA_PAUSE, Gtk.IconSize.BUTTON);
+
+        clicked += on_clicked;
+
+        is_play = audio.musicplayer.get_state() == Gst.State.PAUSED;
+
+        audio.musicplayer.state_changed += (state) => {
+            if (is_play != (state == Gst.State.PAUSED))
+            {
+                is_play = !is_play;
+                update_image();
+            }
+        };
+
+        update_image();
+    }
+
+    protected void update_image()
+    {
+        if (is_play)
+        {
+            set_image(img_play);
+        } else {
+            set_image(img_pause);
+        }
+    }
+
+    void on_clicked(Gtk.Button b)
+    {
+        if (is_play)
+            audio.musicplayer.pause();
+        else
+            audio.musicplayer.resume();
+    }
+}
+
+public class Player : Gtk.VBox
+{
+    protected Gtk.Label l_status;
+    protected PlayPauseButton b_playpause;
+    protected Gtk.Button b_stop;
+    protected Gtk.Button b_prev;
+    protected Gtk.Button b_next;
+
+    construct
+    {
+        l_status = new Gtk.Label("");
+        //l_date.modify_font(Pango.FontDescription.from_string("Sans 40"));
+        b_playpause = new PlayPauseButton();
+        b_stop = new Gtk.Button.from_stock(Gtk.STOCK_MEDIA_STOP);
+        b_stop.clicked += (b) => { audio.musicplayer.stop(); };
+        b_prev = new Gtk.Button.from_stock(Gtk.STOCK_MEDIA_PREVIOUS);
+        b_next = new Gtk.Button.from_stock(Gtk.STOCK_MEDIA_NEXT);
+        pack_start(l_status, false, false, 0);
+        var buttons = new Gtk.HButtonBox();
+        buttons.add(b_prev);
+        buttons.add(b_playpause);
+        buttons.add(b_stop);
+        buttons.add(b_next);
+        pack_start(buttons, false, false, 0);
+
+        //zavai.clock.clock.minute_changed += on_minute_changed;
+        //zavai.clock.clock.schedule_changed += on_schedule_changed;
+        //on_schedule_changed(zavai.clock.clock.next_alarm());
+    }
+
+#if 0
+    private void on_date_clicked(Gtk.Button b)
+    {
+        zavai.app.push_applet(zavai.ui.calendar.calendar);
+    }
+
+    private void on_schedule_changed(zavai.clock.Alarm? next)
+    {
+        if (next == null)
+        {
+            last_deadline = 0;
+            last_deadline_label = "";
+        } else {
+            last_deadline = next.ev.deadline;
+            last_deadline_label = next.label;
+        }
+        on_minute_changed((long)last_time, last_time_type);
+    }
+
+    private void on_minute_changed(long ts, zavai.clock.SourceType type)
+    {
+        last_time = (time_t)ts;
+        last_time_type = type;
+
+        string typetag = "unknown";
+        switch (type)
+        {
+            case zavai.clock.SourceType.GPS:
+                typetag = "gps";
+                break;
+            case zavai.clock.SourceType.SYSTEM:
+                typetag = "sys";
+                break;
+        }
+
+        var t = Time.local((time_t)ts);
+        l_date.set_text(t.format("%a %d %b"));
+        l_time.set_text("%2d:%02d (%s)".printf(t.hour, t.minute, typetag));
+
+        if (last_deadline == 0)
+            l_deadline.set_text("");
+        else
+        {
+            int remaining = (int)(last_deadline - last_time);
+            int hours = remaining / 3600;
+            int minutes = (remaining % 3600) / 60;
+            if (hours == 0 && minutes == 0)
+                l_deadline.set_text(last_deadline_label + "\nanytime now");
+            else
+                l_deadline.set_text("%s\nin %02dh %02dm".printf(last_deadline_label, hours, minutes));
+        }
+    }
+#endif
+}
+
+public Player player = null;
+
+public void init()
+{
+    player = new Player();
+    // Menus
+    //zavai.menu_gsm.add_service_toggle(zavai.gsm.gsm, "Start GSM", "Stop GSM");
+    //zavai.menu_gsm.add_service_toggle(zavai.gsm.gprs, "Start GPRS", "Stop GPRS");
+}
+
+}
+}
+}
index fb607fc784758432165c2e0fe484625c2b24a30e..1c3942012a0747a8a0b71e6a31b096e869bb6fb4 100644 (file)
@@ -27,6 +27,7 @@ public class Audio: zavai.Service
 {
     protected Omhacks.Led vibrator;
     protected bool has_vibrator;
+
     /*
        protected dynamic DBus.Object audiodev;
        protected dynamic DBus.Object vibdev;
@@ -48,45 +49,47 @@ public class Audio: zavai.Service
                 "/org/freesmartphone/Device/LED/neo1973_vibrator",
                 "org.freesmartphone.Device.LED");
 */
-        if (has_vibrator)
-        {
-            zavai.log.warning("audio: can notify alarm triggers");
-            clock.alarm_trigger_queue.triggered += on_alarm_trigger;
-            clock.alarm_trigger_queue.acked += on_alarm_done;
-            clock.alarm_trigger_queue.canceled += on_alarm_done;
-        } else {
-            zavai.log.warning("audio: no way to notify alarm triggers");
-        }
+        clock.alarm_trigger_queue.triggered += on_alarm_trigger;
+        clock.alarm_trigger_queue.acked += on_alarm_done;
+        clock.alarm_trigger_queue.canceled += on_alarm_done;
     }
 
     public void on_alarm_trigger(clock.AlarmTriggerInfo info)
     {
-        zavai.log.debug("Start vibrator");
-        vibrator.brightness = 256;
-        // FIXME: is there a better way? I hope there is a better way. Please
-        // tell me there is a better way.
-        var trig = "timer";
-        for (int i = 0; ; ++i)
+        zavai.log.debug("Make noise for alarm");
+        if (has_vibrator)
         {
-            vibrator.trigger[i] = (char)trig[i];
-            if (trig[i] == 0) break;
+            vibrator.brightness = 256;
+            // FIXME: is there a better way? I hope there is a better way. Please
+            // tell me there is a better way.
+            var trig = "timer";
+            for (int i = 0; ; ++i)
+            {
+                vibrator.trigger[i] = (char)trig[i];
+                if (trig[i] == 0) break;
+            }
+            vibrator.delay_on = 200;
+            vibrator.delay_off = 300;
+            vibrator.set();
         }
-        vibrator.delay_on = 200;
-        vibrator.delay_off = 300;
-        vibrator.set();
+        soundplayer.play(config.ringtone_alarm, true);
     }
 
     public void on_alarm_done(clock.AlarmTriggerInfo info)
     {
-        zavai.log.debug("Stop vibrator");
-        var trig = "none";
-        for (int i = 0; ; ++i)
+        zavai.log.debug("Stop noise for alarm");
+        if (has_vibrator)
         {
-            vibrator.trigger[i] = (char)trig[i];
-            if (trig[i] == 0) break;
+            var trig = "none";
+            for (int i = 0; ; ++i)
+            {
+                vibrator.trigger[i] = (char)trig[i];
+                if (trig[i] == 0) break;
+            }
+            vibrator.brightness = 0;
+            vibrator.set();
         }
-        vibrator.brightness = 0;
-        vibrator.set();
+        soundplayer.stop();
     }
 
 /*
@@ -105,11 +108,117 @@ public class Audio: zavai.Service
 */
 }
 
+public class Player: zavai.Resource, Object
+{
+    protected Gst.Element player;
+    protected bool playing;
+    protected Player slave;
+    protected Player master;
+    protected bool loop;
+    protected string uri;
+    public signal void state_changed(Gst.State new_state);
+
+    public Player()
+    {
+        slave = null;
+        master = null;
+        player = Gst.ElementFactory.make("playbin", null);
+        playing = false;
+        loop = false;
+        var bus = player.get_bus();
+        bus.add_signal_watch();
+        bus.message += on_message;
+    }
+
+    public void set_slave(Player player)
+    {
+        slave = player;
+        slave.master = this;
+    }
+
+    public void play(string uri, bool loop = false)
+    {
+stderr.printf("Playing %s\n", uri);
+        this.uri = uri;
+
+        if (slave != null && slave.playing)
+            slave.pause();
+
+        player.set_property("uri", uri);
+        player.set_state(master != null && master.playing ? Gst.State.PAUSED : Gst.State.PLAYING);
+        playing = true;
+        this.loop = loop;
+    }
+
+    public Gst.State get_state()
+    {
+        Gst.State state;
+        Gst.State pending;
+
+        player.get_state(out state, out pending, (Gst.ClockType)Gst.CLOCK_TIME_NONE);
+
+        return state;
+    }
+
+    public void pause()
+    {
+        player.set_state(Gst.State.PAUSED);
+        state_changed(Gst.State.PAUSED);
+    }
+
+    public void resume()
+    {
+        player.set_state(Gst.State.PLAYING);
+        state_changed(Gst.State.PLAYING);
+    }
+
+    public void restart()
+    {
+        player.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT, 0);
+        player.set_state(Gst.State.PLAYING);
+        state_changed(Gst.State.PLAYING);
+    }
+
+    public void stop()
+    {
+        playing = false;
+        player.set_state(Gst.State.NULL);
+        state_changed(Gst.State.NULL);
+
+        // Resume slave after we are done
+        if (slave != null && slave.playing)
+            slave.resume();
+    }
+
+    protected void on_message(Gst.Message message)
+    {
+        if (message.type == Gst.MessageType.EOS)
+        {
+            if (loop)
+                restart();
+            else
+                stop();
+        }
+    }
+
+    public void shutdown()
+    {
+        stop();
+    }
+}
+
 public Audio audio = null;
+public Player musicplayer = null;
+public Player soundplayer = null;
 
 public void init()
 {
     audio = new Audio();
+    musicplayer = new Player();
+    soundplayer = new Player();
+    soundplayer.set_slave(musicplayer);
+    zavai.registry.register(musicplayer);
+    zavai.registry.register(soundplayer);
 }
 
 }
index dcc352eb56e57144a144a53a37da4960c061e4b3..3f9ff416dd663e1b55f7af05a1ed42194830bb7a 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * config - zavai configuration
  *
- * Copyright (C) 2009  Enrico Zini <enrico@enricozini.org>
+ * Copyright (C) 2009--2010  Enrico Zini <enrico@enricozini.org>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -145,6 +145,13 @@ public class Config
         set { _aux_button_keycode = set_int("aux_button_keycode", value); }
     }
 
+    private string _ringtone_alarm;
+    public string ringtone_alarm
+    {
+        get { return _ringtone_alarm; }
+        set { _ringtone_alarm = set_string("ringtone_alarm", value); }
+    }
+
     public int backlight_max
     {
         get;
@@ -186,6 +193,7 @@ public class Config
         _sim_pin = get_string("sim_pin");
         _power_button_keycode = get_int("power_button_keycode");
         _aux_button_keycode = get_int("aux_button_keycode");
+        _ringtone_alarm = get_string("ringtone_alarm");
     }
 
     public Config()
@@ -210,6 +218,7 @@ public class Config
         backlight_max = 15;
         power_button_keycode = 124;
         aux_button_keycode = 177;
+        ringtone_alarm = "file:///usr/share/sounds/yue-fso/lec1.ogg";
 
         // Read config
         if (lua.do_file(homedir + "/config"))
index 3c3c55d0eed8bb823fe9a145742e3117de1a01cc..50a2c39386795047861889b5d7de5d26308f0aa9 100644 (file)
@@ -24,6 +24,7 @@ using GLib;
 
 static int main (string[] args) {
        Gtk.init (ref args);
+    Gst.init (ref args);
 
        // parser = Parser(usage="usage: %prog [options]",
        //                 version="%prog "+ VERSION,
@@ -112,6 +113,7 @@ static int main (string[] args) {
        */
 
        // User interface
+       zavai.ui.music.init();
        zavai.ui.main.init();
        zavai.ui.gps.init();
        zavai.ui.gsm.init();
@@ -172,6 +174,20 @@ static int main (string[] args) {
         zavai.clock.alarm_trigger_queue.enqueue_trigger(alarm);
     }
 
+       if (args.length > 2 && args[1] == "play")
+    {
+        zavai.audio.musicplayer.play("file://" + args[2]);
+
+        // Timeout.add(3 * 1000, () => {
+        //     zavai.audio.soundplayer.play("file:///backup/ciapino/src/openmoocow/data/moo.wav", true);
+        //     Timeout.add(4 * 1000, () => {
+        //         zavai.audio.soundplayer.stop();
+        //         return false;
+        //     });
+        //     return false;
+        // });
+    }
+
        Gtk.main();
 
        // zavai.info("Shutting down")