Reworked track settings.
[demorecorder] / src / TrackPipeline.vala
1 /*  Demo Recorder for MAEMO 5
2 *   Copyright (C) 2010 Dru Moore <usr@dru-id.co.uk>
3 *   This program is free software; you can redistribute it and/or modify
4 *   it under the terms of the GNU General Public License version 2,
5 *   or (at your option) any later version, as published by the Free
6 *   Software Foundation
7 *
8 *   This program is distributed in the hope that it will be useful,
9 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
10 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 *   GNU General Public License for more details
12 *
13 *   You should have received a copy of the GNU General Public
14 *   License along with this program; if not, write to the
15 *   Free Software Foundation, Inc.,
16 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 */
18 namespace IdWorks {
19
20 public class TrackPipeline : GLib.Object {
21   
22   Gst.Bin bin;
23   Gst.Element src;
24   Gst.Element decoder;
25   Gst.Element converter;
26   Gst.Element equalizer;
27   Gst.Element panorama;
28   Gst.Element volume;
29   Gst.Element sink;
30   string track_title;
31   Gst.Query duration_query;
32   Gst.Query position_query;
33   int64 position;
34   int64 duration;
35   
36   public void set_sink(Gst.Element sink) {
37     this.sink = sink;
38   }
39   
40   public void link_track(Gst.Element adder) {
41     volume.link(adder);
42   }
43   
44   //define a delegate function to handle tags
45   private static Gst.TagForeachFunc handle_tags_delegate;
46   
47   public signal void position_duration(int64 position, int64 duration);
48   public signal void tag_parsed(string tag, string val);
49   public signal void end_of_stream();
50   public signal void stream_error(string msg);
51   
52   public TrackPipeline(string name) {
53     //set the tag handling delegate
54     this.handle_tags_delegate = this.handle_tags;
55     //set some default values
56     this.position=0;
57     this.duration=0;
58     this.construct_bin(name);
59     //define the queries
60     duration_query = new Gst.Query.duration(Gst.Format.TIME);
61     position_query = new Gst.Query.position(Gst.Format.TIME);
62   }
63   
64   private void construct_bin(string name) {
65     bin      = new Gst.Bin(name);
66     src      = Gst.ElementFactory.make("fakesrc", "source");
67     bin.add(src);
68     decoder  = Gst.ElementFactory.make("decodebin", "decoder");
69     decoder.connect ("swapped-object-signal::new-decoded-pad", new_decoded_pad, this);
70     bin.add(decoder);
71     converter = Gst.ElementFactory.make("audioconvert", "converter");
72     bin.add(converter);
73     panorama = Gst.ElementFactory.make("audiopanorama", "panorama");
74     bin.add(panorama);
75     //echo     = ElementFactory.make("audioecho", "echo");
76     //echo.set_property("delay", 500000000);
77     //echo.set_property("feedback", 0.4);
78     //echo.set_property("intensity", 0.5);
79     //echo.set_property("max-delay", 500000000);
80     //pipeline.add(echo);
81     //converter2 = ElementFactory.make("audioconvert", "converter2");
82     //pipeline.add(converter2);
83     equalizer  = Gst.ElementFactory.make("equalizer-10bands", "eq");
84     bin.add(equalizer);
85     volume    = Gst.ElementFactory.make("volume", "vol");
86     bin.add(volume);
87     sink     = Gst.ElementFactory.make("autoaudiosink", "audio-output");
88     /* TODO How does bin handle this */ //pipeline.add(sink);
89     converter.link(equalizer);
90     equalizer.link(panorama);
91     panorama.link(volume);
92     /* TODO How does bin handle this */ //volume.link(sink);
93     //we need to receive signals from the pipelines bus
94     Gst.Bus bus = this.bin.get_bus( );
95     //make sure we are watching the signals on the bus
96     bus.add_signal_watch();
97     //what do we do when a tag is part of the bus signal?
98     bus.message.connect(
99       (bus,message)=> {
100         this.bus_message(message);
101       }
102     );
103   }
104   
105   
106   public void foreach_tag (Gst.TagList list, string tag) {
107     stdout.printf("foreach_tag called\n");
108     stdout.flush();
109     switch (tag) { 
110       case "title":
111         list.get_string (tag, out track_title);
112         stdout.printf ("%s\n", track_title);
113         stdout.flush();
114         //info_label.set_markup("<b>" + track_title + "</b><br>" + track_uri);
115         break;
116       default:
117         break;
118     }
119   }
120   
121   public void handle_tags(Gst.TagList list, string tag) {
122     if (tag != null) {
123       switch (tag) {
124         case "artist":
125         case "title":
126         case "album":
127         case "channel-mode":
128         case "comment":
129         case "audio-codec":
130           string val="Unknown";
131           list.get_string(tag,out val);
132           tag_parsed(tag, val);
133           break;
134         case "track-number":
135         case "bitrate":
136           uint val;
137           list.get_uint(tag,out val);
138           //tag_parsed(tag,(string)val);
139           break;
140         default:
141           //stdout.printf("%s\n",tag);
142           //stdout.flush();
143           break;
144       }
145     }
146   }
147   
148   
149   public void bus_message(Gst.Message message) {
150     Gst.TagList tag_list;
151     switch(message.type) {
152       case Gst.MessageType.ERROR:
153         GLib.Error err;
154         string debug;
155         message.parse_error (out err, out debug);
156         stream_error(err.message);
157         break;
158       case Gst.MessageType.TAG:
159         message.parse_tag(out tag_list);
160         //we need to get the key and value from the tag
161         tag_list.foreach( this.handle_tags_delegate );
162         break;
163       case Gst.MessageType.STATE_CHANGED:
164         //stdout.printf("state changed\n");
165         Gst.State oldstate;
166         Gst.State newstate;
167         Gst.State pending;
168         message.parse_state_changed (out oldstate, out newstate, out pending);
169         //stdout.printf ("%s->%s:%s\n", oldstate.to_string(), newstate.to_string(), pending.to_string());
170         //stdout.flush();
171         break;
172       case Gst.MessageType.EOS:
173         end_of_stream();
174         break;
175       default:
176         break;
177     }
178   }
179   
180   public bool bus_callback (Gst.Bus bus, Gst.Message message) {
181     stdout.printf("state changed\n");
182     stdout.flush();
183     switch (message.type) {
184       case Gst.MessageType.ERROR:
185         GLib.Error err;
186         string debug;
187         message.parse_error (out err, out debug);
188         stdout.printf("%s\n", err.message);
189         stdout.flush();
190         break;
191       case Gst.MessageType.EOS:
192         stdout.printf ("end of stream\n");
193         stdout.flush();
194         break;
195       case Gst.MessageType.STATE_CHANGED:
196         Gst.State oldstate;
197         Gst.State newstate;
198         Gst.State pending;
199         message.parse_state_changed (out oldstate, out newstate, out pending);
200         stdout.printf ("%s->%s:%s\n", oldstate.to_string(), newstate.to_string(), pending.to_string());
201         stdout.flush();
202         break;
203       case Gst.MessageType.TAG:
204         Gst.TagList tag_list;
205         message.parse_tag (out tag_list);
206         tag_list.foreach (foreach_tag);
207         break;
208       default:
209         break;
210     }
211     return true;
212   }
213   
214   public void get_duration_info() {
215     bool duration_result,position_result;
216     Gst.Format format = Gst.Format.TIME;
217     duration_result = this.bin.query(this.duration_query);
218     position_result = this.bin.query(this.position_query);
219     if ( duration_result && position_result  ) {
220       this.duration_query.parse_duration(out format, out this.duration);
221       this.position_query.parse_position(out format, out this.position);
222       this.position_duration(this.position,this.duration);
223     }
224   }
225   
226   public void play() {
227     bin.set_state(Gst.State.PLAYING);
228   }
229   
230   public void pause() {
231     bin.set_state(Gst.State.PAUSED);
232   }
233   
234   public void stop() {
235     bin.set_state(Gst.State.READY);
236   }
237   
238   public void move_to(int64 newloc) {
239     if (0 > newloc) newloc = 0;
240     this.converter.seek_simple(Gst.Format.TIME, Gst.SeekFlags.KEY_UNIT | Gst.SeekFlags.FLUSH, newloc);
241     this.play();
242   }
243   
244   public void seek(int percent) {
245     move_to((int64) this.position + (this.duration * percent / 100));
246   }
247   
248   public void seek_forward(int percent) {
249     seek(percent);
250   }
251   
252   public void seek_backward(int percent) {
253     seek(-percent);
254   }
255   
256   public void set_uri(string uri) {
257     this.bin.set_state(Gst.State.NULL);
258     try {
259       //is there a src pipeline?
260       this.bin.remove(this.src);
261     } finally {
262       //do nothing, the src doesn't exist
263     }
264     this.src = Gst.Element.make_from_uri(Gst.URIType.SRC, uri, "my_src");
265     Gst.Bus bus = this.src.get_bus();
266     bus.add_watch (bus_callback);
267     this.bin.add(this.src);
268     this.src.link(this.decoder);
269   }
270   
271   public void set_volume(double val)
272   requires (val >= 0.0 && val <= 10.0) {
273     volume.set_property("volume",val);
274   }
275   
276   public double get_volume() {
277     GLib.Value ret = 0.0;
278     volume.get_property("volume", ref ret);
279     return ret.get_double();
280   }
281   
282   public void set_panorama(double val) 
283   requires (val >= -1.0 && val <= 1.0) {
284     panorama.set_property("panorama", val);
285   }
286   
287   public double get_panorama() {
288     GLib.Value ret = 0.0;
289     panorama.get_property("panorama", ref ret);
290     return ret.get_double();
291   }
292   
293   public void set_eq(int band, double val)
294   requires (band > -1 && band < 10)
295   requires (val >= -24 && val <= 12) {
296     equalizer.set_property("band" + band.to_string(), val);
297   }
298   
299   public double get_eq(int band) {
300     GLib.Value ret = 0.0;
301     equalizer.get_property("band" + band.to_string(), ref ret);
302     return ret.get_double();
303   }
304   
305   public bool new_decoded_pad(Gst.Pad decodebin, bool arg1, void* data) {
306     //link the pad to the audioconverter
307     decodebin.link( converter.get_static_pad("sink") );
308     return true;
309   }
310
311 }
312
313 }