/* 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.Threading; using System.Xml; namespace DMX2 { public class SequenceurMidi : Sequenceur , IDisposable { public class Ligne { public Ligne(){} string nom = string.Empty; TimeSpan top = TimeSpan.MinValue; string commande =string.Empty; public string Nom { get { return nom; } set { nom = value; } } public TimeSpan Top { get { return top; } set { top = value; } } public string Commande { get { return commande; } set{ commande = value; } } public void Save (XmlElement parent) { XmlElement el = parent.OwnerDocument.CreateElement ("Ligne"); parent.AppendChild (el); el.SetAttribute ("nom", nom); el.SetAttribute ("top", top.ToString ()); el.SetAttribute ("cmd", commande); } public static Ligne Load (Conduite c, XmlElement el) { Ligne l = new Ligne(); l.nom = el.GetAttribute ("nom"); l.top = TimeSpan.Parse(el.GetAttribute("top")); l.commande = el.GetAttribute ("cmd"); return l; } } List lignes = new List(); Ligne aSuivre = null; Ligne enCours = null; Ligne ligneMaitre = null; TimeSpan timeStamp = TimeSpan.Zero; TimeSpan topSuivant = TimeSpan.Zero; bool topPresent = false; actionEventTarget goNextEventTarget=null; actionEventTarget goBackEventTarget=null; SeqMidiUI ui = null; bool change = false; bool paused=false; AlsaSeqLib.MidiPort midiport; static int portnum=0; String destination; public String Destination { get{ return destination; } set{ destination = value; } } public bool Paused { get { return paused; } set { paused = value; } } int midiCh=0; public int MidiCh { get{ return midiCh; } } public SequenceurMidi () { goNextEventTarget = new actionEventTarget ( delegate(EventData data) { if(data.value==255) LigneSuivante(); return true; } ); goBackEventTarget = new actionEventTarget ( delegate(EventData data) { if(data.value==255){ if (IndexLigneEnCours > 0) { IndexLigneaSuivre = IndexLigneEnCours - 1; LigneSuivante (); } } return true; } ); string portname = string.Format ("midi_seq_{0}", portnum++); midiport = new AlsaSeqLib.MidiPort (portname); } bool disposed=false; ~SequenceurMidi () { ((IDisposable)this).Dispose (); } void IDisposable.Dispose (){ if (disposed) return; disposed = true; midiport.Close (); } public int IndexLigneEnCours { get { if (enCours == null) return -1; return lignes.IndexOf(enCours); } } public int IndexLigneaSuivre { get { if (aSuivre == null) return -1; return lignes.IndexOf (aSuivre); } set { aSuivre = lignes[value]; } } public int AjoutLigne (int pos) { lock (this) { lignes.Insert (pos, new Ligne ()); CommandAdd(pos); return pos; } } public void RetireLigne (int pos) { lock (this) { if (lignes [pos] == enCours) { enCours = null; if (pos + 1 < lignes.Count) aSuivre = lignes [pos + 1]; } if (lignes [pos] == aSuivre) aSuivre = null; lignes.RemoveAt (pos); CommandRemove(pos); } } public TimeSpan TimeStamp { get { return timeStamp; } } public ReadOnlyCollection Lignes { get { return lignes.AsReadOnly(); } } public override int ValeurCircuit (Circuit c) { return 0; } TimeSpan autoconnectTimer = TimeSpan.Zero; readonly TimeSpan acInterval = TimeSpan.FromSeconds(5); public override void Tick (TimeSpan time) { if (paused) return; timeStamp += time; autoconnectTimer += time; if (autoconnectTimer > acInterval) { AutoConnect(); autoconnectTimer = TimeSpan.Zero; } if (Monitor.TryEnter (this)) { try { while (topPresent &&(timeStamp >= topSuivant)) { LigneSuivante (); } } finally { Monitor.Exit (this); } } } private void AutoConnect() { if (Destination.Length < 3) return; System.Text.RegularExpressions.Regex r = new System.Text.RegularExpressions.Regex( destination, System.Text.RegularExpressions.RegexOptions.Compiled | System.Text.RegularExpressions.RegexOptions.IgnoreCase ); foreach (var cli in AlsaSeqLib.EnumClients()) { foreach (var p in cli.Ports) { string fullportname = cli.Name + ':' + p.Name; if (r.IsMatch (fullportname )) { // midiport.ConnectFrom(p); midiport.ConnectTo(p); } } } } public void LigneSuivante () { lock (this) { if(lignes.Count==0) return; int index; change = true; topPresent = false; if(aSuivre==null) // selection souris { index = IndexLigneEnCours +1; // Premier effet si aucun précédement if(index>= lignes.Count) index = 0; // Boucle si arrivé à la fin enCours = lignes[index]; // Gestion de la Reprise if (enCours.Commande.ToLower ().Equals ("r") && ligneMaitre != null) { enCours = ligneMaitre; } System.Text.RegularExpressions.Match m = regexGotoCmd.Match (enCours.Commande); if (m.Success) { int gtindex = int.Parse (m.Groups [2].Value)-1; if (lignes.Count >= gtindex) { timeStamp = TimeSpan.Zero; enCours = lignes [gtindex]; } } if(enCours.Nom.Length!=0) { ligneMaitre = enCours; timeStamp = TimeSpan.Zero; } } else { enCours = aSuivre; ligneMaitre = enCours; timeStamp = TimeSpan.Zero; } index = IndexLigneEnCours+1; if(index= TimeSpan.Zero) { topPresent = true; topSuivant= lignes[index].Top; } } aSuivre = null; LanceCommandeMidi(); if(ui!=null) ui.EffetChange(); } } static System.Text.RegularExpressions.Regex regexMidiCmd = new System.Text.RegularExpressions.Regex( @"(ch(\d+))|(CC(\d+),(\d+))|(PC(\d+))|(N(\d+)(\+|\-)(\d+)?)", System.Text.RegularExpressions.RegexOptions.Compiled | System.Text.RegularExpressions.RegexOptions.IgnoreCase ); static System.Text.RegularExpressions.Regex regexGotoCmd = new System.Text.RegularExpressions.Regex( @"(GT(\d+))", System.Text.RegularExpressions.RegexOptions.Compiled | System.Text.RegularExpressions.RegexOptions.IgnoreCase ); void LanceCommandeMidi () { Console.WriteLine (enCours.Commande); AlsaSeqLib.snd_seq_event_t ev; var matches = regexMidiCmd.Matches (enCours.Commande); foreach (System.Text.RegularExpressions.Match match in matches) { if (match.Groups [2].Success) { midiCh = int.Parse (match.Groups [2].Value)-1; continue; } if (match.Groups [4].Success) { ev = new AlsaSeqLib.snd_seq_event_t (); ev.type = AlsaSeqLib.snd_seq_event_type_t.SND_SEQ_EVENT_CONTROLLER; ev.data_ev_ctrl.channel = (byte)midiCh; ev.data_ev_ctrl.param = uint.Parse (match.Groups [4].Value)-0; ev.data_ev_ctrl.value = int.Parse (match.Groups [5].Value); midiport.SendEvent (ev); } if (match.Groups [7].Success) { ev = new AlsaSeqLib.snd_seq_event_t (); ev.type = AlsaSeqLib.snd_seq_event_type_t.SND_SEQ_EVENT_PGMCHANGE; ev.data_ev_ctrl.channel = (byte)midiCh; ev.data_ev_ctrl.value = int.Parse (match.Groups [7].Value)-1; midiport.SendEvent (ev); } if (match.Groups [9].Success) { ev = new AlsaSeqLib.snd_seq_event_t (); ev.data_ev_note.note = byte.Parse (match.Groups [9].Value); ev.data_ev_note.channel = (byte)midiCh; if (match.Groups [10].Value.Equals ("+")) { ev.type = AlsaSeqLib.snd_seq_event_type_t.SND_SEQ_EVENT_NOTEON; if (match.Groups [11].Success) ev.data_ev_note.velocity = byte.Parse (match.Groups [11].Value); else ev.data_ev_note.velocity = 127; } else { ev.type = AlsaSeqLib.snd_seq_event_type_t.SND_SEQ_EVENT_NOTEOFF; if (match.Groups [11].Success) ev.data_ev_note.off_velocity = byte.Parse (match.Groups [11].Value); else ev.data_ev_note.off_velocity = 0; } midiport.SendEvent (ev); } } } public bool LigneChange () { if (change) { change = false; return true; } return false; } public override void Save (System.Xml.XmlElement parent) { System.Xml.XmlElement el = parent.OwnerDocument.CreateElement ("SequenceurMidi"); System.Xml.XmlElement xmlEl; parent.AppendChild (el); el.SetAttribute ("id", ID.ToString ()); el.SetAttribute ("name", Name); el.SetAttribute("destination", Destination); //el.SetAttribute ("master", master.ToString ()); xmlEl = parent.OwnerDocument.CreateElement ("EffetSuivant"); if(Conduite.Courante.EventManager.SaveBindings(xmlEl,goNextEventTarget )) el.AppendChild(xmlEl); xmlEl = parent.OwnerDocument.CreateElement ("EffetPrecedent"); if(Conduite.Courante.EventManager.SaveBindings(xmlEl,goBackEventTarget )) el.AppendChild(xmlEl); foreach (Ligne li in lignes) { li.Save(el); } } public override SequenceurUI GetUI () { if (ui == null) { ui = new SeqMidiUI (this); ui.Destroyed += UiDestroyed; } return ui; } void UiDestroyed (object sender, EventArgs e) { ui = null; } public void BindEffetSuivantEvent (string eventId) { if (eventId.Length == 0) { Conduite.Courante.EventManager.Unbind (goNextEventTarget); return; } Conduite.Courante.EventManager.Bind(eventId,goNextEventTarget); } public void BindEffetPrecedentEvent (string eventId) { if (eventId.Length == 0) { Conduite.Courante.EventManager.Unbind (goBackEventTarget); return; } Conduite.Courante.EventManager.Bind(eventId,goBackEventTarget); } public static new SequenceurMidi Load (Conduite conduite, System.Xml.XmlElement el) { SequenceurMidi seq = new SequenceurMidi(); seq.LoadSeq(conduite,el); return seq; } private void LoadSeq (Conduite conduite, System.Xml.XmlElement el) { ID = int.Parse (el.GetAttribute ("id")); Name = el.GetAttribute ("name"); Destination = el.TryGetAttribute("destination", String.Empty); XmlElement xmlE; if ((xmlE = el["EffetSuivant"])!= null) foreach(string id in EventManager.LoadBindings(xmlE)) BindEffetSuivantEvent(id); if ((xmlE = el["EffetPrecedent"])!= null) foreach(string id in EventManager.LoadBindings(xmlE)) BindEffetPrecedentEvent(id); foreach (var xe in el.GetElementsByTagName("Ligne")) lignes.Add(Ligne.Load(conduite,xe as System.Xml.XmlElement)); } static System.Text.RegularExpressions.Regex regexCommand1 = new System.Text.RegularExpressions.Regex( @"(?\d+)", System.Text.RegularExpressions.RegexOptions.Compiled); static System.Text.RegularExpressions.Regex regexCommand2 = new System.Text.RegularExpressions.Regex( @"(?\d+)(?(t\d+)?)?", System.Text.RegularExpressions.RegexOptions.Compiled); public override void Command (string command) { lock (this) { var cmd = regexCommand1.Match(command); if (cmd.Success) { if (cmd.Groups ["effet"].Success) { int effet = int.Parse (cmd.Groups ["effet"].Value) - 1; if(effet>=lignes.Count) return; enCours = lignes[effet]; ligneMaitre = enCours; timeStamp = TimeSpan.Zero; topPresent = false; int index = IndexLigneEnCours+1; if(index= TimeSpan.Zero) { topPresent = true; topSuivant= lignes[index].Top; } } aSuivre = null; LanceCommandeMidi(); if(ui!=null) ui.EffetChange(); } } } } 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["effet"].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["effet"].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["effet"].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); } } } }