/* 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; } } }