ffont / push2-python

Utils to interface with Push2 from Python
MIT License
85 stars 19 forks source link

ERROR:root:Could not initialize Push 2 Display: #5

Closed yoshakami closed 2 years ago

yoshakami commented 2 years ago

the code example provided in the readme gives out this error. I tried some workarounds to get the display working such as turning it off then on once the program is launched, or displaying a png, trying to set "use the user port" to false, but it's the same result

here's the full code, I made some edits so it would run without pressing any button (sorry about the extension, github restricts the file extension we can upload lol) push2-display-example.txt

ffont commented 2 years ago

Is this happening on linux? If so there might be issues with permissions for opening the usb port. Try running with sudo and see if it works. If it does, then you need to add extra permissions to the user running the script to make it work without sudo (should be similar to step 4 in these instructions)

yoshakami commented 2 years ago

ah, I forgot to mention I'm on Windows 10 and Python 3.9.9

yoshakami commented 2 years ago

I got some ideas to find out why, I've put some print in push2_python/display.py and I found that it's executing this code (meanwhile lighting up pads and launching functions when pressing buttons work well)

    def send_to_display(self, prepared_frame):
        if self.usb_endpoint is None:
            try:
                self.configure_usb_device()
            except Push2USBDeviceNotFoundas e:
                print("Push2USBDeviceNotFound")  #it prints this out
                log_error = False
                if self.push.simulator_controller is not None:
                   [....]
                else:
                    log_error = True
                    print("else")  # it prints this out
                if log_error:
                    print(e)
                    logging.error('Could not initialize Push 2 Display: {0}'.format(e))
ffont commented 2 years ago

Hi again, It could be then that the expected name of the MIDI device in Windows is not correct. I never tested on windows so I'm not sure about that. This is the function which checks whether a MIDI device port name corresponds to Push or not: https://github.com/ffont/push2-python/blob/884a3f06fdf4d78d0e99afc9a7cf6623bb7622d1/push2_python/constants.py#L14-L34

If you add some prints in the elif platform.system() == "Windows": (for example print port_name) you can probably see what is failing and update the logic so that it works properly.

yoshakami commented 2 years ago

still cannot make it run, thanks for pointing out this function, as it appears you've swapped out user port and live port names for both in and out, but putting them to something random makes the pad colour fonction no longer work, so I guess they're the right name as specified in the documentation, though "Ableton Push 2" and "Ableton Push" both work for both midi live port names

also, after this function, usb_device is equal to none (in display.py / configure_usb_device) usb_device = usb.core.find(idVendor=ABLETON_VENDOR_ID, idProduct=PUSH2_PRODUCT_ID)

yoshakami commented 2 years ago

yeah the main problem isn't the midi ports as the display isn't a midi signal as the documentation says here https://github.com/Ableton/push-interface/blob/master/doc/AbletonPush2MIDIDisplayInterface.asc#12-architecture-overview it's rather than the whole list(usb.core.find(find_all=True)) is an empty list

yoshakami commented 2 years ago

OH WOW, thanks for responding, I found out the only little problem is that you've swapped out the midi port names, these should be the corrected functions

def is_push_midi_in_port_name(port_name, use_user_port=False):
    """Returns True if the given 'port_name' is the MIDI port name corresponding to Push2 MIDI
    input for the current OS platform. If 'use_user_port', it will check against Push2 User port instead
    of Push2 Live port.
    See https://github.com/Ableton/push-interface/blob/master/doc/AbletonPush2MIDIDisplayInterface.asc#21-midi-interface-access
    """
    if platform.system() == "Linux":
        if not use_user_port:
            return 'Ableton Push' in port_name and port_name.endswith(':0')  # 'Ableton Push 2 nn:0', with nn being a variable number
        else:
            return 'Ableton Push' in port_name and port_name.endswith(':1')  # 'Ableton Push 2 nn:1', with nn being a variable number
    elif platform.system() == "Windows":
        if not use_user_port:
            print("live port on windows")
            return 'Ableton Push 2' in port_name  # 'Ableton Push 2 nn', with nn being a variable number
        else:
            print("user port on windows")
            return 'MIDIIN2 (Ableton Push 2)' in port_name  # 'MIDIIN2 (Ableton Push 2) nn', with nn being a variable number
    else: #macOS
        if not use_user_port:
            return 'Ableton Push 2 Live Port' in port_name
        else:
            return 'Ableton Push 2 User Port' in port_name

def is_push_midi_out_port_name(port_name, use_user_port=False):
    """Returns True if the given 'port_name' is the MIDI port name corresponding to Push2 MIDI
    output for the current OS platform. If 'use_user_port', it will check against Push2 User port instead
    of Push2 Live port.
    See https://github.com/Ableton/push-interface/blob/master/doc/AbletonPush2MIDIDisplayInterface.asc#21-midi-interface-access
    """
    if platform.system() == "Linux":
        if not use_user_port:
            return 'Ableton Push' in port_name and port_name.endswith(':0')  # 'Ableton Push 2 nn:0', with nn being a variable number
        else:
            return 'Ableton Push' in port_name and port_name.endswith(':1')  # 'Ableton Push 2 nn:1', with nn being a variable number
    elif platform.system() == "Windows":
        if not use_user_port:
            return 'Ableton Push 2' in port_name  # 'Ableton Push 2 nn', with nn being a variable number
        else:
            return 'MIDIOUT2 (Ableton Push 2)' in port_name  # 'MIDIIN2 (Ableton Push 2) nn', with nn being a variable number
    else: #macOS
        if not use_user_port:
            return 'Ableton Push 2 Live Port' == port_name
        else:
            return 'Ableton Push 2 User Port' == port_name

tested on Windows and working perfectly :D this is the link that made me think it's a py_usb silent fail not finding any libusb-1.0.dll https://stackoverflow.com/questions/8555930/pyusb-cant-find-device if anyone on windows ever wants to display a static frame on push2, you'll need to paste "libusb-1.0.dll" in a folder contained in the PATH variable, such as "C:/Windows" or "C:/Windows/System32" you can find this dll in "C:\ProgramData\Ableton\\\Program\libusb-1.0.dll"

NOTE: as specified in ableton documentation, a frame will only last 2 seconds then drop off to a black screen, so you'll need to send the same one before 2 seconds each time

by the way, you don't need git to install this repo, you can put the folder on your local machine then run pip install ./push2-python-master ./ before the folder name specifies it's a relative path that pip will not search online

ffont commented 2 years ago

Glad so see you got it working! Do you want to make a PR with the change of port names in windows?

yoshakami commented 2 years ago

I can make one, I've also been making a function to save a prepared frame to get over encoding the same frame each time

karen-pal commented 1 year ago

I'm running on linux and facing permission difficulties for sending to the display...

usb.core.USBError: [Errno 13] Access denied (insufficient permissions)

Can light up the pads just fine, already had a lot of fun with that so thanks for this lib!

I'm also not receiving the inputs on callback apparently... could you give me a clue on how to continue? did you have to register push2-python as a service like in pysha? I tried adding a rule in etc/udev/rules.d/50-push2.rules and following those instructions but found no success.

Thanks!

ffont commented 1 year ago

Hi,

Did you try running as root to see if it works and confirm it is a permissions issue? If that is the case maybe you need some alternative to the etc/udev/rules.d/50-push2.rules trick, but something like that should work.

About the callbacks this is a bit strange as if you're able to set pads and therefore communicate with Push successfully, you should be receiving callbacks as well. Are you sure your "app loop" is correct? Are you running the code from some of the examples?

let me know how it goes...

karen-pal commented 1 year ago

Hi,

Did you try running as root to see if it works and confirm it is a permissions issue? If that is the case maybe you need some alternative to the etc/udev/rules.d/50-push2.rules trick, but something like that should work.

About the callbacks this is a bit strange as if you're able to set pads and therefore communicate with Push successfully, you should be receiving callbacks as well. Are you sure your "app loop" is correct? Are you running the code from some of the examples?

let me know how it goes...

I resumed yesterday developings with your library and found that when triggering events with the simulation page everything works, but not with the code from the examples.... got stuck with that, but the images are displayed correctly when triggering the display event with an event from the simulator! can't get it to work yet with the push2 itself. For example this example code:

@push2_python.on_button_pressed(push2_python.constants.BUTTON_PLAY)
def on_play_pressed(push):
    print('Play!')

doesn't work in the context of a code snippet with a while True loop that is correctly lighting up pads:


import push2_python
import random
import numpy
from PIL import Image
import time

# Init Push2
push = push2_python.Push2(run_simulator=True)
print(dir(push))

# Define util function to generate a frame with some colors to be shown in the display
# Frames are created as matrices of shape 960x160 and with colors defined in bgr565 format
# This function is defined in a rather silly way, could probably be optimized a lot ;)
def generate_3_color_frame():
    colors = ['{b:05b}{g:06b}{r:05b}'.format(
        r=int(31*random.random()), g=int(63*random.random()), b=int(31*random.random())),
        '{b:05b}{g:06b}{r:05b}'.format(
        r=int(31*random.random()), g=int(63*random.random()), b=int(31*random.random())),
        '{b:05b}{g:06b}{r:05b}'.format(
        r=int(31*random.random()), g=int(63*random.random()), b=int(31*random.random()))]
    colors = [int(c, 2) for c in colors]
    line_bytes = []
    for i in range(0, 960):  # 960 pixels per line
        if i <= 960 // 3:
            line_bytes.append(colors[0])
        elif 960 // 3 < i <= 2 * 960 // 3:
            line_bytes.append(colors[1])
        else:
            line_bytes.append(colors[2])
    frame = []
    for i in range(0, 160):  # 160 lines
        frame.append(line_bytes)
    return numpy.array(frame, dtype=numpy.uint16).transpose()

# Pre-generate different color frames
color_frames = list()
for i in range(0, 20):
    color_frames.append(generate_3_color_frame())

# Now crate an extra frame which loads an image from a file. Image must be 960x160 pixels.
img = Image.open('test.png') #_img_960x160.png')
frame = numpy.array(img)
frame = frame/255  # Convert rgb values to [0.0, 1.0] floats

# Now lets configure some action handlers which will display frames in Push2's display in 
# reaction to pad and button presses
@push2_python.on_pad_pressed()
def on_pad_pressed(push, pad_n, pad_ij, velocity):
    print("pad PRESSED")
    # Display one of the three color frames on the display
    random_frame = random.choice(color_frames)
    push.display.display_frame(random_frame)

@push2_python.on_button_pressed()
def on_button_pressed(push, button_name):
    print("button PRESSED")
    # Display the frame with the loaded image
    push.display.display_frame(frame, input_format=push2_python.constants.FRAME_FORMAT_RGB)

@push2_python.on_button_pressed(push2_python.constants.BUTTON_PLAY)
def on_play_pressed(push):
    print('Play!')

# Start infinite loop so the app keeps running
print('App runnnig...')
colors = ['black','orange','yellow','turquoise','dark_gray','purple','pink','light_gray','white','light_gray','dark_gray','blue', 'green','red', 'white']

import numpy
#push.display.display_frame(numpy.array(img,dtype=numpy.uint16), input_format=push2_python.constants.FRAME_FORMAT_RGB)

@push2_python.on_touchstrip()
def on_touchstrip(push, value):
    print('Touchstrip touched with value', value)

color = "green"

while True:
    push.buttons.set_all_buttons_color("white")
    for i in range(0,8):
        for j in range(0,8):
            pad_ij = (i, j)  # Fourth pad of the top row
            color = random.choice(colors)
            push.pads.set_pad_color(pad_ij,color=color)
            time.sleep(.1)