loupiottes/DMX-2.0/SequenceurSon.cs
2014-12-30 16:44:26 +00:00

461 lines
10 KiB
C#

/*
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 <http://www.gnu.org/licenses/>.
*/
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<string> files = new List<string>();
string curfile = string.Empty;
uint volume=100;
delegate void Task ();
System.Collections.Concurrent.ConcurrentQueue<Task> taskQueue =
new System.Collections.Concurrent.ConcurrentQueue<Task>();
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<string> 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) {
if(files.Contains(file)) return -1;
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(
@"(?<file>\d+)?(g(?<goto>[\d\.:]+))?(v(?<volume>\d+)(,(?<fade>\d+))?)?(?<play>(ps|p|s))?",
System.Text.RegularExpressions.RegexOptions.Compiled);
static System.Text.RegularExpressions.Regex regexCommand2 = new System.Text.RegularExpressions.Regex(
@"^(?<file>\d+)(?<params>[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
}
}