Skip to content

Instantly share code, notes, and snippets.

@Informatic
Created June 15, 2014 16:47
Show Gist options
  • Save Informatic/34e9211f70bc6df9ba94 to your computer and use it in GitHub Desktop.
Save Informatic/34e9211f70bc6df9ba94 to your computer and use it in GitHub Desktop.
#!/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