ideoforms / AbletonOSC

Control Ableton Live 11 via Open Sound Control (OSC)
MIT License
422 stars 70 forks source link

Live 12 support #117

Closed esaruoho closed 1 month ago

esaruoho commented 8 months ago

Hi, is Live 12 support incoming for AbletonOSC?

ideoforms commented 8 months ago

I don't currently have access to the Live 12 beta, but will get a copy when it's released and be able to confirm then.

If you have access to the beta and can verify, any reports would be appreciated!

esaruoho commented 8 months ago

I do have access but am on a new computer so dont yet ha e AbletonOSC installed. I will check and see if it works!! Fingers crossed :)

esaruoho commented 8 months ago

i'll try and send messages to it hopefully tomorrow evening. is this promising, however, @ideoforms ?

Screenshot 2024-02-23 at 23 09 24

edit: I had to put in an image a second time to make sure it shows the bottom bar info

esaruoho commented 8 months ago

i can confirm that my clip pitchers, track volume controls and clip launchers + recorders work. unfortunately i'm still having issues with Grid objects - which seems to be a TouchOSC specific issue, all Grid objects are unstable. not sure how to proceed with this, since I'd need to talk with TouchOSC developers about this.

But yes, TouchOSC definitely works with Live12. So closing this!

ideoforms commented 8 months ago

Thanks for the report @esaruoho !

esaruoho commented 1 month ago

yep, TouchOSC people fixed the Grid object issues.

i'm now trying to figure out how to get my TouchOSC script to talk to Ableton Live 12 again. I definitely had them working on February, but it's September now. Trying to remember where all of this was.

what i can say is that i'm on macOS Sonoma 14.6.1

I've tried with Ableton Live 11 and Ableton Live 12, i've re-installed and copied and installed, and both state that the old folder is able to be recognized and they connect to 11000, but for some reason, i can't make the connection.

to add insult to injury, even sending messages from ./run-console.py no longer work.

Is there some form of getting more details from ./run-console.py ? @ideoforms like more logging?

this is definitely the computer i was using back then, which is very strange. i might've updated to a newer version of Python and a newer version of macOS, but i'm completely in the dark here, being unable to get ./run

ideoforms commented 1 month ago

Hmm, that's strange. Try running ./run-console.py -v — this turns on verbose output, which will report whenever a message is received from Ableton.

I've just tested here with the latest Live 12 on macOS Ventura and it is behaving as expected. Here's the output I get if I try querying the tempo:

(1823)(master)(mbp2022:AbletonOSC)$ ./run-console.py
AbletonOSC command console
Usage: /live/osc/command [params]
>>> /live/song/get/tempo
120.0
>>> ^D
(1823)(master)(mbp2022:AbletonOSC)$ ./run-console.py  -v
AbletonOSC command console
Usage: /live/osc/command [params]
>>> /live/startup ()
/live/application/get/average_process_usage ()
/live/song/get/tempo
/live/song/get/tempo (120.0,)
120.0
>>> ^D

Can you confirm that: AbletonOSC is visible in the Control Surface column in "Link, Tempo & MIDI" (in Preferences)? You should also see some logging in the internal AbletonOSC logs: ~/Music/Ableton/User Library/Remote Scripts/AbletonOSC/logs/abletonosc.log

esaruoho commented 1 month ago

this is what i get, unfortunately:

$ ./run-console.py -v 
AbletonOSC command console
Usage: /live/osc/command [params]
>>> /live/osc/get/tempo
>>> /live/osc/get/tempo
>>> /live/osc/get/tempo
>>> /live/startup ()
(after which i pressed ctrl-c)
>>> Traceback (most recent call last):
  File "/Users/esaruoho/work/AbletonOSC/./run-console.py", line 96, in <module>
    main(args)
  File "/Users/esaruoho/work/AbletonOSC/./run-console.py", line 52, in main
    command_str = input(">>> ")
                  ^^^^^^^^^^^^^
KeyboardInterrupt
Screenshot 2024-09-18 at 22 32 46

i tried it without sync,remote,mpe. no change.

when i turn the row the AbletonOSC is on, to Off, and re-select it, i do get the yellow message at the bottom:

Screenshot 2024-09-18 at 22 33 34

there's a brief delay between /live/osc/get/tempo and the next >>> but nothing more. not sure how to proceed.

also, no logging appears in the Remote Scripts folder in the expected place.

esaruoho commented 1 month ago

ok, three more details.

$ ./run-console.py -v
/Users/esaruoho/Music/Ableton/User Library/Remote Scripts/AbletonOSC/pythonosc/dispatcher.py:153: SyntaxWarning: invalid escape sequence '\w'
  pattern = pattern.replace('\\*', '[\w|\+]*')
AbletonOSC command console
Usage: /live/osc/command [params]
>>> 

this strange error.

also, i am using an older AbletonOSC build. the reason for that? i downloaded the current zip, and followed the advice for unzipping, and renaming the folder abletonosc-master to AbletonOSC. i put that folder into the User Scripts, and while it shows in the settings menu, it does not give me the yellow message at the bottom, which is very strange, right?

esaruoho commented 1 month ago

ok, i was told to do this:

replace
pattern = pattern.replace('\\*', '[\w|\+]*')
with
pattern = pattern.replace('\\*', '[\\w|\\+]*')

let's see if that helps. ok i fixed it, and now -v no longer gives an error. but also no information.

is the problem that i'm using Live 12.0.25 ( Build: 2024-08-27_2627c43816)

ideoforms commented 1 month ago

To debug the startup issue with the latest .zip, could you try running Live with the latest AbletonOSC installed/enabled, run the instructions in "Debugging compile-time issues" and paste the output? And do the same with the older version installed in case it's hitting some runtime error.

I am also running Live 12.0.25 and no problems. I wonder if it's a network issue.

Could you also paste the output of the below terminal command which will list UDP ports that Live is listening on?

sudo lsof -nP -i4UDP | grep ^Live
esaruoho commented 1 month ago

(old version) ok. this is very strange. i was getting no results, until i thought, let's close TouchOSC.

now that i completely quit TouchOSC - but have TouchOSC Bridge running, now i get messages!

i'm starting to think i maybe had TouchOSC listening to 11000 and capturing it, and that's why the errors. i'll continue troubleshooting from this point of view. Thanks a lot for your help!

>>> /live/song/get/tempo
/live/song/get/tempo (86.75,)
(86.75,)
>>> 
esaruoho commented 1 month ago

i ran this on the new version.

$ pytest
================================================================= test session starts ==================================================================
platform darwin -- Python 3.12.6, pytest-8.3.3, pluggy-1.5.0
rootdir: /Users/esaruoho/Music/Ableton/User Library/Remote Scripts/AbletonOSC
collected 54 items                                                                                                                                     

tests/test_application.py .F.                                                                                                                    [  5%]
tests/test_bundle.py .                                                                                                                           [  7%]
tests/test_clip.py FFFFFFFFF                                                                                                                     [ 24%]
tests/test_clip_slot.py FFF                                                                                                                      [ 29%]
tests/test_song.py ..F..................FFFF                                                                                                     [ 75%]
tests/test_track.py .......FFF                                                                                                                   [ 94%]
tests/test_view.py ..F                                                                                                                           [100%]

======================================================================= FAILURES =======================================================================
_____________________________________________________________ test_application_get_version _____________________________________________________________

client = <AbletonOSC.client.client.AbletonOSCClient object at 0x104b47200>

    def test_application_get_version(client):
        rv = client.query("/live/application/get/version")
>       assert len(rv) == 2 and rv[0] == 11
E       assert (2 == 2 and 12 == 11)
E        +  where 2 = len((12, 0))

tests/test_application.py:12: AssertionError
_______________________________________________________________ test_clip_property_name ________________________________________________________________

client = <AbletonOSC.client.client.AbletonOSCClient object at 0x105aadb50>

    def test_clip_property_name(client):
>       _test_clip_property(client, 0, 0, "name", ("Alpha", "Beta"))

tests/test_clip.py:40: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
tests/test_clip.py:37: in _test_clip_property
    assert client.query("/live/clip/get/%s" % property, (track_id, clip_id)) == (track_id, clip_id, value,)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <AbletonOSC.client.client.AbletonOSCClient object at 0x105aadb50>, address = '/live/clip/get/name', params = (0, 0), timeout = 0.15

    def query(self,
              address: str,
              params: tuple = (),
              timeout: float = TICK_DURATION):
        rv = None
        _event = threading.Event()

        def received_response(address, params):
            nonlocal rv
            nonlocal _event
            rv = params
            _event.set()

        self.set_handler(address, received_response)
        self.send_message(address, params)
        _event.wait(timeout)
        self.remove_handler(address)
        if not _event.is_set():
>           raise RuntimeError("No response received to query: %s" % address)
E           RuntimeError: No response received to query: /live/clip/get/name

client/client.py:153: RuntimeError
_______________________________________________________________ test_clip_property_color _______________________________________________________________

client = <AbletonOSC.client.client.AbletonOSCClient object at 0x105aadb50>

    def test_clip_property_color(client):
>       _test_clip_property(client, 0, 0, "color", (0x001AFF2F, 0x001A2F96))

tests/test_clip.py:43: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
tests/test_clip.py:37: in _test_clip_property
    assert client.query("/live/clip/get/%s" % property, (track_id, clip_id)) == (track_id, clip_id, value,)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <AbletonOSC.client.client.AbletonOSCClient object at 0x105aadb50>, address = '/live/clip/get/color', params = (0, 0), timeout = 0.15

    def query(self,
              address: str,
              params: tuple = (),
              timeout: float = TICK_DURATION):
        rv = None
        _event = threading.Event()

        def received_response(address, params):
            nonlocal rv
            nonlocal _event
            rv = params
            _event.set()

        self.set_handler(address, received_response)
        self.send_message(address, params)
        _event.wait(timeout)
        self.remove_handler(address)
        if not _event.is_set():
>           raise RuntimeError("No response received to query: %s" % address)
E           RuntimeError: No response received to query: /live/clip/get/color

client/client.py:153: RuntimeError
_______________________________________________________________ test_clip_property_gain ________________________________________________________________

client = <AbletonOSC.client.client.AbletonOSCClient object at 0x105aadb50>

    def test_clip_property_gain(client):
>       _test_clip_property(client, 2, 0, "gain", (0.5, 1.0))

tests/test_clip.py:46: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
tests/test_clip.py:37: in _test_clip_property
    assert client.query("/live/clip/get/%s" % property, (track_id, clip_id)) == (track_id, clip_id, value,)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <AbletonOSC.client.client.AbletonOSCClient object at 0x105aadb50>, address = '/live/clip/get/gain', params = (2, 0), timeout = 0.15

    def query(self,
              address: str,
              params: tuple = (),
              timeout: float = TICK_DURATION):
        rv = None
        _event = threading.Event()

        def received_response(address, params):
            nonlocal rv
            nonlocal _event
            rv = params
            _event.set()

        self.set_handler(address, received_response)
        self.send_message(address, params)
        _event.wait(timeout)
        self.remove_handler(address)
        if not _event.is_set():
>           raise RuntimeError("No response received to query: %s" % address)
E           RuntimeError: No response received to query: /live/clip/get/gain

client/client.py:153: RuntimeError
___________________________________________________________ test_clip_property_pitch_coarse ____________________________________________________________

client = <AbletonOSC.client.client.AbletonOSCClient object at 0x105aadb50>

    def test_clip_property_pitch_coarse(client):
>       _test_clip_property(client, 2, 0, "pitch_coarse", (4, 0))

tests/test_clip.py:49: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
tests/test_clip.py:37: in _test_clip_property
    assert client.query("/live/clip/get/%s" % property, (track_id, clip_id)) == (track_id, clip_id, value,)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <AbletonOSC.client.client.AbletonOSCClient object at 0x105aadb50>, address = '/live/clip/get/pitch_coarse', params = (2, 0), timeout = 0.15

    def query(self,
              address: str,
              params: tuple = (),
              timeout: float = TICK_DURATION):
        rv = None
        _event = threading.Event()

        def received_response(address, params):
            nonlocal rv
            nonlocal _event
            rv = params
            _event.set()

        self.set_handler(address, received_response)
        self.send_message(address, params)
        _event.wait(timeout)
        self.remove_handler(address)
        if not _event.is_set():
>           raise RuntimeError("No response received to query: %s" % address)
E           RuntimeError: No response received to query: /live/clip/get/pitch_coarse

client/client.py:153: RuntimeError
____________________________________________________________ test_clip_property_pitch_fine _____________________________________________________________

client = <AbletonOSC.client.client.AbletonOSCClient object at 0x105aadb50>

    def test_clip_property_pitch_fine(client):
>       _test_clip_property(client, 2, 0, "pitch_fine", (0.5, 0.0))

tests/test_clip.py:52: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
tests/test_clip.py:37: in _test_clip_property
    assert client.query("/live/clip/get/%s" % property, (track_id, clip_id)) == (track_id, clip_id, value,)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <AbletonOSC.client.client.AbletonOSCClient object at 0x105aadb50>, address = '/live/clip/get/pitch_fine', params = (2, 0), timeout = 0.15

    def query(self,
              address: str,
              params: tuple = (),
              timeout: float = TICK_DURATION):
        rv = None
        _event = threading.Event()

        def received_response(address, params):
            nonlocal rv
            nonlocal _event
            rv = params
            _event.set()

        self.set_handler(address, received_response)
        self.send_message(address, params)
        _event.wait(timeout)
        self.remove_handler(address)
        if not _event.is_set():
>           raise RuntimeError("No response received to query: %s" % address)
E           RuntimeError: No response received to query: /live/clip/get/pitch_fine

client/client.py:153: RuntimeError
______________________________________________________________ test_clip_add_remove_notes ______________________________________________________________

client = <AbletonOSC.client.client.AbletonOSCClient object at 0x105aadb50>

    def test_clip_add_remove_notes(client):
>       assert client.query("/live/clip/get/notes", (0, 0)) == (0, 0)

tests/test_clip.py:55: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <AbletonOSC.client.client.AbletonOSCClient object at 0x105aadb50>, address = '/live/clip/get/notes', params = (0, 0), timeout = 0.15

    def query(self,
              address: str,
              params: tuple = (),
              timeout: float = TICK_DURATION):
        rv = None
        _event = threading.Event()

        def received_response(address, params):
            nonlocal rv
            nonlocal _event
            rv = params
            _event.set()

        self.set_handler(address, received_response)
        self.send_message(address, params)
        _event.wait(timeout)
        self.remove_handler(address)
        if not _event.is_set():
>           raise RuntimeError("No response received to query: %s" % address)
E           RuntimeError: No response received to query: /live/clip/get/notes

client/client.py:153: RuntimeError
_______________________________________________________________ test_clip_add_many_notes _______________________________________________________________

client = <AbletonOSC.client.client.AbletonOSCClient object at 0x105aadb50>

    def test_clip_add_many_notes(client):
        """
        Test adding large numbers of notes to a clip.
        Note that Ableton API's get_notes returns notes sorted by pitch, then time, so add notes
        in this same order.
        """
        random.seed(0)
        all_note_data = []
        pitch = 0
        for pitch_index in range(127):
            time = random.randrange(-32, 32) / 4
            duration = random.randrange(1, 4) / 4
            velocity = random.randrange(1, 128)
            # Create multiple instances of the same sequence, shifted in time.
            for timeshift in range(3):
                note = (pitch,
                        time + (timeshift * 8),
                        duration,
                        velocity,
                        False)
                all_note_data += note
            pitch += 1
        all_note_data = tuple(all_note_data)

        # Check clip is initially empty
>       assert client.query("/live/clip/get/notes", (0, 0)) == (0, 0)

tests/test_clip.py:108: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <AbletonOSC.client.client.AbletonOSCClient object at 0x105aadb50>, address = '/live/clip/get/notes', params = (0, 0), timeout = 0.15

    def query(self,
              address: str,
              params: tuple = (),
              timeout: float = TICK_DURATION):
        rv = None
        _event = threading.Event()

        def received_response(address, params):
            nonlocal rv
            nonlocal _event
            rv = params
            _event.set()

        self.set_handler(address, received_response)
        self.send_message(address, params)
        _event.wait(timeout)
        self.remove_handler(address)
        if not _event.is_set():
>           raise RuntimeError("No response received to query: %s" % address)
E           RuntimeError: No response received to query: /live/clip/get/notes

client/client.py:153: RuntimeError
__________________________________________________________ test_clip_playing_position_listen ___________________________________________________________

client = <AbletonOSC.client.client.AbletonOSCClient object at 0x105aadb50>

    def test_clip_playing_position_listen(client):
        client.send_message("/live/clip/start_listen/playing_position", [0, 0])
        client.send_message("/live/clip/fire", [0, 0])

>       rv = client.await_message("/live/clip/get/playing_position", TICK_DURATION * 2)

tests/test_clip.py:121: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <AbletonOSC.client.client.AbletonOSCClient object at 0x105aadb50>, address = '/live/clip/get/playing_position', timeout = 0.25

    def await_message(self,
                      address: str,
                      timeout: float = TICK_DURATION):
        """
        Awaits a reply from the given `address`, and optionally asserts that the function `fn`
        returns True when called with the returned OSC parameters.

        Args:
            address: OSC query (and reply) address
            fn: Optional assertion function
            timeout: Maximum number of seconds to wait for a successful reply

        Returns:
            True if the reply is received within the timeout period and the assertion succeeds,
            False otherwise

        """
        rv = None
        _event = threading.Event()

        def received_response(address, params):
            print("Received response: %s %s" % (address, str(params)))
            nonlocal rv
            nonlocal _event
            rv = params
            _event.set()

        self.set_handler(address, received_response)
        _event.wait(timeout)
        self.remove_handler(address)
        if not _event.is_set():
>           raise RuntimeError("No response received to query: %s" % address)
E           RuntimeError: No response received to query: /live/clip/get/playing_position

client/client.py:132: RuntimeError
______________________________________________________________ test_clip_listen_lifecycle ______________________________________________________________

client = <AbletonOSC.client.client.AbletonOSCClient object at 0x105aadb50>

    def test_clip_listen_lifecycle(client):
        client.send_message("/live/clip/set/name", [0, 0, "Alpha"])
        wait_one_tick()
        client.send_message("/live/clip/start_listen/name", [0, 0])
>       assert client.await_message("/live/clip/get/name", TICK_DURATION * 2) == (0, 0, "Alpha")

tests/test_clip.py:135: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <AbletonOSC.client.client.AbletonOSCClient object at 0x105aadb50>, address = '/live/clip/get/name', timeout = 0.25

    def await_message(self,
                      address: str,
                      timeout: float = TICK_DURATION):
        """
        Awaits a reply from the given `address`, and optionally asserts that the function `fn`
        returns True when called with the returned OSC parameters.

        Args:
            address: OSC query (and reply) address
            fn: Optional assertion function
            timeout: Maximum number of seconds to wait for a successful reply

        Returns:
            True if the reply is received within the timeout period and the assertion succeeds,
            False otherwise

        """
        rv = None
        _event = threading.Event()

        def received_response(address, params):
            print("Received response: %s %s" % (address, str(params)))
            nonlocal rv
            nonlocal _event
            rv = params
            _event.set()

        self.set_handler(address, received_response)
        _event.wait(timeout)
        self.remove_handler(address)
        if not _event.is_set():
>           raise RuntimeError("No response received to query: %s" % address)
E           RuntimeError: No response received to query: /live/clip/get/name

client/client.py:132: RuntimeError
_______________________________________________________________ test_clip_slot_has_clip ________________________________________________________________

client = <AbletonOSC.client.client.AbletonOSCClient object at 0x105aafb30>

    def test_clip_slot_has_clip(client):
>       assert client.query("/live/clip_slot/get/has_clip", (0, 0)) == (0, 0, False)

tests/test_clip_slot.py:4: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <AbletonOSC.client.client.AbletonOSCClient object at 0x105aafb30>, address = '/live/clip_slot/get/has_clip', params = (0, 0), timeout = 0.15

    def query(self,
              address: str,
              params: tuple = (),
              timeout: float = TICK_DURATION):
        rv = None
        _event = threading.Event()

        def received_response(address, params):
            nonlocal rv
            nonlocal _event
            rv = params
            _event.set()

        self.set_handler(address, received_response)
        self.send_message(address, params)
        _event.wait(timeout)
        self.remove_handler(address)
        if not _event.is_set():
>           raise RuntimeError("No response received to query: %s" % address)
E           RuntimeError: No response received to query: /live/clip_slot/get/has_clip

client/client.py:153: RuntimeError
_______________________________________________________________ test_clip_slot_duplicate _______________________________________________________________

client = <AbletonOSC.client.client.AbletonOSCClient object at 0x105aafb30>

    def test_clip_slot_duplicate(client):
        client.send_message("/live/clip_slot/create_clip", [0, 0, 4.0])
        client.send_message("/live/clip/get/notes", (0, 0))
>       assert client.await_message("/live/clip/get/notes") == (0, 0)

tests/test_clip_slot.py:12: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <AbletonOSC.client.client.AbletonOSCClient object at 0x105aafb30>, address = '/live/clip/get/notes', timeout = 0.15

    def await_message(self,
                      address: str,
                      timeout: float = TICK_DURATION):
        """
        Awaits a reply from the given `address`, and optionally asserts that the function `fn`
        returns True when called with the returned OSC parameters.

        Args:
            address: OSC query (and reply) address
            fn: Optional assertion function
            timeout: Maximum number of seconds to wait for a successful reply

        Returns:
            True if the reply is received within the timeout period and the assertion succeeds,
            False otherwise

        """
        rv = None
        _event = threading.Event()

        def received_response(address, params):
            print("Received response: %s %s" % (address, str(params)))
            nonlocal rv
            nonlocal _event
            rv = params
            _event.set()

        self.set_handler(address, received_response)
        _event.wait(timeout)
        self.remove_handler(address)
        if not _event.is_set():
>           raise RuntimeError("No response received to query: %s" % address)
E           RuntimeError: No response received to query: /live/clip/get/notes

client/client.py:132: RuntimeError
____________________________________________________________ test_clip_slot_property_listen ____________________________________________________________

client = <AbletonOSC.client.client.AbletonOSCClient object at 0x105aafb30>

    def test_clip_slot_property_listen(client):
        client.send_message("/live/clip_slot/start_listen/has_clip", (0, 0))
        assert client.await_message("/live/clip_slot/get/has_clip", TICK_DURATION * 2) == (0, 0, False)
        client.send_message("/live/clip_slot/create_clip", [0, 0, 4.0])
>       assert client.await_message("/live/clip_slot/get/has_clip", TICK_DURATION * 2) == (0, 0, True)

tests/test_clip_slot.py:29: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <AbletonOSC.client.client.AbletonOSCClient object at 0x105aafb30>, address = '/live/clip_slot/get/has_clip', timeout = 0.25

    def await_message(self,
                      address: str,
                      timeout: float = TICK_DURATION):
        """
        Awaits a reply from the given `address`, and optionally asserts that the function `fn`
        returns True when called with the returned OSC parameters.

        Args:
            address: OSC query (and reply) address
            fn: Optional assertion function
            timeout: Maximum number of seconds to wait for a successful reply

        Returns:
            True if the reply is received within the timeout period and the assertion succeeds,
            False otherwise

        """
        rv = None
        _event = threading.Event()

        def received_response(address, params):
            print("Received response: %s %s" % (address, str(params)))
            nonlocal rv
            nonlocal _event
            rv = params
            _event.set()

        self.set_handler(address, received_response)
        _event.wait(timeout)
        self.remove_handler(address)
        if not _event.is_set():
>           raise RuntimeError("No response received to query: %s" % address)
E           RuntimeError: No response received to query: /live/clip_slot/get/has_clip

client/client.py:132: RuntimeError
----------------------------------------------------------------- Captured stdout call -----------------------------------------------------------------
Received response: /live/clip_slot/get/has_clip (0, 0, False)
_______________________________________________________________ test_song_stop_all_clips _______________________________________________________________

client = <AbletonOSC.client.client.AbletonOSCClient object at 0x105ac3710>

    def test_song_stop_all_clips(client):
        client.send_message("/live/clip_slot/create_clip", (0, 0, 4))
        client.send_message("/live/clip_slot/create_clip", (1, 0, 4))
        client.send_message("/live/clip/fire", (0, 0))
        client.send_message("/live/clip/fire", (1, 0))
        # Sometimes a wait >one tick is required here. Not sure why.
        wait_one_tick()
        wait_one_tick()
>       assert client.query("/live/clip/get/is_playing", (0, 0)) == (0, 0, True,)

tests/test_song.py:40: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <AbletonOSC.client.client.AbletonOSCClient object at 0x105ac3710>, address = '/live/clip/get/is_playing', params = (0, 0), timeout = 0.15

    def query(self,
              address: str,
              params: tuple = (),
              timeout: float = TICK_DURATION):
        rv = None
        _event = threading.Event()

        def received_response(address, params):
            nonlocal rv
            nonlocal _event
            rv = params
            _event.set()

        self.set_handler(address, received_response)
        self.send_message(address, params)
        _event.wait(timeout)
        self.remove_handler(address)
        if not _event.is_set():
>           raise RuntimeError("No response received to query: %s" % address)
E           RuntimeError: No response received to query: /live/clip/get/is_playing

client/client.py:153: RuntimeError
___________________________________________________________________ test_song_tracks ___________________________________________________________________

client = <AbletonOSC.client.client.AbletonOSCClient object at 0x105ac3710>

    def test_song_tracks(client):
>       assert client.query("/live/song/get/num_tracks") == (4,)
E       assert (85,) == (4,)
E         
E         At index 0 diff: 85 != 4
E         Use -v to get more diff

tests/test_song.py:144: AssertionError
___________________________________________________________________ test_song_scenes ___________________________________________________________________

client = <AbletonOSC.client.client.AbletonOSCClient object at 0x105ac3710>

    def test_song_scenes(client):
>       assert client.query("/live/song/get/num_scenes") == (8,)
E       assert (2,) == (8,)
E         
E         At index 0 diff: 2 != 8
E         Use -v to get more diff

tests/test_song.py:161: AssertionError
______________________________________________________________ test_song_duplicate_scene _______________________________________________________________

client = <AbletonOSC.client.client.AbletonOSCClient object at 0x105ac3710>

    def test_song_duplicate_scene(client):
        track_id = 0
        scene_id = 7
>       assert client.query("/live/song/get/num_scenes") == (8,)
E       assert (2,) == (8,)
E         
E         At index 0 diff: 2 != 8
E         Use -v to get more diff

tests/test_song.py:172: AssertionError
_________________________________________________________________ test_song_undo_redo __________________________________________________________________

client = <AbletonOSC.client.client.AbletonOSCClient object at 0x105ac3710>

    def test_song_undo_redo(client):
>       assert client.query("/live/song/get/num_scenes") == (8,)
E       assert (2,) == (8,)
E         
E         At index 0 diff: 2 != 8
E         Use -v to get more diff

tests/test_song.py:186: AssertionError
___________________________________________________________________ test_track_clips ___________________________________________________________________

client = <AbletonOSC.client.client.AbletonOSCClient object at 0x105b722d0>

    def test_track_clips(client):
        track_id = 0
        client.send_message("/live/clip_slot/create_clip", (track_id, 0, 4))
        client.send_message("/live/clip_slot/create_clip", (track_id, 1, 2))
        client.send_message("/live/clip/set/name", (track_id, 0, "Alpha"))
        client.send_message("/live/clip/set/name", (track_id, 1, "Beta"))

        wait_one_tick()
>       assert client.query("/live/track/get/clips/name", (track_id,)) == (track_id,
                                                                           "Alpha", "Beta", None, None,
                                                                           None, None, None, None)

tests/test_track.py:60: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <AbletonOSC.client.client.AbletonOSCClient object at 0x105b722d0>, address = '/live/track/get/clips/name', params = (0,), timeout = 0.15

    def query(self,
              address: str,
              params: tuple = (),
              timeout: float = TICK_DURATION):
        rv = None
        _event = threading.Event()

        def received_response(address, params):
            nonlocal rv
            nonlocal _event
            rv = params
            _event.set()

        self.set_handler(address, received_response)
        self.send_message(address, params)
        _event.wait(timeout)
        self.remove_handler(address)
        if not _event.is_set():
>           raise RuntimeError("No response received to query: %s" % address)
E           RuntimeError: No response received to query: /live/track/get/clips/name

client/client.py:153: RuntimeError
__________________________________________________________________ test_track_devices __________________________________________________________________

client = <AbletonOSC.client.client.AbletonOSCClient object at 0x105b722d0>

    def test_track_devices(client):
        track_id = 0
>       assert client.query("/live/track/get/num_devices", (track_id,)) == (track_id, 0,)

tests/test_track.py:76: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <AbletonOSC.client.client.AbletonOSCClient object at 0x105b722d0>, address = '/live/track/get/num_devices', params = (0,), timeout = 0.15

    def query(self,
              address: str,
              params: tuple = (),
              timeout: float = TICK_DURATION):
        rv = None
        _event = threading.Event()

        def received_response(address, params):
            nonlocal rv
            nonlocal _event
            rv = params
            _event.set()

        self.set_handler(address, received_response)
        self.send_message(address, params)
        _event.wait(timeout)
        self.remove_handler(address)
        if not _event.is_set():
>           raise RuntimeError("No response received to query: %s" % address)
E           RuntimeError: No response received to query: /live/track/get/num_devices

client/client.py:153: RuntimeError
_________________________________________________________ test_track_listen_playing_slot_index _________________________________________________________

client = <AbletonOSC.client.client.AbletonOSCClient object at 0x105b722d0>

    def test_track_listen_playing_slot_index(client):
        # 1/16th quantize
        client.send_message("/live/song/set/clip_trigger_quantization", (11,))
        for track_id, clip_id in itertools.product((0, 1), (0, 1)):
            client.send_message("/live/clip_slot/create_clip", (track_id, clip_id, 4))

        client.send_message("/live/track/start_listen/playing_slot_index", (0,))
>       assert client.await_message("/live/track/get/playing_slot_index", TICK_DURATION * 2) == (0, -1,)

tests/test_track.py:89: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <AbletonOSC.client.client.AbletonOSCClient object at 0x105b722d0>, address = '/live/track/get/playing_slot_index', timeout = 0.25

    def await_message(self,
                      address: str,
                      timeout: float = TICK_DURATION):
        """
        Awaits a reply from the given `address`, and optionally asserts that the function `fn`
        returns True when called with the returned OSC parameters.

        Args:
            address: OSC query (and reply) address
            fn: Optional assertion function
            timeout: Maximum number of seconds to wait for a successful reply

        Returns:
            True if the reply is received within the timeout period and the assertion succeeds,
            False otherwise

        """
        rv = None
        _event = threading.Event()

        def received_response(address, params):
            print("Received response: %s %s" % (address, str(params)))
            nonlocal rv
            nonlocal _event
            rv = params
            _event.set()

        self.set_handler(address, received_response)
        _event.wait(timeout)
        self.remove_handler(address)
        if not _event.is_set():
>           raise RuntimeError("No response received to query: %s" % address)
E           RuntimeError: No response received to query: /live/track/get/playing_slot_index

client/client.py:132: RuntimeError
__________________________________________________________________ test_selected_clip __________________________________________________________________

client = <AbletonOSC.client.client.AbletonOSCClient object at 0x105ac1580>

    def test_selected_clip(client):
        client.send_message("/live/view/set/selected_clip", (3, 4))
>       rv = client.query("/live/view/get/selected_clip")

tests/test_view.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <AbletonOSC.client.client.AbletonOSCClient object at 0x105ac1580>, address = '/live/view/get/selected_clip', params = (), timeout = 0.15

    def query(self,
              address: str,
              params: tuple = (),
              timeout: float = TICK_DURATION):
        rv = None
        _event = threading.Event()

        def received_response(address, params):
            nonlocal rv
            nonlocal _event
            rv = params
            _event.set()

        self.set_handler(address, received_response)
        self.send_message(address, params)
        _event.wait(timeout)
        self.remove_handler(address)
        if not _event.is_set():
>           raise RuntimeError("No response received to query: %s" % address)
E           RuntimeError: No response received to query: /live/view/get/selected_clip

client/client.py:153: RuntimeError
=============================================================== short test summary info ================================================================
FAILED tests/test_application.py::test_application_get_version - assert (2 == 2 and 12 == 11)
FAILED tests/test_clip.py::test_clip_property_name - RuntimeError: No response received to query: /live/clip/get/name
FAILED tests/test_clip.py::test_clip_property_color - RuntimeError: No response received to query: /live/clip/get/color
FAILED tests/test_clip.py::test_clip_property_gain - RuntimeError: No response received to query: /live/clip/get/gain
FAILED tests/test_clip.py::test_clip_property_pitch_coarse - RuntimeError: No response received to query: /live/clip/get/pitch_coarse
FAILED tests/test_clip.py::test_clip_property_pitch_fine - RuntimeError: No response received to query: /live/clip/get/pitch_fine
FAILED tests/test_clip.py::test_clip_add_remove_notes - RuntimeError: No response received to query: /live/clip/get/notes
FAILED tests/test_clip.py::test_clip_add_many_notes - RuntimeError: No response received to query: /live/clip/get/notes
FAILED tests/test_clip.py::test_clip_playing_position_listen - RuntimeError: No response received to query: /live/clip/get/playing_position
FAILED tests/test_clip.py::test_clip_listen_lifecycle - RuntimeError: No response received to query: /live/clip/get/name
FAILED tests/test_clip_slot.py::test_clip_slot_has_clip - RuntimeError: No response received to query: /live/clip_slot/get/has_clip
FAILED tests/test_clip_slot.py::test_clip_slot_duplicate - RuntimeError: No response received to query: /live/clip/get/notes
FAILED tests/test_clip_slot.py::test_clip_slot_property_listen - RuntimeError: No response received to query: /live/clip_slot/get/has_clip
FAILED tests/test_song.py::test_song_stop_all_clips - RuntimeError: No response received to query: /live/clip/get/is_playing
FAILED tests/test_song.py::test_song_tracks - assert (85,) == (4,)
FAILED tests/test_song.py::test_song_scenes - assert (2,) == (8,)
FAILED tests/test_song.py::test_song_duplicate_scene - assert (2,) == (8,)
FAILED tests/test_song.py::test_song_undo_redo - assert (2,) == (8,)
FAILED tests/test_track.py::test_track_clips - RuntimeError: No response received to query: /live/track/get/clips/name
FAILED tests/test_track.py::test_track_devices - RuntimeError: No response received to query: /live/track/get/num_devices
FAILED tests/test_track.py::test_track_listen_playing_slot_index - RuntimeError: No response received to query: /live/track/get/playing_slot_index
FAILED tests/test_view.py::test_selected_clip - RuntimeError: No response received to query: /live/view/get/selected_clip
============================================================ 22 failed, 32 passed in 19.50s ============================================================

i hope this helps @ideoforms

ideoforms commented 1 month ago

Thanks @esaruoho, those errors just indicate that AbletonOSC can't be reached.

If you run sudo lsof -nP -i4UDP | grep 11000, that will tell you if anything else is listening on that port (the process name being in the first column), which would definitely produce the symptoms you've described.

I've also added an error message to the latest commit of AbletonOSC that will indicate whether it encounters a problem binding to the network port.

esaruoho commented 1 month ago

thanks for adding the error message!

upon checking, i had both running:

$ sudo lsof -nP -i4UDP | grep 11000
Password:
Live      16844       esaruoho  179u  IPv4 0x634db4a0a3942f99      0t0  UDP *:11000
TouchOSC  25783       esaruoho    7u  IPv6 0x6d2de584fb3ca3e9      0t0  UDP *:11000

so i closed TouchOSC and only ran Live, and i still got 22 failed and 32 passed.

here's a video of various trials of it. https://www.loom.com/share/910732a274cd470dacfe82df82f74977?sid=458d2b3a-d67d-4915-bb9c-a890562697df

what i noticed, is, that if i have a clip in the session view, then only 18 tests fail - otherwise 22 tests fail.

also, i am wondering about this

FAILED tests/test_application.py::test_application_get_version - assert (2 == 2 and 12 == 11)

is the test trying to see that my version 12 is not version11 and thus fails?

i'm wondering if the test clip stuff fails because the clip is not created first, or..?

is there a specific ableton live project i should load before starting pytests?

@ideoforms

ideoforms commented 1 month ago

Listening network sockets can sometimes hang around after their process has died. Try restarting your computer, checking that nothing is listening on port 11000, and starting Live again.

Info on preconditions for running unit tests is here.

I've just updated that version test to pass for Live 11 and 12, and all tests are now passing on my end.

ideoforms commented 1 month ago

Tested and confirmed, closing issue