FMMT666 / launchpad.py

Novation Launchpad (and Midi Fighter) control suite for Python
Other
350 stars 59 forks source link

Support for Novation Launch Control FA0940-01 #45

Open gene1wood opened 4 years ago

gene1wood commented 4 years ago

Any chance of adding support for Novation Launch Control

FMMT666 commented 4 years ago

Interesting, never saw than one before. You mean this thingy with 2 potentiometers and only one button in each column, right?

Seems to be discontinued, so I can only try to implement that blindly. Could you test this if I do so?

gene1wood commented 4 years ago

You mean this thingy with 2 potentiometers and only one button in each column, right?

Exactly

Seems to be discontinued, so I can only try to implement that blindly.

Maybe not worth it then, I'd say if this sounds interesting or if you're bored, go for it, otherwise feel free to close this issue.

Could you test this if I do so?

Yes, happily.

FMMT666 commented 4 years ago

Could you please do me a favor and execute the file "information.py", available from the /examples folder or manually post what "lp.ListAll()" shows?

I need the MIDI devices' names.

gene1wood commented 4 years ago

Sure thing. I'm on Ubuntu 18.04 with pygame installed via the python-pygame package and launchpad.py installed with pip install --user launchpad_py under the system Python 2.7.17.

information.py returns

Running...
 - OS      : posix
 - Platform: Linux
 - Release : 4.15.0-96-generic
 - Python  : 2.7.17
 - PyGame  : 1.9.1release

Available MIDI devices:
('ALSA', 'Midi Through Port-0', 0, 1, 0)
('ALSA', 'Midi Through Port-0', 1, 0, 0)
('ALSA', 'Launch Control MIDI 1', 0, 1, 0)
('ALSA', 'Launch Control MIDI 1', 1, 0, 0)

goodbye ...

and here's what I get in /var/log/syslog when plugging the device in. I'm not entirely sure if every line of this relates to the device.

Apr 18 10:45:54 carol kernel: [150091.971852] usb 1-2.3.1: new full-speed USB device number 43 using xhci_hcd
Apr 18 10:45:55 carol kernel: [150092.303775] usb 1-2.3.1: New USB device found, idVendor=1235, idProduct=0034
Apr 18 10:45:55 carol kernel: [150092.303778] usb 1-2.3.1: New USB device strings: Mfr=1, Product=2, SerialNumber=0
Apr 18 10:45:55 carol kernel: [150092.303781] usb 1-2.3.1: Product: Launch Control
Apr 18 10:45:55 carol kernel: [150092.303782] usb 1-2.3.1: Manufacturer: Focusrite A.E. Ltd
Apr 18 10:45:55 carol mtp-probe: checking bus 1, device 43: "/sys/devices/pci0000:00/0000:00:01.3/0000:03:00.0/usb1/1-2/1-2.3/1-2.3.1"
Apr 18 10:45:55 carol mtp-probe: bus: 1, device: 43 was not an MTP device
Apr 18 10:45:55 carol kdeconnectd.desktop[9810]: UdevQt: unhandled device action "bind"
Apr 18 10:45:55 carol upowerd[2113]: unhandled action 'bind' on /sys/devices/pci0000:00/0000:00:01.3/0000:03:00.0/usb1/1-2/1-2.3/1-2.3.1/1-2.3.1:1.1
Apr 18 10:45:55 carol systemd-udevd[29353]: Process '/usr/sbin/alsactl -E HOME=/run/alsa restore 3' failed with exit code 99.
Apr 18 10:45:55 carol kdeconnectd.desktop[9810]: UdevQt: unhandled device action "bind"
Apr 18 10:45:55 carol upowerd[2113]: unhandled action 'bind' on /sys/devices/pci0000:00/0000:00:01.3/0000:03:00.0/usb1/1-2/1-2.3/1-2.3.1/1-2.3.1:1.0
Apr 18 10:45:55 carol kdeconnectd.desktop[9810]: UdevQt: unhandled device action "bind"
Apr 18 10:45:55 carol upowerd[2113]: unhandled action 'bind' on /sys/devices/pci0000:00/0000:00:01.3/0000:03:00.0/usb1/1-2/1-2.3/1-2.3.1
Apr 18 10:45:55 carol pulseaudio[9602]: [pulseaudio] module-alsa-card.c: Failed to find a working profile.
Apr 18 10:45:55 carol pulseaudio[9602]: [pulseaudio] module.c: Failed to load module "module-alsa-card" (argument: "device_id="3" name="usb-Focusrite_A.E._Ltd_Launch_Control-00" card_name="alsa_card.usb-Focusrite_A.E._Ltd_Launch_Control-00" namereg_fail=false tsched=yes fixed_latency_range=no ignore_dB=no deferred_volume=yes use_ucm=yes card_properties="module-udev-detect.discovered=1""): initialization failed.
Apr 18 10:45:55 carol colord-sane: [bjnp] create_broadcast_socket: ERROR - bind socket to local address failed - Cannot assign requested address
Apr 18 10:45:55 carol colord-sane: [bjnp] create_broadcast_socket: ERROR - bind socket to local address failed - Cannot assign requested address
Apr 18 10:46:00 carol colord-sane: io/hpmud/pp.c 627: unable to read device-id ret=-1

And here's when it gets unplugged

Apr 18 10:48:00 carol kernel: [150217.858248] usb 1-2.3.1: USB disconnect, device number 43
Apr 18 10:48:00 carol kdeconnectd.desktop[9810]: UdevQt: unhandled device action "unbind"
Apr 18 10:48:00 carol upowerd[2113]: unhandled action 'unbind' on /sys/devices/pci0000:00/0000:00:01.3/0000:03:00.0/usb1/1-2/1-2.3/1-2.3.1/1-2.3.1:1.1
Apr 18 10:48:00 carol kdeconnectd.desktop[9810]: UdevQt: unhandled device action "unbind"
Apr 18 10:48:00 carol upowerd[2113]: unhandled action 'unbind' on /sys/devices/pci0000:00/0000:00:01.3/0000:03:00.0/usb1/1-2/1-2.3/1-2.3.1/1-2.3.1:1.0
Apr 18 10:48:00 carol kdeconnectd.desktop[9810]: UdevQt: unhandled device action "unbind"
Apr 18 10:48:00 carol upowerd[2113]: unhandled action 'unbind' on /sys/devices/pci0000:00/0000:00:01.3/0000:03:00.0/usb1/1-2/1-2.3/1-2.3.1
Apr 18 10:48:00 carol colord-sane: [bjnp] create_broadcast_socket: ERROR - bind socket to local address failed - Cannot assign requested address
Apr 18 10:48:00 carol colord-sane: [bjnp] create_broadcast_socket: ERROR - bind socket to local address failed - Cannot assign requested address
Apr 18 10:48:05 carol colord-sane: io/hpmud/pp.c 627: unable to read device-id ret=-1
FMMT666 commented 4 years ago

Great thanks!

FMMT666 commented 4 years ago

Ok, I added the class "LaunchControl".

The following methods are available and should work:

Open( number = 0, name = "Control MIDI", template = 0 )
Check( number = 0, name = "Control MIDI" )
Reset( )
LedAllOn( colorcode = None )
LedGetColor( red, green )
LedCtrlRaw( number, red, green )
InputFlush()
InputChanged()
InputStateRaw()

This one's prepared, but not yet functional:

LedCtrlXY( x, y, red, green )

The problem ist that, as I don't have any access to this device, I don't know any of the button or potentiometer numbers, so - well, could you please execute this and note the numbers of the potentiometers and buttons, while you turn/press them? I only need the first number in the brackets.

lp = launchpad.LaunchControl()
lp.Open()

while(True):
  buts = lp.ButtonStateRaw()
  if buts != []:
    print( buts )

Thanks

gene1wood commented 4 years ago

So with #50 added, when I run this

#!/usr/bin/env python

import launchpad_py as launchpad

def main():
    lp = launchpad.LaunchControl()
    lp.Open()

if __name__ == '__main__':
    main()

I get this

Traceback (most recent call last):
  File "r.py", line 11, in <module>
    main()
  File "r.py", line 7, in main
    lp.Open()
  File "/home/gene/.local/lib/python2.7/site-packages/launchpad_py/launchpad.py", line 1849, in Open
    retval = super( LaunchControl, self ).Open( number = number, name = name )
  File "/home/gene/.local/lib/python2.7/site-packages/launchpad_py/launchpad.py", line 1622, in Open
    self.TemplateSet( self.UserTemplate )
  File "/home/gene/.local/lib/python2.7/site-packages/launchpad_py/launchpad.py", line 1874, in TemplateSet
    self.midi.RawWriteSysEx( [ 0, 32, 41, 2, 10, 119, templateNum-1 ] )
  File "/home/gene/.local/lib/python2.7/site-packages/launchpad_py/launchpad.py", line 167, in RawWriteSysEx
    self.devOut.write_sys_ex( timeStamp, array.array('B', [0xf0] + lstMessage + [0xf7] ).tostring() )
OverflowError: unsigned byte integer is less than minimum
Exception Exception: "PortMidi: `Bad pointer'" in <pypm.Input object at 0x7fc15d186690> ignored
Exception Exception: "PortMidi: `Bad pointer'" in <pypm.Output object at 0x7fc14ad46270> ignored

Also, I suspect I won't be able to use lp.ButtonStateRaw() as both the LaunchControl class and it's parent the LaunchControlXL class don't implement it. Did you mean lp.InputStateRaw()?

This is done with the template set to User : 1

IMG_20200418_214306

FMMT666 commented 4 years ago

Oh my, yes, of course; sorry.

lp = launchpad.LaunchControl()
lp.Open( 0 )

while(True):
  inps = lp.InputStateRaw()
  if inps != []:
    print( inps )

Also fixed that overflow issue. Could you try again, please?

Thanks.

gene1wood commented 4 years ago
Interface ID
Button 1 9
Button 2 10
Button 3 11
Button 4 12
Button 5 25
Button 6 26
Button 7 27
Button 8 28
Potentiometer 1 Top 21
Potentiometer 2 Top 22
Potentiometer 3 Top 23
Potentiometer 4 Top 24
Potentiometer 5 Top 25
Potentiometer 6 Top 26
Potentiometer 7 Top 27
Potentiometer 8 Top 28
Potentiometer 1 Bottom 41
Potentiometer 2 Bottom 42
Potentiometer 3 Bottom 43
Potentiometer 4 Bottom 44
Potentiometer 5 Bottom 45
Potentiometer 6 Bottom 46
Potentiometer 7 Bottom 47
Potentiometer 8 Bottom 48
Up Arrow 114
Down Arrow 115
Left Arrow 116
Right Arrow 117
    # LED AND BUTTON NUMBERS IN RAW MODE (DEC)
    #
    #       0   1   2   3   4   5   6   7      8    9
    #      
    #     +---+---+---+---+---+---+---+---+  +---++---+
    #  0  |21 |22 |23 |24 |25 |26 |27 |28 |  |NOP||NOP| 
    #     +---+---+---+---+---+---+---+---+  +---++---+
    #  1  |41 |42 |43 |44 |45 |46 |47 |48 |  |114||115| 
    #     +---+---+---+---+---+---+---+---+  +---++---+
    #     +---+---+---+---+---+---+---+---+  +---++---+
    #  2  |9  |10 |11 |12 |25 |26 |27 |28 |  |116||117| 
    #     +---+---+---+---+---+---+---+---+  +---++---+
FMMT666 commented 4 years ago

Great, thanks!

I updated the "docs" and added a tiny LedControlXY() method. Reversed Y bc I think that the major action is probably going to happen at the bottom row.

gene1wood commented 4 years ago

Excellent.

I've not been able to get the LED controls to work. When I try something like this

import launchpad_py as launchpad
lp = launchpad.LaunchControl()
lp.Open(0)
lp.LedCtrlRaw(9, 3, 3)
lp.LedCtrlXY(0, 0, 3, 3)
lp.LedAllOn()

None of the LEDs are activated.

I see that

LedCtrlRaw uses pygame.midi.Output().write_short(144, number, led)

LedCtrlXY uses pygame.midi.Output().write_sys_ex(timeStamp, array.array('B', [0xf0] + [ 0, 32, 41, 2, 10, 120, 0, index, color ] + [0xf7]).tostring())

I'm unsure why these two LED activation methods use these different methods of interacting with the midi device.

Also, how do you determine these values (like 144 and 0, 32, 41, 2, 10, 120, 0)?

Is there anything I can run to diagnose why the LED lights don't respond to the code?

FMMT666 commented 4 years ago

I hoped it would work like the XL, but that obviously isn't the case.

Also always make sure to add some delays between the calls. Otherwise it simply might be too fast to see anything. I assume you know this, but you could simply use PyGame's internal timer:

  from pygame import time
  ...
  time.wait( <ms> )

I changed LedCtrlXY(), that shall work now, except that I don't know the order of the four control (arrow) buttons. X is from left to right, starting at 0. Y is from bottom to top, starting at 0.

  lp.LedCtrlXY( 0, 0, 3, 3 )  # Pad 1 yellow
  lp.LedCtrlXY( 7, 0, 3, 3 )  # Pad 8 yellow
  lp.LedCtrlXY( 9, 0, 3, 3 )  # Arrow Right yellow (unsure)
  lp.LedCtrlXY( 8, 1, 3, 3 )  # Arrow Up yellow (unsure)

The numbers for LedCtrlRaw()are, at least in theory, those you sent me. 9-12, 25-28, 114-117.

  lp.LedCtrlRaw( 9, 3, 0)  # Pad 1 red
  lp.LedCtrlRaw( 11, 0, 3)  # Pad 3 green

If these number still don't work, you can just try other values (0..127). You cannot damage anything. Maybe they also start at 0? I don't know ...

gene1wood commented 4 years ago

Also always make sure to add some delays between the calls.

Ok, good hint.

lp.LedCtrlXY( 0, 0, 3, 3 ) # Pad 1 yellow lp.LedCtrlXY( 7, 0, 3, 3 ) # Pad 8 yellow lp.LedCtrlXY( 9, 0, 3, 3 ) # Arrow Right yellow (unsure) lp.LedCtrlXY( 8, 1, 3, 3 ) # Arrow Up yellow (unsure)

So I've tried this but it didn't light up any of the LEDs

import launchpad_py as launchpad
from pygame import time

lp = launchpad.LaunchControl()
lp.Open(0)

lp.LedCtrlXY( 0, 0, 3, 3 )  # Pad 1 yellow
time.wait(500)
lp.LedCtrlXY( 7, 0, 3, 3 )  # Pad 8 yellow
time.wait(500)
lp.LedCtrlXY( 9, 0, 3, 3 )  # Arrow Right yellow (unsure)
time.wait(500)
lp.LedCtrlXY( 8, 1, 3, 3 )  # Arrow Up yellow (unsure)
time.wait(500)

If these number still don't work, you can just try other values (0..127).

I tried iterating through all numbers 0-127 with 500ms waits between each calling lp.LedCtrlRaw( i, 3, 0) and didn't get any of the LEDs to light up.

I'm wondering, where does the value 144 come from in the LaunchControlXL().LedCtrlRaw() method which is what the LaunchControl class inherits? I see that it calls self.midi.RawWrite( 144, number, led ), what is that 144?

Also, given that I've tried lots of number values (0-127) I wondered if the issue is with LedGetColor and the data2 value so I tested by bypassing the LedGetColor method and calling lp.midi.RawWrite(144, 9, data2) and just tried numbers between 0 and 127 for the LED color. That didn't light up any LEDs either.

So, first a summary

and some questions

FMMT666 commented 4 years ago

The Control and the Control XL share exactly the same commands (except for that the SysEx ones use "10d" instead of "17d". for the Set LED command).

"144" is a MIDI "Note On" command, followed by the note and a velocity. The Launchpads use "note" as an identifier for the buttons, LEDs ... For LED activation, velocity determines the color. For the old, red/green devices, it's just a bitfield (MIDI uses 7 bits). Bits 5..4 for green and 1..0 for red. In between are "clear" and "copy", but those are not used in here (0).

The commands should be ok, this has to be something different. https://resource.novationmusic.com/sites/default/files/novation/downloads/6958/launch-control-programmers-reference-guide.pdf

Even when the output MIDI channel would be wrong, which might happen, than at least the SysEx commands shall be working, as they're completely indpendent.

Does the automatic switching of the templates work? If you manually set it to user 2..8, does it switch over to 1 after executing the lp.Open() code?

Could also be a Linux/ALSA issue. Did you compile PyGame or is this a version from PyPi?

gene1wood commented 4 years ago

I'm sorry I've not been able to follow up on this recently, I'll endeavor to make time to do so.