Created
June 15, 2014 16:47
-
-
Save Informatic/34e9211f70bc6df9ba94 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# -* coding: utf-8 *- | |
''' | |
Guitar Hero® World Tour wireless PS3 drums to MIDI adapter for Linux in python. | |
usage: PROG [hidraw device, default: /dev/hidraw4] [MIDI device number, default: last output] | |
''' | |
# FIXME: I dunno... refactor maybe? ;) | |
# FIXME: Sadly, latency is quite noticable | |
# TODO: Make buttons fire MIDI events too! | |
import pypm | |
import sys | |
import os | |
# MIDI Notes fired on drum hits, in the following order: | |
# Yellow, Red, Green, Blue, Kick, Orange (look at the bottom for further | |
# information, following bindings are reasonable in default Hydrogen drumset) | |
NOTES = [49, 38, 41, 43, 36, 46] | |
DEFAULT_DEV = '/dev/hidraw4' | |
REPORT_SIZE = 27 | |
MAX_VELOCITY = 122 | |
DEBUG = False | |
def main(argv): | |
hidraw_dev = argv[0] if len(argv) >= 1 else DEFAULT_DEV | |
# Wroom wroom wroom! | |
devices = [dict(zip(['interface', 'name', 'input', 'output', 'open'], pypm.GetDeviceInfo(i)), id=i) for i in xrange(pypm.CountDevices())] | |
output_devices = filter(lambda x: x['output'], devices) | |
chosen_device = devices[int(argv[1])] if len(argv) >= 2 and devices[int(argv[1])] in output_devices else output_devices[-1] | |
for d in output_devices: | |
print '%(id)2d: %(star)s%(name)s' % dict(star='*' if chosen_device == d else ' ', **d) | |
MidiOut = pypm.Output(chosen_device['id'], 0) | |
if not os.access(hidraw_dev, os.R_OK): | |
import getpass | |
print >>sys.stderr, '\n*** You have no access to %s device, try:\n***\tsudo chown %s %s\n' % (hidraw_dev, getpass.getuser(), hidraw_dev) | |
hidraw = open(hidraw_dev) | |
state = [0, 0, 0, 0, 0, 0] | |
lastbuf = None | |
while True: | |
buf = hidraw.read(REPORT_SIZE) | |
for i, prev in enumerate(state): | |
if ord(buf[11 + i]) != prev: | |
state[i] = ord(buf[11 + i]) | |
if state[i] == 127: | |
# Action Buttons | |
pass | |
elif state[i] > 0: | |
# Drum hit | |
print 'Hit',i | |
MidiOut.WriteShort(0x90, NOTES[i], state[i]) | |
else: | |
# Drum/key release | |
pass | |
#MidiOut.WriteShort(0x80, NOTES[i], prev) | |
if DEBUG: | |
if lastbuf != None: | |
diff = [(i, lastbuf[i], n) for i, n in enumerate(buf) if n != lastbuf[i]] | |
for i, p, c in diff: | |
print 'byte %d changed from %d to %d' % (i, ord(p), ord(c)) | |
lastbuf = buf | |
if __name__ == '__main__': | |
try: | |
main(sys.argv[1:]) | |
except KeyboardInterrupt: pass | |
''' | |
Some notes | |
========== | |
Drum state/velocity bytes in report: | |
buf[11] yellow / square | |
buf[12] red / circle | |
buf[13] green / cross | |
buf[14] blue / triangle | |
buf[15] kick | |
buf[16] orange / L1 | |
Buttons: | |
buf[0] & 1 Square | |
buf[0] & 2 Cross | |
buf[0] & 4 Circle | |
buf[0] & 8 Triangle | |
buf[11:15] == 127 Square/Cross/Circle/Triangle (look above) | |
buf[1] & 1 SELECT | |
buf[1] & 2 START | |
buf[1] & 16 PS/HOME | |
buf[7] D-Pad Right | |
buf[8] D-Pad Left | |
buf[9] D-Pad Up | |
buf[10] D-Pad Down | |
Drums disconnected: | |
byte 5 changed from 0 to 128 | |
byte 6 changed from 0 to 128 | |
byte 20 changed from 0 to 2 | |
byte 22 changed from 0 to 2 | |
byte 24 changed from 0 to 2 | |
Drums reconnected: | |
byte 5 changed from 128 to 0 | |
byte 6 changed from 128 to 0 | |
byte 20 changed from 2 to 0 | |
byte 22 changed from 2 to 0 | |
byte 24 changed from 2 to 0 | |
Drum/state byte behaviour when button is kept pressed: | |
byte 13 changed from 0 to 127 [button pressed] | |
byte 13 changed from 127 to 122 [drum hit] | |
byte 13 changed from 122 to 127 [drum released, button still pressed] | |
''' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment