loupiottes/DMX-2.0/MidiEventProvider.cs
2023-12-29 10:09:51 +01:00

930 lines
23 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.Runtime.InteropServices;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace DMX2
{
public class MidiEventProvider : IEventProvider, IDisposable
{
/// <summary>
/// Etat interne des evenements midi paginés.
/// </summary>
readonly Dictionary<string, internalEventDesc> eventlist = new Dictionary<string, internalEventDesc>();
/// <summary>
/// Liste des peripheriques connus (presents ou non)
/// </summary>
readonly Dictionary<string, MidiControler> knowndevices = new Dictionary<string, MidiControler>();
/// <summary>
/// Liste des ports connectés avec feedback
/// </summary>
readonly List<AlsaSeqLib.Port> feedbacksources = new List<AlsaSeqLib.Port>();
//static readonly Dictionary<int,MidiDev> srcidToDev = new Dictionary<int, MidiDev>();
readonly List<byte> unpaginatedchannels = new List<byte>();
/// <summary>
/// Derniere valeur connue pour une evenement sur source donnée :
/// Soit recue, soit envoyée (feedback / changement de page)
/// </summary>
readonly Dictionary<int, byte> lastValueOfSrc = new Dictionary<int, byte>();
// EventData last;
internalEventDesc levent = null;
bool guirefreshflag = false;
uint page = 1;
uint maxpage = 8;
protected bool use14b = false;
protected int max14bValue = 100;
byte[] fbTmpData = new byte[512];
uint pageUpCC = 127;
uint pageDownCC = 126;
AlsaSeqLib.MidiPort midiport;
public uint CurrentPage
{
get
{
return page;
}
set
{
if (value < 1 || value > maxpage)
return;
page = value;
Refresh();
}
}
public uint PageUpCC
{
get
{
return pageUpCC;
}
set
{
pageUpCC = value;
}
}
public uint PageDownCC
{
get
{
return pageDownCC;
}
set
{
pageDownCC = value;
}
}
public uint Maxpage
{
get
{
return maxpage;
}
set
{
maxpage = value;
}
}
public List<byte> UnpaginatedChannels
{
get
{
return unpaginatedchannels;
}
}
public bool Use14bCC
{
get
{
return use14b;
}
set
{
use14b = value;
}
}
public int Max14bValue
{
get
{
return max14bValue;
}
set
{
max14bValue = value;
}
}
public bool GuiRefreshFlag
{
get
{
if (guirefreshflag)
{
guirefreshflag = false;
return true;
}
return false;
}
}
public void ConnectDevice(string name)
{
knowndevices.Add(name, new MidiControler(name));
AutoConnect();
}
public void RefreshFeedback(string name)
{
foreach (AlsaSeqLib.Port port in knowndevices[name].ConnectedPorts)
{
if (knowndevices[name].HasFeedback)
{
if (!feedbacksources.Contains(port))
feedbacksources.Add(port);
}
else
{
if (feedbacksources.Contains(port))
feedbacksources.Remove(port);
}
}
}
public void DisconnectDevice(MidiEventProvider.MidiControler dev)
{
if (!knowndevices.ContainsKey(dev.Name))
return;
knowndevices.Remove(dev.Name);
foreach (AlsaSeqLib.Port connectedport in dev.ConnectedPorts)
{
midiport.Deconnecte(connectedport);
}
}
public bool IsKnownDevice(string name)
{
return knowndevices.ContainsKey(name);
}
public IEnumerable<MidiControler> KnownDevices
{
get
{
return knowndevices.Values;
}
}
public MidiEventProvider(EventManager manager)
{
#if DEBUG
MidiControler dev = new MidiControler("VMPK Input:VMPK Input");
dev.HasFeedback = true;
knowndevices.Add(dev.Name, dev);
dev = new MidiControler("VMPK Output:VMPK Output");
dev.HasFeedback = true;
knowndevices.Add(dev.Name, dev);
#endif
manager.RegisterProvider(this);
//AlsaSeqLib.Init ();
midiport = new AlsaSeqLib.MidiPort("event_prov_in_out");
//midiport.ConnectFrom(AlsaSeqLib.SND_SEQ_CLIENT_SYSTEM, AlsaSeqLib.SND_SEQ_PORT_SYSTEM_ANNOUNCE);
AlsaSeqLib.Port systemport = AlsaSeqLib.GetPortByIDs(AlsaSeqLib.SND_SEQ_CLIENT_SYSTEM, AlsaSeqLib.SND_SEQ_PORT_SYSTEM_ANNOUNCE);
midiport.ConnectFrom(systemport);
AutoConnect();
}
void AutoConnect()
{
foreach (var cli in AlsaSeqLib.EnumClients())
{
foreach (var p in cli.Ports)
{
PortDetected(cli, p);
}
}
}
void PortDetected(AlsaSeqLib.Client cli, AlsaSeqLib.Port p)
{
// Execute a chaque 'apparition' d'un port midi
// teste si connection auto au port et connecte si besoin
guirefreshflag = true;
string fullportname = cli.Name + ':' + p.Name;
if (knowndevices.ContainsKey(fullportname))
{
int srcid = p.ClientId << 8 + p.PortId;
if (knowndevices[fullportname].ConnectedPorts.Contains(p))
return;
midiport.ConnectFrom(p);
midiport.ConnectTo(p);
}
}
void PortConnect(AlsaSeqLib.snd_seq_connect_t cn, bool connect)
{
int clientId, portId;
if (cn.dest.client == AlsaSeqLib.ClientId)
{
clientId = cn.sender.client;
portId = cn.sender.port;
}
else
{
clientId = cn.dest.client;
portId = cn.dest.port;
}
AlsaSeqLib.Port p = AlsaSeqLib.GetPortByIDs(clientId, portId);
if (connect)
{
AlsaSeqLib.Client c = AlsaSeqLib.GetClientByID(clientId);
string fpname = c.Name + ":" + p.Name;
if (!knowndevices.ContainsKey(fpname))
return;
if (knowndevices[fpname].ConnectedPorts.Contains(p))
return;
knowndevices[fpname].ConnectedPorts.Add(p);
if (knowndevices[fpname].HasFeedback)
feedbacksources.Add(p);
guirefreshflag = true;
//srcidToDev[srcid] = knowndevices [fpname];
return;
}
foreach (var dev in knowndevices.Values)
{
if (dev.ConnectedPorts.Contains(p))
{
dev.ConnectedPorts.Remove(p);
guirefreshflag = true;
return;
}
}
}
static int CombineHash(int hash1, int hash2)
{
unchecked
{
return hash1 * 33 + hash2;
}
}
protected bool HasFeedback(AlsaSeqLib.Port source)
{
return feedbacksources.Contains(source);
}
public void SendEvent(AlsaSeqLib.snd_seq_event_t ev)
{
midiport.SendEvent(ev);
}
public void Refresh()
{
foreach (var ievent in eventlist.Values)
{
if (ievent.Page == page)
{
ievent.SendFeedback();
foreach (AlsaSeqLib.Port src in feedbacksources)
{
int lnvk = CombineHash(src.SrcId, ievent.MidiEvCode);
if (ievent.LastKnownValue != -1)
lastValueOfSrc[lnvk] = (byte)ievent.LastKnownValue;
}
}
}
}
#region IEventProvider implementation
bool IEventProvider.Bind(string eventId)
{
// On indique a l'EventManager qu'on traite, si l'ID commence par 'MIDI-'
if (!eventId.StartsWith("MIDI-"))
return false;
if (!eventlist.ContainsKey(eventId))
{
Match res = regexEventID.Match(eventId);
if (!res.Success)
return false;
uint _page = uint.Parse(res.Groups["page"].Value);
int _evHC = res.Groups["id"].Value.GetHashCode();
midiFeedbackSender sender = null;
res = regexCtrlEventID.Match(eventId);
if (res.Success)
{
byte chan = byte.Parse(res.Groups["chan"].Value);
uint param = uint.Parse(res.Groups["param"].Value);
sender = new midiCCFbSender(this, chan, param);
}
else if ((res = regexPbEventID.Match(eventId)).Success)
{
byte chan = byte.Parse(res.Groups["chan"].Value);
sender = new midiPBFbSender(this, chan);
}
eventlist.Add(eventId, new internalEventDesc(eventId, _page, _evHC, sender));
}
eventlist[eventId].Bound = true;
return true;
}
void IEventProvider.Unbind(string eventId)
{
if (!eventlist.ContainsKey(eventId))
return;
eventlist[eventId].Bound = false;
return;
}
Gtk.Menu IEventProvider.GetProviderSubMenu(EventManager.EventMenuData state, Gtk.ButtonPressEventHandler handler)
{
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(GetDescription(levent.InternalName));
item.Data[EventManager.EventIdKey] = levent.InternalName;
item.Data[EventManager.StateKey] = state;
item.ButtonPressEvent += handler;
retmenu.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<string> sortedKeys = eventlist.Keys.ToList(); // On recupere des IDs
sortedKeys.Sort(); // et on les trie
foreach (string key in sortedKeys)
{
internalEventDesc evt = eventlist[key];
Gtk.MenuItem item = new Gtk.MenuItem(GetDescription(evt.InternalName));
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;
uint evpage;
// Tant qu'il y des evenements midi en attente
while (midiport.GetEvent(out evS))
{
Console.WriteLine(string.Format("event {0}", evS.type));
string id = null;
int value = 0;
byte channel = 255;
switch (evS.type)
{
case AlsaSeqLib.snd_seq_event_type_t.SND_SEQ_EVENT_PORT_SUBSCRIBED: // Connection d'un périph midi
case AlsaSeqLib.snd_seq_event_type_t.SND_SEQ_EVENT_PORT_UNSUBSCRIBED:
PortConnect(
evS.data_connect,
evS.type == AlsaSeqLib.snd_seq_event_type_t.SND_SEQ_EVENT_PORT_SUBSCRIBED
);
continue;
case AlsaSeqLib.snd_seq_event_type_t.SND_SEQ_EVENT_CONTROLLER:
if (evS.data_ev_ctrl.param == pageUpCC && evS.data_ev_ctrl.value > 0)
{
CurrentPage++;
continue;
}
if (evS.data_ev_ctrl.param == pageDownCC && evS.data_ev_ctrl.value > 0)
{
CurrentPage--;
continue;
}
channel = evS.data_ev_ctrl.channel;
if (use14b && evS.data_ev_ctrl.param < 64)
{
long msbAddr;
if (evS.data_ev_ctrl.param < 32)
{
msbAddr = evS.data_ev_ctrl.channel * 32 + evS.data_ev_ctrl.param;
fbTmpData[msbAddr] = (byte)evS.data_ev_ctrl.value;
continue;
}
evS.data_ev_ctrl.param -= 32;
msbAddr = evS.data_ev_ctrl.channel * 32 + evS.data_ev_ctrl.param;
value = ((fbTmpData[msbAddr] << 7) ^ evS.data_ev_ctrl.value);
value = 255 * value / max14bValue;
if (value > 255) value = 255;
}
else
{
value = 255 * evS.data_ev_ctrl.value / 127; // Conversion {0,127} => {0,255}
}
id = string.Format("CTRL-C{0}P{1}", evS.data_ev_ctrl.channel, evS.data_ev_ctrl.param);
break;
case AlsaSeqLib.snd_seq_event_type_t.SND_SEQ_EVENT_NOTEON:
id = string.Format("NOTE-C{0}N{1}", evS.data_ev_note.channel, evS.data_ev_note.note);
channel = evS.data_ev_note.channel;
value = 255;
break;
case AlsaSeqLib.snd_seq_event_type_t.SND_SEQ_EVENT_NOTEOFF:
id = string.Format("NOTE-C{0}N{1}", evS.data_ev_note.channel, evS.data_ev_note.note);
channel = evS.data_ev_note.channel;
value = 0;
break;
case AlsaSeqLib.snd_seq_event_type_t.SND_SEQ_EVENT_PITCHBEND:
id = string.Format("PB-C{0}", evS.data_ev_ctrl.channel);
channel = evS.data_ev_ctrl.channel;
value = ((evS.data_ev_ctrl.value + 7000) * 255 / 14000);
if (value < 0)
value = 0;
if (value > 255)
value = 255;
break;
case AlsaSeqLib.snd_seq_event_type_t.SND_SEQ_EVENT_PGMCHANGE:
//CurrentPage = (uint)evS.data_ev_ctrl.value;
continue;
case AlsaSeqLib.snd_seq_event_type_t.SND_SEQ_EVENT_PORT_START:
PortDetected(
AlsaSeqLib.GetClientByID(evS.data_addr.client),
AlsaSeqLib.GetPortByIDs(evS.data_addr.client,evS.data_addr.port)
);
continue;
/*case AlsaSeqLib.snd_seq_event_type_t.SND_SEQ_EVENT_CLOCK:
case AlsaSeqLib.snd_seq_event_type_t.SND_SEQ_EVENT_SENSING:
continue;*/
default:
id = null;
#if DEBUG
Console.WriteLine(string.Format("event {0}", evS.type));
Info.Publish(string.Format("event {0}", evS.type)); // On affiche les evenements inconnus
#endif
continue;
}
if (id != null)
{
// Hashcode de l'ev Midi, non pagine
int evHC = id.GetHashCode();
int srcid = evS.source.client << 8 + evS.source.port;
int lnvk = CombineHash(srcid, evHC);
if (channel == 255 || unpaginatedchannels.Contains(channel))
evpage = 0;
else
evpage = page;
// Construction de l'ID evenement
id = string.Format("MIDI-PAGE{0}-{1}", evpage, id);
// Creation de l'objet interne si innexistant
if (!eventlist.ContainsKey(id))
{
switch (evS.type)
{
// TODO : Pitchbend feedback
case AlsaSeqLib.snd_seq_event_type_t.SND_SEQ_EVENT_CONTROLLER:
eventlist.Add(id, new internalEventDesc(id, evpage, evHC,
new midiCCFbSender(this, channel, evS.data_ev_ctrl.param))
);
break;
case AlsaSeqLib.snd_seq_event_type_t.SND_SEQ_EVENT_PITCHBEND:
eventlist.Add(id, new internalEventDesc(id, evpage, evHC,
new midiPBFbSender(this, channel))
);
break;
default:
eventlist.Add(id, new internalEventDesc(id, evpage, evHC, null));
break;
}
}
levent = eventlist[id]; //Dernier Evenement recu conserve pour menu
if (!lastValueOfSrc.ContainsKey(lnvk))
{
lastValueOfSrc[lnvk] = (byte)value;
}
else if (lastValueOfSrc[lnvk] == (byte)value)
continue;
EventData evData = new EventData();
evData.id = id;
evData.value = (byte)value;
evData.prev_value = lastValueOfSrc[lnvk];
/*if (evData.Equals (last))
continue; */
//last = evData;
eventlist[id].CallEvent(callback, evData);
/*if (eventlist [id].Bound) {
callback (evData);
}*/
lastValueOfSrc[lnvk] = (byte)value;
eventlist[id].LastKnownValue = (byte)value;
}
}
}
string IEventProvider.MenuName
{
get
{
return "Midi";
}
}
static System.Text.RegularExpressions.Regex regexEventID = new System.Text.RegularExpressions.Regex(
@"MIDI-PAGE(?<page>\d+)-(?<id>.+)",
System.Text.RegularExpressions.RegexOptions.Compiled);
static System.Text.RegularExpressions.Regex regexCtrlEventID = new System.Text.RegularExpressions.Regex(
@"MIDI-PAGE(?<page>\d+)-CTRL-C(?<chan>\d+)P(?<param>\d+)",
System.Text.RegularExpressions.RegexOptions.Compiled);
static System.Text.RegularExpressions.Regex regexPbEventID = new System.Text.RegularExpressions.Regex(
@"MIDI-PAGE(?<page>\d+)-PB-C(?<chan>\d+)",
System.Text.RegularExpressions.RegexOptions.Compiled);
static System.Text.RegularExpressions.Regex regexNoteEventID = new System.Text.RegularExpressions.Regex(
@"MIDI-PAGE(?<page>\d+)-NOTE-C(?<chan>\d+)N(?<note>\d+)",
System.Text.RegularExpressions.RegexOptions.Compiled);
string GetDescription(string eventId)
{
if (!eventlist.ContainsKey(eventId))
return null;
var res = regexCtrlEventID.Match(eventId);
if (res.Success)
{
uint page = uint.Parse(res.Groups["page"].Value);
byte chan = byte.Parse(res.Groups["chan"].Value);
uint param = uint.Parse(res.Groups["param"].Value);
return string.Format("Page {2} => Control-Change C({0}) Param-{1}", chan + 1, param, page);
}
res = regexPbEventID.Match(eventId);
if (res.Success)
{
uint page = uint.Parse(res.Groups["page"].Value);
byte chan = byte.Parse(res.Groups["chan"].Value);
return string.Format("Page {1} => PitchBend C({0})", chan + 1, page);
}
res = regexNoteEventID.Match(eventId);
if (res.Success)
{
uint page = uint.Parse(res.Groups["page"].Value);
byte chan = byte.Parse(res.Groups["chan"].Value);
byte note = byte.Parse(res.Groups["note"].Value);
return string.Format("Page {2} => Note C({0}) Note-{1}", chan + 1, note, page);
}
return eventId;
}
IFeedbackInfo IEventProvider.GetFeedbackInfo(string eventId)
{
if (!eventlist.ContainsKey(eventId))
return null;
return new midifeedbackinfo(this, eventlist[eventId]);
}
#endregion
#region IDisposable implementation
bool disposed = false;
~MidiEventProvider()
{
Dispose();
}
public void Dispose()
{
if (disposed)
return;
disposed = true;
//AlsaSeqLib.Close();
midiport.Close();
}
#endregion
public void Save(System.Xml.XmlElement parent)
{
System.Xml.XmlElement el = parent.OwnerDocument.CreateElement("Midi");
parent.AppendChild(el);
el.SetAttribute("maxpage", maxpage.ToString());
el.SetAttribute("pageUpCC", pageUpCC.ToString());
el.SetAttribute("pageDownCC", pageDownCC.ToString());
el.SetAttribute("fourteenbits", use14b ? "true" : "false");
el.SetAttribute("max14b", max14bValue.ToString());
System.Xml.XmlElement xmlEl;
foreach (MidiControler dev in knowndevices.Values)
{
el.AppendChild(xmlEl = parent.OwnerDocument.CreateElement("MidiDev"));
xmlEl.SetAttribute("name", dev.Name);
xmlEl.SetAttribute("feedback", dev.HasFeedback.ToString());
}
foreach (byte ch in unpaginatedchannels)
{
el.AppendChild(xmlEl = parent.OwnerDocument.CreateElement("UPC"));
xmlEl.SetAttribute("ch", ch.ToString());
}
}
public void Load(System.Xml.XmlElement el)
{
if (el == null)
return;
maxpage = uint.Parse(el.TryGetAttribute("maxpage", "8"));
pageUpCC = uint.Parse(el.TryGetAttribute("pageUpCC", "127"));
pageDownCC = uint.Parse(el.TryGetAttribute("pageDownCC", "126"));
use14b = el.TryGetAttribute("fourteenbits", string.Empty).Equals("true");
max14bValue = int.Parse(el.TryGetAttribute("max14b", "255"));
foreach (var xd in el.GetElementsByTagName("MidiDev"))
{
System.Xml.XmlElement xdev = xd as System.Xml.XmlElement;
string name = xdev.GetAttribute("name");
if (!knowndevices.ContainsKey(name))
knowndevices.Add(name, new MidiControler(name));
knowndevices[name].HasFeedback = bool.Parse(xdev.TryGetAttribute("feedback", "false"));
}
unpaginatedchannels.Clear();
foreach (var xu in el.GetElementsByTagName("UPC"))
{
System.Xml.XmlElement xupc = xu as System.Xml.XmlElement;
unpaginatedchannels.Add(byte.Parse(xupc.GetAttribute("ch")));
}
AutoConnect();
}
class internalEventDesc
{
readonly string internalName;
bool bound = false;
readonly uint page;
readonly int midiEvCode;
readonly midiFeedbackSender fbSender;
public bool Bound
{
get
{
return bound;
}
set
{
bound = value;
}
}
public string InternalName
{
get
{
return internalName;
}
}
public uint Page
{
get
{
return page;
}
}
public int MidiEvCode
{
get
{
return midiEvCode;
}
}
public internalEventDesc(string _id, uint _page, int _evHCode, midiFeedbackSender _evsender)
{
internalName = _id;
page = _page;
midiEvCode = _evHCode;
fbSender = _evsender;
}
int lastknownvalue = -1;
public int LastKnownValue
{
get
{
return lastknownvalue;
}
set
{
lastknownvalue = value;
}
}
bool nofbflag = false;
public void SendFeedback()
{
if (fbSender != null && !nofbflag)
fbSender.SendFeedback(lastknownvalue);
}
public void CallEvent(EventManagerCallback callback, EventData evData)
{
nofbflag = true;
callback(evData);
nofbflag = false;
}
}
abstract class midiFeedbackSender
{
public abstract void SendFeedback(int value);
}
class midiCCFbSender : midiFeedbackSender
{
readonly MidiEventProvider prov;
AlsaSeqLib.snd_seq_event_t ev;
AlsaSeqLib.snd_seq_event_t ev2;
public midiCCFbSender(MidiEventProvider _prov, byte _chan, uint _param)
{
prov = _prov;
ev = new AlsaSeqLib.snd_seq_event_t();
ev.type = AlsaSeqLib.snd_seq_event_type_t.SND_SEQ_EVENT_CONTROLLER;
ev.data_ev_ctrl.channel = _chan;
ev.data_ev_ctrl.param = _param;
ev2 = ev;
ev2.data_ev_ctrl.param += 32;
}
public override void SendFeedback(int value)
{
if (prov.use14b && ev.data_ev_ctrl.param < 32)
{
value = value * prov.max14bValue / 255;
ev.data_ev_ctrl.value = value >> 7;
ev2.data_ev_ctrl.value = value & 0xFF;
prov.SendEvent(ev);
prov.SendEvent(ev2);
}
else
{
ev.data_ev_ctrl.value = value * 127 / 255;
prov.SendEvent(ev);
}
}
}
class midiPBFbSender : midiFeedbackSender
{
readonly MidiEventProvider prov;
AlsaSeqLib.snd_seq_event_t ev;
public midiPBFbSender(MidiEventProvider _prov, byte _chan)
{
prov = _prov;
ev = new AlsaSeqLib.snd_seq_event_t();
ev.type = AlsaSeqLib.snd_seq_event_type_t.SND_SEQ_EVENT_PITCHBEND;
ev.data_ev_ctrl.channel = _chan;
}
public override void SendFeedback(int value)
{ // value = ((evS.data_ev_ctrl.value + 7000) * 255 / 14000);
ev.data_ev_ctrl.value = value * 14000 / 255 - 7000;
prov.SendEvent(ev);
}
}
class midifeedbackinfo : IFeedbackInfo
{
MidiEventProvider prov;
readonly internalEventDesc iev;
public midifeedbackinfo(MidiEventProvider _prov, internalEventDesc _iev)
{
prov = _prov;
iev = _iev;
}
#region IFeedbackInfo implementation
bool IFeedbackInfo.FeedBack(byte data)
{
iev.LastKnownValue = data;
if (prov.CurrentPage == iev.Page || iev.Page == 0)
{
iev.SendFeedback();
foreach (AlsaSeqLib.Port src in prov.feedbacksources)
{
int lnvk = CombineHash(src.SrcId, iev.MidiEvCode);
if (iev.LastKnownValue != -1)
prov.lastValueOfSrc[lnvk] = (byte)iev.LastKnownValue;
}
}
return true;
}
#endregion
}
public class MidiControler
{
string name;
public string Name { get { return name; } }
public bool HasFeedback { get; set; }
readonly List<AlsaSeqLib.Port> connected = new List<AlsaSeqLib.Port>();
public List<AlsaSeqLib.Port> ConnectedPorts
{
get { return connected; }
}
public MidiControler(string _name)
{
name = _name;
}
}
}
}