/* 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.Diagnostics; using System.Xml; using System.Globalization; namespace DMX2 { public class SequenceurSon : Sequenceur, IDisposable { SeqSonUI ui=null; Process mplayerProcess = null; List files = new List(); string curfile = string.Empty; uint volume=100; TimeSpan fading=TimeSpan.Zero; TimeSpan fadeLen; int fadeVStart; int fadeVEnd; TimeSpan duration = TimeSpan.Zero; TimeSpan position = TimeSpan.Zero; bool repeat = false; actionEventTargetEx volumeEventTarget=null; public ReadOnlyCollection Files { get { return files.AsReadOnly(); } } public string CurFile { get{return curfile; } } public int Index { get { return files.IndexOf (curfile); } set { repeat = false; if(value>=0 && value< files.Count) lock(this) LoadFile(value); } } bool Paused{ get; set;} bool CheckMplayer(){ if (mplayerProcess == null || mplayerProcess.HasExited) { if (mplayerProcess != null) mplayerProcess.Dispose (); ProcessStartInfo startinfo = new ProcessStartInfo (); startinfo.FileName = "mplayer"; startinfo.Arguments = "-quiet -idle -slave"; startinfo.RedirectStandardInput = true; startinfo.RedirectStandardOutput = true; startinfo.UseShellExecute = false; mplayerProcess = Process.Start (startinfo); mplayerProcess.OutputDataReceived += ProcessMplayerOut; mplayerProcess.BeginOutputReadLine (); return !mplayerProcess.HasExited; } return true; } void ProcessMplayerOut (object sender, DataReceivedEventArgs e) { string data = e.Data; if (data == null) return; if (data.StartsWith ("ANS_LENGTH")) { string d = data.Substring (11); double dData; if(double.TryParse(d,NumberStyles.Number,CultureInfo.InvariantCulture,out dData)) duration = TimeSpan.FromSeconds( dData ); } if (data.StartsWith ("ANS_TIME_POSITION")) { string d = data.Substring (18); double dData; if(double.TryParse(d,NumberStyles.Number,CultureInfo.InvariantCulture,out dData)) position = TimeSpan.FromSeconds( dData ); } Console.WriteLine ("data => {0}", data); } public int AddFile (int pos, string file) { lock (this) { if(files.Contains(file)) return -1; files.Insert (pos, file); CommandAdd(pos); return pos; } } public uint Volume { get{ return volume;} set { volume=value; mplayerProcess.StandardInput.WriteLine ("volume {0} 1", volume); volumeEventTarget.FeedBack (); } } public TimeSpan PlayTime { get{ if(mplayerProcess!=null && !mplayerProcess.HasExited) mplayerProcess.StandardInput.WriteLine ("get_time_pos"); return position; } set{ if (CheckMplayer ()) { mplayerProcess.StandardInput.WriteLine ("seek {0} 2", value.TotalMilliseconds / 1000); } } } public TimeSpan Duration { get { 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(); mplayerProcess.StandardInput.WriteLine ("volume 0 1"); mplayerProcess.StandardInput.WriteLine ("loadfile \"{0}\" 0", curfile); mplayerProcess.StandardInput.WriteLine ("get_time_length"); //mplayerProcess.StandardInput.WriteLine ("pausing seek 0 2"); mplayerProcess.StandardInput.WriteLine ("pause"); mplayerProcess.StandardInput.WriteLine ("seek 0 2"); mplayerProcess.StandardInput.WriteLine ("volume {0} 1", volume); Paused = true; } public void Play () { if (curfile==null || !Paused) return; if (!CheckMplayer ()) return; mplayerProcess.StandardInput.WriteLine ("pause"); Paused = false; } public void Pause () { if (curfile==null || Paused) return; if (!CheckMplayer ()) return; mplayerProcess.StandardInput.WriteLine ("pause"); Paused = true; } public void Stop () { CheckMplayer (); mplayerProcess.StandardInput.WriteLine ("stop"); } public SequenceurSon () { volumeEventTarget = new actionEventTargetEx ( delegate(EventData data) { Volume = (uint)(100 * data.value / 255); return true; }, delegate{ return (int)(Volume * 255 /100); }, true ); } #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 { return string.Empty; } } void UpdateState () { } public override void Tick (TimeSpan time) { 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); } } if (repeat) { } } 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))?(?(r))?", System.Text.RegularExpressions.RegexOptions.Compiled); static System.Text.RegularExpressions.Regex regexCommand2 = new System.Text.RegularExpressions.Regex( @"^(?\d+)(?[gvpsr](.+)?)?", 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; } } if (cmd.Groups ["repeat"].Success) { repeat = true; } } } } 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 (mplayerProcess != null && !mplayerProcess.HasExited) { mplayerProcess.StandardInput.WriteLine ("quit"); if (!mplayerProcess.WaitForExit (1000)) mplayerProcess.Kill (); } } #endregion } }