/*
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.Runtime.InteropServices;
using System.Collections.Generic;
using System.Linq;
namespace DMX2
{
public class MidiEventProvider : IEventProvider
{
class internalEvent {
public string internalName;
public string description;
public internalEvent(string _id, string _desc)
{
internalName=_id;
description=_desc;
}
}
class feedbackinfo : IFeedbackInfo {
MidiEventProvider prov;
AlsaSeqLib.snd_seq_event_t ev;
public feedbackinfo(MidiEventProvider _prov, byte channel,uint param){
ev = new AlsaSeqLib.snd_seq_event_t(); prov=_prov;
ev.data_ev_ctrl.channel = channel;
ev.data_ev_ctrl.param= param;
ev.type = AlsaSeqLib.snd_seq_event_type_t.SND_SEQ_EVENT_CONTROLLER;
}
#region IFeedbackInfo implementation
bool IFeedbackInfo.FeedBack (byte data)
{
ev.data_ev_ctrl.value = (byte)((int)data * 127 / 255) ;
prov.SendEvent(ev);
return true;
}
#endregion
}
Dictionary eventlist = new Dictionary();
EventData last;
internalEvent levent=null;
bool connected=false;
public MidiEventProvider (EventManager manager)
{
manager.RegisterProvider (this);
AlsaSeqLib.Init ();
foreach (var cli in AlsaSeqLib.EnumClients()) {
Console.WriteLine(cli.Name);
foreach(var p in cli.Ports){
Console.WriteLine("Port {0} - {1} => caps {2}",p.PortId,p.Name,p.Caps);
}
}
}
public void SendEvent (AlsaSeqLib.snd_seq_event_t ev)
{
AlsaSeqLib.SendEventToSubscribers(ev);
}
#region IEventProvider implementation
bool IEventProvider.Bind (string eventId)
{
// On indique a l'EventManager qu'on traite, si l'ID commence par 'MIDI-'
return eventId.StartsWith("MIDI-");
}
void IEventProvider.Unbind (string eventId)
{
return;
}
Gtk.Menu IEventProvider.GetProviderSubMenu (EventManager.EventMenuData state, Gtk.ButtonPressEventHandler handler)
{
if(!connected) return null; // Si pas encore recu d'evenements => pas de menu
Gtk.Menu retmenu = new Gtk.Menu ();
if (levent != null) { // Creation du sous menu "Dernier"
Gtk.MenuItem lmenuitem = new Gtk.MenuItem ("Dernier");
retmenu.Add (lmenuitem);
Gtk.Menu lmenu = new Gtk.Menu ();
lmenuitem.Submenu = lmenu;
Gtk.MenuItem item = new Gtk.MenuItem(levent.description);
item.Data[EventManager.EventIdKey] = levent.internalName;
item.Data[EventManager.StateKey] = state;
item.ButtonPressEvent += handler;
lmenu.Add (item);
}
Gtk.MenuItem evmenuitem = new Gtk.MenuItem ("Events"); // Creation du sous menu "Events"
retmenu.Add (evmenuitem);
Gtk.Menu evmenu = new Gtk.Menu ();
evmenuitem.Submenu = evmenu;
List sortedKeys = eventlist.Keys.ToList(); // On recupere des IDs
sortedKeys.Sort(); // et on les trie
foreach ( string key in sortedKeys ) {
internalEvent evt= eventlist[key];
Gtk.MenuItem item = new Gtk.MenuItem(evt.description);
item.Data[EventManager.EventIdKey] = evt.internalName;
item.Data[EventManager.StateKey] = state;
item.ButtonPressEvent += handler;
evmenu.Add (item);
}
return retmenu;
}
void IEventProvider.ProcessEvents (EventManagerCallback callback)
{
AlsaSeqLib.snd_seq_event_t evS;
// Tant qu'il y des evenements midi en attente
while (AlsaSeqLib.GetEvent(out evS)) {
string id=null, description=null; int value=0;
switch (evS.type) {
case AlsaSeqLib.snd_seq_event_type_t.SND_SEQ_EVENT_PORT_SUBSCRIBED: // Connection d'un périph midi
connected = true;
continue;
case AlsaSeqLib.snd_seq_event_type_t.SND_SEQ_EVENT_CONTROLLER:
id= string.Format("MIDI-CTRL-C{0}P{1}",evS.data_ev_ctrl.channel,evS.data_ev_ctrl.param);
description = string.Format("Controller Ch {0} Param {1}",evS.data_ev_ctrl.channel,evS.data_ev_ctrl.param);
value = 255 * evS.data_ev_ctrl.value / 127; // Conversion {0,127} => {0,255}
break;
case AlsaSeqLib.snd_seq_event_type_t.SND_SEQ_EVENT_NOTEON:
id= string.Format("MIDI-NOTE-C{0}N{1}",evS.data_ev_note.channel, evS.data_ev_note.note );
description = string.Format("Note {1} Ch {0}",evS.data_ev_note.channel, evS.data_ev_note.note );
value = 255 ;
break;
case AlsaSeqLib.snd_seq_event_type_t.SND_SEQ_EVENT_NOTEOFF:
id= string.Format("MIDI-NOTE-C{0}N{1}",evS.data_ev_note.channel, evS.data_ev_note.note );
description = string.Format("Note {1} Ch {0}",evS.data_ev_note.channel, evS.data_ev_note.note );
value = 0;
break;
case AlsaSeqLib.snd_seq_event_type_t.SND_SEQ_EVENT_CLOCK:
case AlsaSeqLib.snd_seq_event_type_t.SND_SEQ_EVENT_SENSING:
continue;
//TODO : Regarder si d'autres controles interessants.
default:
id= null;
#if DEBUG
Info.Publish(string.Format ("event {0}", evS.type) ); // On affiche les evenements inconnus
Console.WriteLine(string.Format ("event {0}", evS.type) );
#endif
continue;
}
connected=true;
if(id!=null)
{
if(!eventlist.ContainsKey(id))
eventlist.Add(id,new internalEvent(id,description));
levent= eventlist[id]; //Dernier Evenement recu conserve pour menu
EventData evData = new EventData();
evData.id = id;
evData.value = (byte)value;
if(evData.Equals(last)) continue; // On ignore les evenements répétés à l'identique
callback(evData);
last = evData;
}
}
}
string IEventProvider.MenuName {
get {
return "Midi";
}
}
static System.Text.RegularExpressions.Regex regexCtrlEventID = new System.Text.RegularExpressions.Regex(
@"MIDI-CTRL-C(?\d+)P(?\d+)",
System.Text.RegularExpressions.RegexOptions.Compiled);
IFeedbackInfo IEventProvider.GetFeedbackInfo (string eventId)
{
var res = regexCtrlEventID.Match (eventId);
if (res.Success) {
Console.WriteLine("Succes");
byte chan = byte.Parse (res.Groups ["chan"].Value);
uint param = uint.Parse (res.Groups ["param"].Value);
return new feedbackinfo (this, chan, param);
}
return null;
}
#endregion
}
}