1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
#if (UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN) && !UNITY_ANDROID
using System;
using System.Collections.Generic;
using System.Linq;
#if VRC_CLIENT
using VRC.Core;
#endif
using PortMidi;
using UnityEngine;
using VRC.SDK3.Midi;
namespace VRC.SDKBase.Midi
{
public class VRCPortMidiInput: IVRCMidiInput
{
private MidiInput _input;
private byte[] _data;
private MidiDeviceInfo _info;
public bool OpenDevice(string name = null)
{
try
{
if (!string.IsNullOrWhiteSpace(name))
{
_info = MidiDeviceManager.AllDevices.FirstOrDefault(device =>
device.IsInput && device.Name.ToLower().Contains(name.ToLower()));
}
else
{
_info = MidiDeviceManager.AllDevices.FirstOrDefault(device =>
device.ID == MidiDeviceManager.DefaultInputDeviceId);
}
_input = MidiDeviceManager.OpenInput(_info.ID);
_input.SetFilter(MidiFilter.Active | MidiFilter.SysEx | MidiFilter.Clock | MidiFilter.Play | MidiFilter.Tick | MidiFilter.Undefined | MidiFilter.Reset | MidiFilter.RealTime | MidiFilter.AF | MidiFilter.Program | MidiFilter.PitchBend | MidiFilter.SystemCommon);
_data = new byte[1024];
return true;
}
catch (Exception e)
{
#if VRC_CLIENT
VRC.Core.Logger.LogError($"Error opening Default Device: {e.Message}");
#else
Debug.Log($"Error opening Default Device: {e.Message}");
#endif
return false;
}
}
public void Close()
{
if (_input != null)
{
_input.Close();
}
}
public void Update()
{
if (_input == null) return;
if (_input.HasData)
{
// Portmidi reports 4 bytes per event but the buffer has 8 bytes so we multiply count by 2
int count = (_input.Read(_data, 0, _data.Length)) * 2;
for (int i = 0; i < count; i+=8)
{
ConvertAndSend(_data[i], _data[i + 1], _data[i + 2]);
}
}
}
public IEnumerable<string> GetDeviceNames()
{
return MidiDeviceManager.AllDevices.Select(d => d.Name);
}
private void ConvertAndSend(byte status, byte data1, byte data2)
{
var command = status & 0xF0; // mask off all but top 4 bits
if (command >= 0x80 && command <= 0xE0) {
// it's a voice message
// find the channel by masking off all but the low 4 bits
var channel = status & 0x0F;
if (command == VRCMidiHandler.STATUS_NOTE_ON || command == VRCMidiHandler.STATUS_NOTE_OFF || command == VRCMidiHandler.STATUS_CONTROL_CHANGE)
{
OnMidiVoiceMessage?.Invoke(this, new MidiVoiceEventArgs(command, channel, data1, data2));
}
else
{
#if VRC_CLIENT
VRC.Core.Logger.Log($"command:{command} channel:{channel} data1:{data1} data2:{data2}");
#else
Debug.Log($"command:{command} channel:{channel} data1:{data1} data2:{data2}");
#endif
}
}
else
{
// it's a system message, ignore for now
OnMidiRawMessage?.Invoke(this, new MidiRawEventArgs(status, data1, data2));
}
}
public string Name => _info.Name;
public event MidiVoiceMessageDelegate OnMidiVoiceMessage;
public event MidiRawMessageDelegate OnMidiRawMessage;
}
}
#endif
|