625 lines
14 KiB
C#
625 lines
14 KiB
C#
using System;
|
|
using System.Net;
|
|
using System.Net.Sockets;
|
|
using System.Threading;
|
|
using System.Collections.Generic;
|
|
|
|
namespace DMX2
|
|
{
|
|
public class OSCServer : IDisposable
|
|
{
|
|
|
|
UdpClient udpCli=null;
|
|
Thread pollThread = null;
|
|
bool running=true;
|
|
|
|
|
|
enum OSCType {
|
|
Int32,
|
|
Float32,
|
|
String,
|
|
Blob
|
|
}
|
|
|
|
class OSCMessage {
|
|
|
|
public abstract class OSCArg{
|
|
protected OSCArg(){}
|
|
public abstract OSCType Type{ get; }
|
|
public virtual string GetString(){ throw new System.NotImplementedException (); }
|
|
public virtual float GetFloat (){throw new System.NotImplementedException (); }
|
|
public virtual int GetInt(){throw new System.NotImplementedException (); }
|
|
public virtual byte[] GetBlob(){throw new System.NotImplementedException (); }
|
|
}
|
|
|
|
private class OSCStringArg : OSCArg{
|
|
string _s;
|
|
public override OSCType Type {
|
|
get {
|
|
return OSCType.String;
|
|
}
|
|
}
|
|
public OSCStringArg(string s){
|
|
_s=s;
|
|
}
|
|
public override string GetString ()
|
|
{
|
|
return _s;
|
|
}
|
|
public override float GetFloat ()
|
|
{
|
|
float f;
|
|
if(float.TryParse(_s,out f)) return f;
|
|
return 0.0f;
|
|
}
|
|
public override int GetInt ()
|
|
{
|
|
return (int)GetFloat();
|
|
}
|
|
}
|
|
private class OSCIntArg : OSCArg{
|
|
int _i;
|
|
public override OSCType Type {
|
|
get {
|
|
return OSCType.Int32;
|
|
}
|
|
}
|
|
public OSCIntArg(int i){
|
|
_i=i;
|
|
}
|
|
public override int GetInt ()
|
|
{
|
|
return _i;
|
|
}
|
|
public override float GetFloat ()
|
|
{
|
|
return (float)_i;
|
|
}
|
|
public override string GetString ()
|
|
{
|
|
return _i.ToString();
|
|
}
|
|
|
|
}
|
|
private class OSCFloatArg : OSCArg{
|
|
float _f;
|
|
public override OSCType Type {
|
|
get {
|
|
return OSCType.Float32;
|
|
}
|
|
}
|
|
public OSCFloatArg(float f){
|
|
_f=f;
|
|
}
|
|
public override int GetInt ()
|
|
{
|
|
return (int)(_f);
|
|
}
|
|
public override float GetFloat ()
|
|
{
|
|
return _f;
|
|
}
|
|
public override string GetString ()
|
|
{
|
|
return _f.ToString();
|
|
}
|
|
|
|
}
|
|
private class OSCBlobArg : OSCArg{
|
|
byte[] _b;
|
|
public override OSCType Type {
|
|
get {
|
|
return OSCType.Blob;
|
|
}
|
|
}
|
|
public OSCBlobArg(byte[] b){
|
|
_b=b;
|
|
}
|
|
public override byte[] GetBlob ()
|
|
{
|
|
return _b;
|
|
}
|
|
|
|
}
|
|
|
|
static string DecodeString (byte[] b, ref int pos)
|
|
{
|
|
int end = Array.IndexOf<byte>(b,0,pos);
|
|
if(end==-1) end = b.Length;
|
|
string ret = System.Text.Encoding.ASCII.GetString(b,pos,end-pos);
|
|
pos = (end/4+1)*4;
|
|
return ret;
|
|
}
|
|
|
|
static float DecodeFloat (byte[] b, ref int pos)
|
|
{
|
|
if(BitConverter.IsLittleEndian)
|
|
Array.Reverse(b,pos,4);
|
|
float ret = BitConverter.ToSingle(b,pos);
|
|
pos+=4;
|
|
return ret;
|
|
}
|
|
|
|
static int DecodeInt (byte[] b, ref int pos)
|
|
{
|
|
if(BitConverter.IsLittleEndian)
|
|
Array.Reverse(b,pos,4);
|
|
int ret = BitConverter.ToInt32(b, pos);
|
|
pos+=4;
|
|
return ret;
|
|
}
|
|
|
|
public OSCMessage(byte[] bmsg){
|
|
int pos = 0;
|
|
Address = DecodeString(bmsg,ref pos);
|
|
string typestring = DecodeString(bmsg,ref pos);
|
|
|
|
args = new OSCArg[typestring.Length-1];
|
|
|
|
int idx=0;
|
|
foreach(char c in typestring){
|
|
switch(c){
|
|
case 'f':
|
|
args[idx++] = new OSCFloatArg(DecodeFloat(bmsg,ref pos));
|
|
break;
|
|
case 's':
|
|
args[idx++] = new OSCStringArg(DecodeString(bmsg,ref pos));
|
|
break;
|
|
case 'i':
|
|
args[idx++] = new OSCIntArg(DecodeInt(bmsg,ref pos));
|
|
break;
|
|
case 'b':
|
|
int len=DecodeInt(bmsg,ref pos)*4;
|
|
byte[] b = new byte[len];
|
|
bmsg.CopyTo(b,0);
|
|
pos+=len;
|
|
args[idx++] = new OSCBlobArg(b);
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
public string Address{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
OSCArg[] args;
|
|
|
|
public OSCArg[] Args {
|
|
get {
|
|
return args;
|
|
}
|
|
}
|
|
|
|
|
|
// Construction de message
|
|
public OSCMessage(string _address){
|
|
Address=_address;
|
|
}
|
|
|
|
void AddArg (OSCArg arg)
|
|
{
|
|
if (args == null) {
|
|
args = new OSCArg[1];
|
|
} else {
|
|
OSCArg[] na = new OSCArg[args.Length+1];
|
|
args.CopyTo(na,0);
|
|
args = na;
|
|
}
|
|
args[args.Length-1] = arg;
|
|
}
|
|
|
|
public void AddString (string arg)
|
|
{
|
|
AddArg(new OSCStringArg(arg));
|
|
}
|
|
|
|
public void AddInt (int arg)
|
|
{
|
|
AddArg(new OSCIntArg(arg));
|
|
}
|
|
public void AddFloat (float arg)
|
|
{
|
|
AddArg(new OSCFloatArg(arg));
|
|
}
|
|
|
|
public byte[] Encode ()
|
|
{
|
|
int len = System.Text.ASCIIEncoding.ASCII.GetByteCount (Address) / 4+1;
|
|
string typestring = ",";
|
|
foreach (var arg in args) {
|
|
switch(arg.Type){
|
|
case OSCType.Int32:
|
|
len +=1;
|
|
typestring+="i";
|
|
break;
|
|
case OSCType.Float32:
|
|
len += 1;
|
|
typestring+="f";
|
|
break;
|
|
case OSCType.String:
|
|
len += System.Text.ASCIIEncoding.ASCII.GetByteCount (arg.GetString())/4+1;
|
|
typestring+="s";
|
|
break;
|
|
}
|
|
}
|
|
|
|
len += typestring.Length/4 +1;
|
|
byte[] res = new byte[len*4];
|
|
int pos=0;
|
|
EncodeString(res,ref pos,Address);
|
|
EncodeString(res,ref pos,typestring);
|
|
foreach (var arg in args) {
|
|
switch(arg.Type){
|
|
case OSCType.Int32:
|
|
EncodeInt(res,ref pos,arg.GetInt());
|
|
break;
|
|
case OSCType.Float32:
|
|
EncodeFloat(res,ref pos,arg.GetFloat());
|
|
break;
|
|
case OSCType.String:
|
|
EncodeString(res,ref pos,arg.GetString());
|
|
break;
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
void EncodeString (byte[] buff, ref int pos, string s)
|
|
{
|
|
pos +=
|
|
System.Text.ASCIIEncoding.ASCII.GetBytes (
|
|
s, 0, s.Length, buff, pos);
|
|
do {
|
|
buff[pos++]=0;
|
|
} while (pos%4!=0);
|
|
}
|
|
|
|
void EncodeInt (byte[] res, ref int pos, int i)
|
|
{
|
|
byte[] buff = BitConverter.GetBytes(i);
|
|
if(BitConverter.IsLittleEndian)
|
|
Array.Reverse(buff);
|
|
buff.CopyTo(res,pos);
|
|
pos+=4;
|
|
}
|
|
|
|
void EncodeFloat (byte[] res, ref int pos, float f)
|
|
{
|
|
byte[] buff = BitConverter.GetBytes(f);
|
|
if(BitConverter.IsLittleEndian)
|
|
Array.Reverse(buff);
|
|
buff.CopyTo(res,pos);
|
|
pos+=4;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public OSCServer ()
|
|
{
|
|
pollThread = new Thread(new ThreadStart(Loop));
|
|
pollThread.Start();
|
|
}
|
|
|
|
void Loop ()
|
|
{
|
|
udpCli = new UdpClient (7772);
|
|
byte[] recv;
|
|
IPEndPoint remep = new IPEndPoint (IPAddress.Any, 0);
|
|
|
|
|
|
try {
|
|
while (running) {
|
|
recv = udpCli.Receive (ref remep);
|
|
Console.WriteLine(remep);
|
|
OSCMessage msg = new OSCMessage(recv);
|
|
Console.WriteLine(msg.Address);
|
|
foreach(var arg in msg.Args)
|
|
Console.WriteLine(arg.GetString());
|
|
ProcessMessage(msg,remep);
|
|
}
|
|
} catch (SocketException ex) {
|
|
}
|
|
finally {
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// /master <X---
|
|
/// /masterseq/
|
|
/// go
|
|
/// goback
|
|
/// next <X---
|
|
/// goto
|
|
///
|
|
/// /seq/X/
|
|
/// go
|
|
/// goback
|
|
/// goto
|
|
/// master <X---
|
|
/// circuit/X <X---
|
|
///
|
|
/// /universe/X/
|
|
/// on/D
|
|
/// off
|
|
/// onval
|
|
///
|
|
///
|
|
/// </summary>
|
|
|
|
void ProcessMessage (OSCMessage msg,IPEndPoint remep)
|
|
{
|
|
if(Conduite.Courante == null) return;
|
|
string[] toks = msg.Address.Split (new char[]{'/'},StringSplitOptions.RemoveEmptyEntries);
|
|
int arg;
|
|
switch (toks [0]) {
|
|
case "refresh":
|
|
IPEndPoint ep = new IPEndPoint(remep.Address,msg.Args[0].GetInt());
|
|
SendRefresh(ep);
|
|
break;
|
|
case "master":
|
|
arg = msg.Args[0].GetInt();
|
|
if (arg<0) arg=0;
|
|
if (arg>100)arg=100;
|
|
Conduite.Courante.Master = arg;
|
|
break;
|
|
case "masterseq":
|
|
switch(toks[1]){
|
|
case "go":
|
|
if( msg.Args[0].GetInt() !=0)
|
|
Conduite.Courante.SequenceurMaitre.EffetSuivant();
|
|
break;
|
|
case "goback":
|
|
if( msg.Args[0].GetInt() !=0)
|
|
Conduite.Courante.SequenceurMaitre.EffetPrecedent();
|
|
break;
|
|
case "next":
|
|
arg = msg.Args[0].GetInt();
|
|
Conduite.Courante.SequenceurMaitre.IndexLigneaSuivre = arg;
|
|
break;
|
|
case "goto":
|
|
arg = msg.Args[0].GetInt();
|
|
Conduite.Courante.SequenceurMaitre.IndexLigneaSuivre = arg;
|
|
Conduite.Courante.SequenceurMaitre.EffetSuivant();
|
|
break;
|
|
}
|
|
break;
|
|
case "seq":
|
|
ProcessMessageSeq(msg,toks);
|
|
break;
|
|
case "universe":
|
|
ProcessMessageUniv(msg,toks);
|
|
break;
|
|
case "circuitTel":
|
|
ProcessMessageCircuit(msg,toks);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ProcessMessageSeq (OSCMessage msg, string[] toks)
|
|
{
|
|
int seqId;
|
|
Sequenceur seq = null;
|
|
if (int.TryParse (toks [1], out seqId)) {
|
|
if (seqId < 0 || seqId > Conduite.Courante.Sequenceurs.Count)
|
|
return;
|
|
seq = Conduite.Courante.Sequenceurs [seqId-1];
|
|
}
|
|
if (seq is SequenceurLineaire) {
|
|
SequenceurLineaire seql = seq as SequenceurLineaire;
|
|
switch(toks[2]){
|
|
case "go":
|
|
if( msg.Args[0].GetInt() !=0)
|
|
seql.IndexEffetCourrant++;
|
|
break;
|
|
case "goback":
|
|
if( msg.Args[0].GetInt() !=0)
|
|
seql.IndexEffetCourrant--;
|
|
break;
|
|
case "goto":
|
|
int arg = msg.Args[0].GetInt();
|
|
seql.IndexEffetCourrant = arg;
|
|
break;
|
|
case "master":
|
|
arg = msg.Args[0].GetInt();
|
|
if (arg<0) arg=0;
|
|
if (arg>100)arg=100;
|
|
seql.Master = arg;
|
|
break;
|
|
case "circuit":
|
|
int cirId;
|
|
arg = msg.Args[0].GetInt();
|
|
arg = Math.Max(0,arg);
|
|
arg = Math.Min(255,arg);
|
|
if(!int.TryParse(toks[3],out cirId)) return;
|
|
Circuit c = Conduite.Courante.GetCircuitByID (cirId);
|
|
if(seql.Circuits.Contains(c))
|
|
seql.ChangeValeur(c,arg);
|
|
break;
|
|
}
|
|
}
|
|
if (seq is SequenceurMacro) {
|
|
SequenceurMacro seqm = seq as SequenceurMacro;
|
|
switch(toks[2]){
|
|
case "go":
|
|
if( msg.Args[0].GetInt() !=0)
|
|
seqm.LigneSuivante();
|
|
break;
|
|
case "goto":
|
|
int arg = msg.Args[0].GetInt();
|
|
seqm.IndexLigneaSuivre = arg;
|
|
seqm.LigneSuivante();
|
|
break;
|
|
case "next":
|
|
arg = msg.Args[0].GetInt();
|
|
seqm.IndexLigneaSuivre = arg;
|
|
break;
|
|
case "master":
|
|
arg = msg.Args[0].GetInt();
|
|
if (arg<0) arg=0;
|
|
if (arg>100)arg=100;
|
|
seqm.Master = arg;
|
|
break;
|
|
}
|
|
}
|
|
if (seq is SequenceurSon) {
|
|
SequenceurSon seqs = seq as SequenceurSon;
|
|
switch(toks[2]){
|
|
case "master":
|
|
int arg = msg.Args[0].GetInt();
|
|
if (arg<0) arg=0;
|
|
if (arg>100)arg=100;
|
|
seqs.Volume = (uint)arg;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ProcessMessageUniv (OSCMessage msg, string[] toks)
|
|
{
|
|
int univId;
|
|
if (!int.TryParse (toks [1], out univId))
|
|
return;
|
|
if (univId <= 0 || univId > Conduite.Courante.Patches.Count)
|
|
return;
|
|
UniversDMX univ = Conduite.Courante.Patches [univId-1];
|
|
|
|
switch (toks [2]) {
|
|
case "on":
|
|
int dimId;
|
|
if(!int.TryParse(toks[3],out dimId))return;
|
|
if( msg.Args[0].GetInt() !=0)
|
|
univ.AllumageForce = dimId;
|
|
break;
|
|
case "off":
|
|
univ.AllumageForce = -1;
|
|
break;
|
|
case "onval":
|
|
int val = msg.Args[0].GetInt();
|
|
if(val<0) val=0;
|
|
if(val>255) val=255;
|
|
univ.AllumageForceVal = val;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ProcessMessageCircuit (OSCMessage msg, string[] toks)
|
|
{
|
|
switch (toks [1]) {
|
|
case "on":
|
|
int cirId;
|
|
if(!int.TryParse(toks[2],out cirId))return;
|
|
if( msg.Args[0].GetInt() !=0){
|
|
if(cirId>0 && cirId <= Conduite.Courante.Circuits.Count)
|
|
{
|
|
Conduite.Courante.CircuitTelecomande =
|
|
Conduite.Courante.Circuits[cirId-1];
|
|
}
|
|
}
|
|
break;
|
|
case "off":
|
|
Conduite.Courante.CircuitTelecomande= null;
|
|
break;
|
|
case "onval":
|
|
int val = msg.Args[0].GetInt();
|
|
if(val<0) val=0;
|
|
if(val>255) val=255;
|
|
Conduite.Courante.CircuitTelecomandeVal = val;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void SendRefresh (IPEndPoint ep)
|
|
{
|
|
OSCMessage msg = new OSCMessage ("/master");
|
|
msg.AddFloat (Conduite.Courante.Master);
|
|
byte[] buff = msg.Encode ();
|
|
udpCli.Send (buff, buff.Length, ep);
|
|
|
|
var id = Conduite.Courante.SequenceurMaitre.IndexLigneEnCours - 1;
|
|
msg = new OSCMessage ("/masterseq/prevstep");
|
|
if (id >= 0) {
|
|
msg.AddString (
|
|
string.Format ("{0}. {1}", id + 1,
|
|
Conduite.Courante.SequenceurMaitre.Lignes [id].Nom)
|
|
);
|
|
} else {
|
|
msg.AddString ("---");
|
|
}
|
|
buff = msg.Encode ();
|
|
udpCli.Send (buff, buff.Length, ep);
|
|
|
|
id = Conduite.Courante.SequenceurMaitre.IndexLigneEnCours;
|
|
string auto = string.Empty;
|
|
msg = new OSCMessage ("/masterseq/curstep");
|
|
if (id >= 0) {
|
|
msg.AddString (
|
|
string.Format ("{0}. {1}", id + 1,
|
|
Conduite.Courante.SequenceurMaitre.Lignes [id].Nom)
|
|
);
|
|
if(Conduite.Courante.SequenceurMaitre.Lignes[id].Duree!= TimeSpan.Zero)
|
|
auto = string.Format(" (AUTO {0})",Conduite.Courante.SequenceurMaitre.Lignes[id].Duree.TotalMilliseconds/100);
|
|
} else {
|
|
msg.AddString ("---");
|
|
}
|
|
buff = msg.Encode();
|
|
udpCli.Send (buff, buff.Length, ep);
|
|
|
|
|
|
id = Conduite.Courante.SequenceurMaitre.IndexLigneaSuivre;
|
|
if (id == -1)
|
|
id = Conduite.Courante.SequenceurMaitre.IndexLigneEnCours + 1;
|
|
msg = new OSCMessage ("/masterseq/nextstep");
|
|
if (id >= 0 && id < Conduite.Courante.SequenceurMaitre.Lignes.Count) {
|
|
msg.AddString (
|
|
string.Format ("{0}. {1}{2}", id + 1,
|
|
Conduite.Courante.SequenceurMaitre.Lignes [id].Nom,auto)
|
|
);
|
|
} else {
|
|
msg.AddString ("---");
|
|
}
|
|
buff = msg.Encode ();
|
|
udpCli.Send (buff, buff.Length, ep);
|
|
|
|
msg = new OSCMessage("/masterseq/time");
|
|
msg.AddFloat(((float) Conduite.Courante.SequenceurMaitre.TimeStamp.TotalMilliseconds/1000 ));
|
|
buff = msg.Encode();
|
|
udpCli.Send (buff, buff.Length, ep);
|
|
}
|
|
|
|
#region IDisposable implementation
|
|
|
|
|
|
public void Dispose ()
|
|
{
|
|
Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
protected virtual void Dispose (bool disposing)
|
|
{
|
|
if (disposing) {
|
|
running=false;
|
|
if(udpCli!=null)
|
|
udpCli.Close();
|
|
if(pollThread!=null)
|
|
{
|
|
if(!pollThread.Join(100))
|
|
pollThread.Abort();
|
|
pollThread=null;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
}
|
|
}
|
|
|