/* Copyright (C) Arnaud Houdelette 2012-2014 Copyright (C) Emmanuel Langlois 2012-2014 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. 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, see . */ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Threading; using System.Xml; namespace DMX2 { public class SequenceurSon : Sequenceur, IDisposable { SeqSonUI ui=null; static bool gsinit=false; List files = new List(); string curfile = string.Empty; uint volume=100; delegate void Task (); System.Collections.Concurrent.ConcurrentQueue taskQueue = new System.Collections.Concurrent.ConcurrentQueue(); TimeSpan fading=TimeSpan.Zero; TimeSpan fadeLen; int fadeVStart; int fadeVEnd; TimeSpan duration = TimeSpan.Zero; Gst.Element element = null; Gst.State state,pending; Gst.StateChangeReturn scret; public ReadOnlyCollection Files { get { return files.AsReadOnly(); } } public string CurFile { get{return curfile; } } public int Index { get { return files.IndexOf (curfile); } set { if(value>=0 && value< files.Count) lock(this) LoadFile(value); } } public int AddFile (int pos, string file) { lock (this) { files.Insert (pos, file); CommandAdd(pos); return pos; } } public uint Volume { get{ return volume;} set { volume=value; if(element!=null) element["volume"] = (double)volume / 100.0d ; } } public TimeSpan PlayTime { get{ if(element==null) return TimeSpan.Zero; Gst.Format format = Gst.Format.Time ; long cur; element.QueryPosition(ref format, out cur); return TimeSpan.FromMilliseconds(cur/1000000); } set{ if(element==null) return; QueueTask(delegate(){ //Console.WriteLine("Seek"); long pos = (long)(value.TotalMilliseconds) * 1000000; element.Seek (Gst.Format.Time,Gst.SeekFlags.Flush|Gst.SeekFlags.Accurate,pos); }); } } public TimeSpan Duration { get { if(element==null) return TimeSpan.Zero; if(duration == TimeSpan.Zero) { Gst.Format fmt = Gst.Format.Time; long dur; element.QueryDuration(ref fmt,out dur); return TimeSpan.FromMilliseconds(dur/1000000); } return duration; } } public void DelFile (int pos) { lock (this) { files.RemoveAt (pos); CommandRemove(pos); } } void LoadFile (int pos) { //Console.WriteLine("LoadFile"); curfile = files[pos]; Stop(); duration = TimeSpan.Zero; Uri uri = new Uri(curfile); element = Gst.ElementFactory.Make("playbin"); element["uri"] = uri.AbsoluteUri; element.SetState(Gst.State.Paused); element["volume"] = (double)volume / 100.0d ; } public void Play () { if(element == null)return; QueueTask(delegate(){ //Console.WriteLine("Play"); element.SetState(Gst.State.Playing); }); } public void Pause () { if(element == null)return; QueueTask(delegate(){ element.SetState(Gst.State.Paused); }); } public void Stop () { if(element == null)return; element.SetState(Gst.State.Null); element.Dispose(); element=null; Task t; while(taskQueue.TryDequeue (out t)); } static void GstInit () { if(gsinit) return; gsinit=true; string[] args = new string[] {"--gst-disable-segtrap"}; Gst.Application.Init("dmx2", ref args ); } public SequenceurSon () { GstInit(); } #region implemented abstract members of DMX2.Sequenceur public override SequenceurUI GetUI () { if (ui == null) { ui = new SeqSonUI (this); ui.Destroyed += UiDestroyed; } return ui; } void UiDestroyed (object sender, EventArgs e) { ui=null; } public override int ValeurCircuit (Circuit c) { return 0; } public string State { get { if(element == null) return "Stopped"; UpdateState(); if(pending == Gst.State.VoidPending){ return state.ToString(); } if(scret == Gst.StateChangeReturn.Async) return string.Format("{0}=>{1}", state,pending); return string.Format("{0}=>{1} : {2}",state,pending,scret); } } void UpdateState () { scret = element.GetState (out state, out pending, 0UL); } bool CheckAsync () { if (element == null) return false; UpdateState (); return (scret != Gst.StateChangeReturn.Async); } void QueueTask (Task t) { if (CheckAsync ()) { //Console.WriteLine("Sync"); t(); return; } //Console.WriteLine("Queue"); taskQueue.Enqueue(t); } public override void Tick (TimeSpan time) { if (taskQueue.Count>0 && CheckAsync()){ //Console.WriteLine(State); Task task; if(taskQueue.TryDequeue(out task)) { //Console.WriteLine("ASync"); task(); } } if (fading != TimeSpan.Zero) { fading-=time; if(fading<=TimeSpan.Zero){ Volume = (uint)fadeVEnd; fading=TimeSpan.Zero; } else{ Volume = (uint)( fadeVEnd + (fadeVStart-fadeVEnd) * fading.TotalMilliseconds / fadeLen.TotalMilliseconds); } } } public override void Save (System.Xml.XmlElement parent) { System.Xml.XmlElement el = parent.OwnerDocument.CreateElement ("SequenceurSon"); System.Xml.XmlElement xmlEl; parent.AppendChild (el); el.SetAttribute ("id", ID.ToString ()); el.SetAttribute ("name", Name); foreach (string file in files) { el.AppendChild(xmlEl = parent.OwnerDocument.CreateElement ("File")); xmlEl.SetAttribute("path",file); } } public static new SequenceurSon Load (Conduite conduite, System.Xml.XmlElement el) { SequenceurSon seq = new SequenceurSon(); seq.LoadSeq(conduite,el); return seq; } void LoadSeq (Conduite conduite, System.Xml.XmlElement el) { ID = int.Parse (el.GetAttribute ("id")); Name = el.GetAttribute ("name"); foreach (var xf in el.GetElementsByTagName("File")) { System.Xml.XmlElement xfil = xf as System.Xml.XmlElement; string file = xfil.GetAttribute ("path"); files.Add(file); } } static System.Text.RegularExpressions.Regex regexCommand1 = new System.Text.RegularExpressions.Regex( @"(?\d+)?(g(?[\d\.:]+))?(v(?\d+)(,(?\d+))?)?(?(ps|p|s))?", System.Text.RegularExpressions.RegexOptions.Compiled); static System.Text.RegularExpressions.Regex regexCommand2 = new System.Text.RegularExpressions.Regex( @"^(?\d+)(?[gvp](.+)?)?", System.Text.RegularExpressions.RegexOptions.Compiled); public override void Command (string command) { //Console.WriteLine("Command"); lock (this) { var cmd = regexCommand1.Match (command); if (cmd.Success) { if (cmd.Groups ["file"].Success) { int filen = int.Parse (cmd.Groups ["file"].Value) - 1; if (filen >= files.Count) return; Index = filen; if (ui != null) ui.updateflag = true; } if (cmd.Groups ["goto"].Success) { string[] formats = new string[]{ @"m\:s\.f", @"m\:s", @"h\:m\:s",@"h\:m\:s\.f" }; TimeSpan ts; if(TimeSpan.TryParseExact( cmd.Groups ["goto"].Value,formats,null,out ts)) { PlayTime = ts; } } if (cmd.Groups ["volume"].Success) { uint vol = uint.Parse (cmd.Groups ["volume"].Value); if(vol>100) vol=100; if(cmd.Groups ["fade"].Success) { fadeLen = fading = TimeSpan.FromMilliseconds (100*int.Parse (cmd.Groups ["fade"].Value)); fadeVStart = (int)volume; fadeVEnd = (int)vol; } else{ fading = TimeSpan.Zero; Volume = vol; if (ui != null) ui.updateflag = true; } } if (cmd.Groups ["play"].Success) { switch(cmd.Groups ["play"].Value) { case "p": Play(); break; case "ps": Pause(); break; case "s": Stop(); break; } } } } } void CommandAdd (int index) { lock (Conduite.Courante.SequenceurMaitre) { string[] commands = Conduite.Courante.SequenceurMaitre.GetCommands (this); for (int i = 0; i < commands.Length; i++) { var cmd = regexCommand2.Match(commands[i]); if(cmd.Success){ int ef = int.Parse(cmd.Groups["file"].Value); if (ef-1>index) { ef++; commands[i] = ef.ToString() + cmd.Groups["params"].Value; } } } Conduite.Courante.SequenceurMaitre.SetCommands(this,commands); } } void CommandRemove (int index) { lock (Conduite.Courante.SequenceurMaitre) { string[] commands = Conduite.Courante.SequenceurMaitre.GetCommands (this); for (int i = 0; i < commands.Length; i++) { var cmd = regexCommand2.Match(commands[i]); if(cmd.Success){ int ef = int.Parse(cmd.Groups["file"].Value); if (ef-1 == index) commands[i] = string.Empty; else if (ef-1>index) { ef--; commands[i] = ef.ToString() + cmd.Groups["params"].Value; } } } Conduite.Courante.SequenceurMaitre.SetCommands(this,commands); } } void CommandSwap (int index) { lock (Conduite.Courante.SequenceurMaitre) { string[] commands = Conduite.Courante.SequenceurMaitre.GetCommands (this); // numeros a swapper int a = index+1; int b = index+2; for (int i = 0; i < commands.Length; i++) { var cmd = regexCommand2.Match(commands[i]); if(cmd.Success){ int ef = int.Parse(cmd.Groups["file"].Value); if (ef == a) commands[i] = b.ToString() + cmd.Groups["params"].Value; if (ef == b) commands[i] = a.ToString() + cmd.Groups["params"].Value; } } Conduite.Courante.SequenceurMaitre.SetCommands(this,commands); } } #endregion #region IDisposable implementation void IDisposable.Dispose () { if(element==null)return; element.SetState(Gst.State.Null); element.Dispose(); element=null; } #endregion } }