Stacked sound player works
[gregoa/zavai.git] / src / audio.vala
index c09739a4e2a61ac0aed94a283ac8fc4f1fca3a3e..51790d0aed63f4d05da2422febda74f0092175c8 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * audio - audio resource for zavai
  *
- * 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
@@ -25,8 +25,6 @@ namespace audio {
 
 public class Audio: zavai.Service
 {
-    protected Omhacks.Led vibrator;
-    protected bool has_vibrator;
     /*
        protected dynamic DBus.Object audiodev;
        protected dynamic DBus.Object vibdev;
@@ -36,8 +34,6 @@ public class Audio: zavai.Service
     {
         Object(name: "audio");
 
-        has_vibrator = (vibrator.init("neo1973:vibrator") == 0);
-
 /*
         audiodev = zavai.registry.sbus.get_object(
                 "org.freesmartphone.odeviced",
@@ -48,68 +44,241 @@ public class Audio: zavai.Service
                 "/org/freesmartphone/Device/LED/neo1973_vibrator",
                 "org.freesmartphone.Device.LED");
 */
-        if (has_vibrator)
+    }
+}
+
+public class PlayerState : Object
+{
+    protected SoundPlayer soundplayer;
+    protected Gst.Element player;
+    public string owner;
+    public string uri;
+    public bool loop;
+
+    public PlayerState(string owner, string uri, bool loop=false)
+    {
+        soundplayer = null;
+
+        this.owner = owner;
+        this.uri = uri;
+        this.loop = loop;
+
+        player = Gst.ElementFactory.make("playbin", null);
+        var bus = player.get_bus();
+        bus.add_signal_watch();
+        bus.message += on_message;
+
+        player.set_property("uri", uri);
+        player.set_state(Gst.State.PAUSED);
+    }
+
+    public void register(SoundPlayer sp)
+    {
+        soundplayer = sp;
+    }
+
+    public void stop()
+    {
+        player.set_state(Gst.State.NULL);
+    }
+
+    public void pause()
+    {
+        player.set_state(Gst.State.PAUSED);
+    }
+
+    public void resume()
+    {
+        player.set_state(Gst.State.PLAYING);
+    }
+
+    protected void on_message(Gst.Message message)
+    {
+        if (message.type == Gst.MessageType.EOS)
+        {
+            if (loop)
+            {
+                player.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT, 0);
+                player.set_state(Gst.State.PLAYING);
+            }
+            else if (soundplayer != null)
+                soundplayer.pop_state(owner);
+        }
+    }
+}
+
+public class SoundPlayer : zavai.Resource, Object
+{
+    protected List<PlayerState> states;
+    protected MusicPlayer slave;
+    protected bool slave_was_playing;
+
+    public SoundPlayer()
+    {
+        states = new List<PlayerState>();
+    }
+
+    public void set_slave(MusicPlayer player)
+    {
+        slave = player;
+        slave.master = this;
+    }
+
+    public bool playing()
+    {
+        return states != null;
+    }
+
+    protected void stop_playing()
+    {
+        if (slave_was_playing)
+            slave.resume();
+    }
+
+    public void push_state(PlayerState state)
+    {
+        // Save current playing position
+        if (states == null)
+        {
+            // We start playing: see about preempting slave
+            slave_was_playing = (slave != null && slave.playing);
+            if (slave_was_playing)
+                slave.pause();
+        } else
+            // We were playing: pause previous sound
+            states.data.pause();
+        state.register(this);
+        states.prepend(state);
+        states.data.resume();
+    }
+
+    public void pop_state(string owner)
+    {
+        // Handle empty list
+        if (states == null) return;
+
+        // Track if the list head changed
+        weak List<PlayerState> old_top = states;
+
+        // Remove state "name" from the stack
+        for (weak List<PlayerState> i = states; i != null; i = i.next)
+            if (i.data.owner == owner)
+            {
+                i.data.stop();
+                states.delete_link(i);
+                break;
+            }
+
+        // If the list head changed, put into action the new top state
+        if (states != old_top)
         {
-            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");
+            if (states == null)
+                stop_playing();
+            else
+                states.data.resume();
         }
     }
 
-    public void on_alarm_trigger(clock.AlarmTriggerInfo info)
+    public void shutdown()
     {
-        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)
+        while (states != null)
         {
-            vibrator.trigger[i] = (char)trig[i];
-            if (trig[i] == 0) break;
+            states.data.stop();
+            states.delete_link(states);
         }
-        vibrator.delay_on = 200;
-        vibrator.delay_off = 300;
-        vibrator.set();
+        stop_playing();
+    }
+}
+
+public class MusicPlayer: zavai.Resource, Object
+{
+    protected Gst.Element player;
+    public SoundPlayer master;
+    public signal void state_changed(Gst.State new_state);
+    public string uri;
+    public bool playing;
+
+    public MusicPlayer()
+    {
+        master = null;
+        uri = null;
+        playing = false;
+        player = Gst.ElementFactory.make("playbin", null);
+        var bus = player.get_bus();
+        bus.add_signal_watch();
+        bus.message += on_message;
+    }
+
+    public void play(string uri)
+    {
+        this.uri = uri;
+        playing = true;
+        player.set_property("uri", uri);
+        player.set_state(master != null && master.playing() ? Gst.State.PAUSED : Gst.State.PLAYING);
+    }
+
+    protected void stop_playing()
+    {
+        player.set_state(Gst.State.NULL);
+        state_changed(Gst.State.NULL);
+    }
+
+    public Gst.State get_state()
+    {
+        return player.current_state;
+    }
+
+    public void pause()
+    {
+        playing = false;
+        player.set_state(Gst.State.PAUSED);
+        state_changed(Gst.State.PAUSED);
     }
 
-    public void on_alarm_done(clock.AlarmTriggerInfo info)
+    public void resume()
     {
-        zavai.log.debug("Stop vibrator");
-        var trig = "none";
-        for (int i = 0; ; ++i)
+        playing = true;
+        if (master == null || !master.playing())
         {
-            vibrator.trigger[i] = (char)trig[i];
-            if (trig[i] == 0) break;
+            player.set_state(Gst.State.PLAYING);
+            state_changed(Gst.State.PLAYING);
         }
-        vibrator.brightness = 0;
-        vibrator.set();
     }
 
-/*
-    public void notify_alarm(zavai.clock.Alarm a)
-    {
-        // Wiggle screen to turn on backlight
-        zavai.ui.power.backlight.wiggle();
-        try {
-            // Method does not exist in this frameworkd
-            vibdev.BlinkSeconds(5, 500, 200);
-        } catch (Error e) {
-            zavai.log.error("Cannot blink vibrator: " + e.message);
+    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);
+    }
+
+    protected void on_message(Gst.Message message)
+    {
+        if (message.type == Gst.MessageType.EOS)
+        {
+            playing = false;
+            stop_playing();
         }
-        // TODO: play music?
     }
-*/
+
+    public void shutdown()
+    {
+        stop_playing();
+    }
 }
 
 public Audio audio = null;
+public SoundPlayer soundplayer = null;
+public MusicPlayer musicplayer = null;
 
 public void init()
 {
     audio = new Audio();
+    musicplayer = new MusicPlayer();
+    soundplayer = new SoundPlayer();
+    soundplayer.set_slave(musicplayer);
+    zavai.registry.register(musicplayer);
+    zavai.registry.register(soundplayer);
 }
 
 }