-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathrbkey.js
120 lines (106 loc) · 3.88 KB
/
rbkey.js
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
117
118
119
120
var _ = require('underscore');
var HID = require('node-hid');
var util = require('util');
_.mixin({
sum: function(obj) {
if (!_.isArray(obj) || obj.length == 0) return 0;
return _.reduce(obj, function(sum, n) {
return sum += n;
});
}
});
module.exports = function RBKeyboard() {
var devices = HID.devices();
var validDevs = _.filter(devices,
function(d) { return d.vendorId == 7085 && d.productId == 13104; });
if (!validDevs.length)
throw new Error('No usable devices found.');
var device = new HID.HID(validDevs[0].path);
var noteData = [];
for (var i = 0; i < 25; i++) noteData[i] = false;
var dpadData = {};
var dirs = { Left: 6, Right: 2, Up: 0, Down: 4 };
_.each(dirs, function(v, k) { dpadData[k] = false });
var buttonData = {};
var buttons = { 1: 1, A: 2, B: 4, 2: 8 };
_.each(buttons, function(v, k) { buttonData[k] = false });
var touchData = 0;
var touchButtonData = false;
function processData(data) {
// Uncomment these lines to print the raw data, useful for development
//_.each(data, function(d) { util.print(d + ' '); });
//console.log();
var dpad = data[2];
_.each(dirs, function(dir, name) {
var pressed = dpad == dir;
if (dpadData[name] != pressed) {
this.call('on' + name, pressed);
dpadData[name] = pressed;
}
}, this);
var buttonsPressed = data[0];
_.each(buttons, function(button, name) {
var pressed = (buttonsPressed & button) != 0;
if (buttonData[name] != pressed) {
this.call('on' + name, pressed);
buttonData[name] = pressed;
}
}, this);
var touch = data[15];
if (touchData != touch) {
touch != 0 ? this.call('onTouchOn', touch) : this.call('onTouchOff');
touchData = touch;
}
var touchButton = data[13] != 0;
if (touchButtonData != touchButton) {
this.call('onTouchButton', touchButton);
touchButtonData = touchButton;
}
var note = 0;
for (var i = 5; i <= 8; i++) {
note += data[i] << (8 * (8 - i));
}
data[8] = data[8] & 0x7F;
var velocityData = _.filter(data.slice(8, 13), function(v) { return v != 0; });
for (var i = 0; i < 25; i++) {
if (noteData[i] && velocityData.length) {
noteData[i] = velocityData[0];
velocityData = velocityData.slice(1);
}
var mask = 0x80000000 >>> i;
var noteOn = (note & mask) != 0;
if ((noteData[i] != 0) != noteOn) {
var velocity = 0;
if (noteOn) {
if (velocityData.length) {
velocity = velocityData[0];
velocityData = velocityData.slice(1);
}
else {
var curVelocities = _.chain(noteData).filter(function(v) { return v != 0; }).values();
velocity = (curVelocities.sum().value() / curVelocities.value().length) | 0;
}
}
noteOn ? this.call('onNoteOn', i, velocity) : this.call('onNoteOff', i, noteData[i]);
noteData[i] = velocity;
}
}
};
var onRead = _.bind(function(error, data) {
if (error) throw error;
processData.call(this, data);
data = null;
device.read(onRead);
}, this);
this.start = function() {
device.read(onRead);
};
this.close = function() {
device.close();
};
this.call = function(name) {
if (typeof this[name] === 'function') {
this[name].apply(this, Array.prototype.slice.call(arguments, 1));
}
};
}