1 # Novation Nocturn driver
2 # Based on DWTFYW code by De Wet van Niekert (dewert). However, I had to
3 # put reading of the input endpoint in a separate thread because the only
4 # reliable way to read it is by using large timeouts (1s or so). With shorter
5 # timeouts, some events are lost/replaced by crossfader value.
17 class NocturnCommands
:
20 def setModeButtonLight(self
, button
, state
):
21 self
.pkt
+= chr(0x70 + button
) + ('\x01' if state
else '\x00')
22 def setUserButtonLight(self
, button
, state
):
23 self
.pkt
+= chr(0x78 + button
) + ('\x01' if state
else '\x00')
24 def setEncoderMode(self
, encoder
, mode
):
25 self
.pkt
+= chr(0x48 + encoder
) + chr(mode
<< 4)
26 def setEncoderValue(self
, encoder
, value
):
27 self
.pkt
+= chr(0x40 + encoder
) + chr(value
)
28 def setSpeedDialMode(self
, mode
):
29 self
.pkt
+= chr(0x51) + chr(mode
<< 4)
30 def setSpeedDialValue(self
, value
):
31 self
.pkt
+= chr(0x50) + chr(value
)
34 self
.setModeButtonLight(i
, False)
35 self
.setUserButtonLight(i
, False)
37 self
.setEncoderMode(i
, 3)
39 self
.setEncoderMode(i
, 4)
40 self
.setEncoderValue(i
, 64)
41 self
.setSpeedDialMode(5)
42 self
.setSpeedDialValue(64)
44 class NocturnHandler(threading
.Thread
):
45 def __init__(self
, n
):
46 threading
.Thread
.__init
__(self
)
48 self
.rpipefd
, self
.wpipefd
= os
.pipe()
49 self
.rpipe
= os
.fdopen(self
.rpipefd
, "rb")
50 self
.wpipe
= os
.fdopen(self
.wpipefd
, "wb")
51 flags
= fcntl
.fcntl(self
.rpipe
, fcntl
.F_GETFL
)
52 fcntl
.fcntl(self
.rpipe
, fcntl
.F_SETFL
, flags | os
.O_NONBLOCK
)
56 pkt
= self
.nocturn
.read()
60 def poll(self
, handler
):
62 data
= array
.array('B', self
.rpipe
.read())
64 # For longer sequences, Nocturn skips the control change message
69 handler(data
[i
], data
[i
+ 1])
73 def get_poll_fd(self
):
80 dev
= usb
.core
.find(idVendor
=self
.vendorID
, idProduct
=self
.productID
)
81 # The values in here don't seem to matter THAT much
82 initPackets
=["b00000","28002b4a2c002e35","2a022c722e30"]
83 #This is a minimum set that enables the device, but then it doesn't
84 #really work reliably, at least the touch sensing
85 #initPackets=["b00000", "2800"]
88 raise ValueError('Device not found')
97 dev
.set_configuration(2)
98 for packet
in initPackets
:
99 self
.ep
.write(binascii
.unhexlify(packet
))
102 self
.reader
= NocturnHandler(self
)
106 cmd
= NocturnCommands()
110 def execute(self
, cmd
):
111 self
.ep
.write(cmd
.pkt
)
114 data
= self
.ep2
.read(8, None)
116 except usb
.core
.USBError
as e
:
118 def poll(self
, handler
):
119 return self
.reader
.poll(handler
)
120 def get_poll_fd(self
):
121 return self
.reader
.get_poll_fd()