Adding first code drop
[demorecorder] / src / TrackPipeline.vala
diff --git a/src/TrackPipeline.vala b/src/TrackPipeline.vala
new file mode 100644 (file)
index 0000000..7870696
--- /dev/null
@@ -0,0 +1,313 @@
+/*  Demo Recorder for MAEMO 5
+*   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License version 2,
+*   or (at your option) any later version, as published by the Free
+*   Software Foundation
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*   GNU General Public License for more details
+*
+*   You should have received a copy of the GNU General Public
+*   License along with this program; if not, write to the
+*   Free Software Foundation, Inc.,
+*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+namespace IdWorks {
+
+public class TrackPipeline : GLib.Object {
+  
+  Gst.Bin bin;
+  Gst.Element src;
+  Gst.Element decoder;
+  Gst.Element converter;
+  Gst.Element equalizer;
+  Gst.Element panorama;
+  Gst.Element volume;
+  Gst.Element sink;
+  string track_title;
+  Gst.Query duration_query;
+  Gst.Query position_query;
+  int64 position;
+  int64 duration;
+  
+  public void set_sink(Gst.Element sink) {
+    this.sink = sink;
+  }
+  
+  public void link_track(Gst.Element adder) {
+    volume.link(adder);
+  }
+  
+  //define a delegate function to handle tags
+  private static Gst.TagForeachFunc handle_tags_delegate;
+  
+  public signal void position_duration(int64 position, int64 duration);
+  public signal void tag_parsed(string tag, string val);
+  public signal void end_of_stream();
+  public signal void stream_error(string msg);
+  
+  public TrackPipeline(string name) {
+    //set the tag handling delegate
+    this.handle_tags_delegate = this.handle_tags;
+    //set some default values
+    this.position=0;
+    this.duration=0;
+    this.construct_bin(name);
+    //define the queries
+    duration_query = new Gst.Query.duration(Gst.Format.TIME);
+    position_query = new Gst.Query.position(Gst.Format.TIME);
+  }
+  
+  private void construct_bin(string name) {
+    bin      = new Gst.Bin(name);
+    src      = Gst.ElementFactory.make("fakesrc", "source");
+    bin.add(src);
+    decoder  = Gst.ElementFactory.make("decodebin", "decoder");
+    decoder.connect ("swapped-object-signal::new-decoded-pad", new_decoded_pad, this);
+    bin.add(decoder);
+    converter = Gst.ElementFactory.make("audioconvert", "converter");
+    bin.add(converter);
+    panorama = Gst.ElementFactory.make("audiopanorama", "panorama");
+    bin.add(panorama);
+    //echo     = ElementFactory.make("audioecho", "echo");
+    //echo.set_property("delay", 500000000);
+    //echo.set_property("feedback", 0.4);
+    //echo.set_property("intensity", 0.5);
+    //echo.set_property("max-delay", 500000000);
+    //pipeline.add(echo);
+    //converter2 = ElementFactory.make("audioconvert", "converter2");
+    //pipeline.add(converter2);
+    equalizer  = Gst.ElementFactory.make("equalizer-10bands", "eq");
+    bin.add(equalizer);
+    volume    = Gst.ElementFactory.make("volume", "vol");
+    bin.add(volume);
+    sink     = Gst.ElementFactory.make("autoaudiosink", "audio-output");
+    /* TODO How does bin handle this */ //pipeline.add(sink);
+    converter.link(equalizer);
+    equalizer.link(panorama);
+    panorama.link(volume);
+    /* TODO How does bin handle this */ //volume.link(sink);
+    //we need to receive signals from the pipelines bus
+    Gst.Bus bus = this.bin.get_bus( );
+    //make sure we are watching the signals on the bus
+    bus.add_signal_watch();
+    //what do we do when a tag is part of the bus signal?
+    bus.message.connect(
+      (bus,message)=> {
+        this.bus_message(message);
+      }
+    );
+  }
+  
+  
+  public void foreach_tag (Gst.TagList list, string tag) {
+    stdout.printf("foreach_tag called\n");
+    stdout.flush();
+    switch (tag) { 
+      case "title":
+        list.get_string (tag, out track_title);
+        stdout.printf ("%s\n", track_title);
+        stdout.flush();
+        //info_label.set_markup("<b>" + track_title + "</b><br>" + track_uri);
+        break;
+      default:
+        break;
+    }
+  }
+  
+  public void handle_tags(Gst.TagList list, string tag) {
+    if (tag != null) {
+      switch (tag) {
+        case "artist":
+        case "title":
+        case "album":
+        case "channel-mode":
+        case "comment":
+        case "audio-codec":
+          string val="Unknown";
+          list.get_string(tag,out val);
+          tag_parsed(tag, val);
+          break;
+        case "track-number":
+        case "bitrate":
+          uint val;
+          list.get_uint(tag,out val);
+          //tag_parsed(tag,(string)val);
+          break;
+        default:
+          //stdout.printf("%s\n",tag);
+          //stdout.flush();
+          break;
+      }
+    }
+  }
+  
+  
+  public void bus_message(Gst.Message message) {
+    Gst.TagList tag_list;
+    switch(message.type) {
+      case Gst.MessageType.ERROR:
+        GLib.Error err;
+        string debug;
+        message.parse_error (out err, out debug);
+        stream_error(err.message);
+        break;
+      case Gst.MessageType.TAG:
+        message.parse_tag(out tag_list);
+        //we need to get the key and value from the tag
+        tag_list.foreach( this.handle_tags_delegate );
+        break;
+      case Gst.MessageType.STATE_CHANGED:
+        //stdout.printf("state changed\n");
+        Gst.State oldstate;
+        Gst.State newstate;
+        Gst.State pending;
+        message.parse_state_changed (out oldstate, out newstate, out pending);
+        //stdout.printf ("%s->%s:%s\n", oldstate.to_string(), newstate.to_string(), pending.to_string());
+        //stdout.flush();
+        break;
+      case Gst.MessageType.EOS:
+        end_of_stream();
+        break;
+      default:
+        break;
+    }
+  }
+  
+  public bool bus_callback (Gst.Bus bus, Gst.Message message) {
+    stdout.printf("state changed\n");
+    stdout.flush();
+    switch (message.type) {
+      case Gst.MessageType.ERROR:
+        GLib.Error err;
+        string debug;
+        message.parse_error (out err, out debug);
+        stdout.printf("%s\n", err.message);
+        stdout.flush();
+        break;
+      case Gst.MessageType.EOS:
+        stdout.printf ("end of stream\n");
+        stdout.flush();
+        break;
+      case Gst.MessageType.STATE_CHANGED:
+        Gst.State oldstate;
+        Gst.State newstate;
+        Gst.State pending;
+        message.parse_state_changed (out oldstate, out newstate, out pending);
+        stdout.printf ("%s->%s:%s\n", oldstate.to_string(), newstate.to_string(), pending.to_string());
+        stdout.flush();
+        break;
+      case Gst.MessageType.TAG:
+        Gst.TagList tag_list;
+        message.parse_tag (out tag_list);
+        tag_list.foreach (foreach_tag);
+        break;
+      default:
+        break;
+    }
+    return true;
+  }
+  
+  public void get_duration_info() {
+    bool duration_result,position_result;
+    Gst.Format format = Gst.Format.TIME;
+    duration_result = this.bin.query(this.duration_query);
+    position_result = this.bin.query(this.position_query);
+    if ( duration_result && position_result  ) {
+      this.duration_query.parse_duration(out format, out this.duration);
+      this.position_query.parse_position(out format, out this.position);
+      this.position_duration(this.position,this.duration);
+    }
+  }
+  
+  public void play() {
+    bin.set_state(Gst.State.PLAYING);
+  }
+  
+  public void pause() {
+    bin.set_state(Gst.State.PAUSED);
+  }
+  
+  public void stop() {
+    bin.set_state(Gst.State.READY);
+  }
+  
+  public void move_to(int64 newloc) {
+    if (0 > newloc) newloc = 0;
+    this.converter.seek_simple(Gst.Format.TIME, Gst.SeekFlags.KEY_UNIT | Gst.SeekFlags.FLUSH, newloc);
+    this.play();
+  }
+  
+  public void seek(int percent) {
+    move_to((int64) this.position + (this.duration * percent / 100));
+  }
+  
+  public void seek_forward(int percent) {
+    seek(percent);
+  }
+  
+  public void seek_backward(int percent) {
+    seek(-percent);
+  }
+  
+  public void set_uri(string uri) {
+    this.bin.set_state(Gst.State.NULL);
+    try {
+      //is there a src pipeline?
+      this.bin.remove(this.src);
+    } finally {
+      //do nothing, the src doesn't exist
+    }
+    this.src = Gst.Element.make_from_uri(Gst.URIType.SRC, uri, "my_src");
+    Gst.Bus bus = this.src.get_bus();
+    bus.add_watch (bus_callback);
+    this.bin.add(this.src);
+    this.src.link(this.decoder);
+  }
+  
+  public void set_volume(double val)
+  requires (val >= 0.0 && val <= 10.0) {
+    volume.set_property("volume",val);
+  }
+  
+  public double get_volume() {
+    GLib.Value ret = 0.0;
+    volume.get_property("volume", ref ret);
+    return ret.get_double();
+  }
+  
+  public void set_panorama(double val) 
+  requires (val >= -1.0 && val <= 1.0) {
+    panorama.set_property("panorama", val);
+  }
+  
+  public double get_panorama() {
+    GLib.Value ret = 0.0;
+    panorama.get_property("panorama", ref ret);
+    return ret.get_double();
+  }
+  
+  public void set_eq(int band, double val)
+  requires (band > -1 && band < 10)
+  requires (val >= -24 && val <= 12) {
+    equalizer.set_property("band" + band.to_string(), val);
+  }
+  
+  public double get_eq(int band) {
+    GLib.Value ret = 0.0;
+    equalizer.get_property("band" + band.to_string(), ref ret);
+    return ret.get_double();
+  }
+  
+  public bool new_decoded_pad(Gst.Pad decodebin, bool arg1, void* data) {
+    //link the pad to the audioconverter
+    decodebin.link( converter.get_static_pad("sink") );
+    return true;
+  }
+
+}
+
+}