ideoforms / AbletonOSC

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

Define a list of messages to fire when a set is loaded #35

Closed rimasinc closed 1 year ago

rimasinc commented 1 year ago

This is my first contact with AbletonOSC. Excellent initiative.

One question: Do some of the files allow you to add a list of actions to be triggered when Ableton is loaded?

Currently, when we reload or open a project in Ableton we get the message "AbletonOSC: Listening for OSC on port 11000".

It would be very interesting to be able to send, for example, messages like: /live/song/get/metronome /live/track/get/clips/name /live/track/get/color /live/device/get/name (among others)

I'm currently a Clyphx Pro user who has a folder called "user_actions". Inside it is possible to create py files to send OSC messages when live is started. For example:

Nome do arquivo: listener_track.py

from ClyphX_Pro.clyphx_pro.UserActionsBase import UserActionsBase

TOCA_MAX_TRACKS = 8

class listener_track(UserActionsBase):

    def create_actions(self):

        self.add_global_action('listener_track', self.listener_track)

    def listener_track(self, *a):
        add_func = self.song().add_tracks_listener(self.atualiza)

    def atualiza(self):
        amount_of_tracks = len(self.song().tracks)

        for tr_nums in range(0, TOCA_MAX_TRACKS):
            if (tr_nums < amount_of_tracks):

                tr_colore = hex(self.song().tracks[tr_nums].color).lstrip('0x')
                tr_named = self.song().tracks[tr_nums].name

            else:

                tr_colore = "rgba(109,181,253,0)"
                tr_named = " "

            self.canonical_parent.clyphx_pro_component.trigger_action_list('OSC str /track_name_%s "%s"' % (tr_nums, tr_named))
            self.canonical_parent.clyphx_pro_component.trigger_action_list('OSC str /color_address%s "%s"' % (tr_nums, tr_colore))

I'm not a python programmer (this code should even have sectors that need refactoring) but it works for me. That is, I can get the names and colors of each track (or an "empty" when they don't exist) as soon as I start my live set.

Yours sincerely!

Coupe70 commented 1 year ago

@rimasinc Not a direct answer to your question, but it is already possible to do it the other way round: Right after a Live set is loaded or Live is started (which equals to the default set being loaded) AbletonOSC sends a /live/reload - so this could be the trigger for you to request all the information you want from AbletonOSC.

rimasinc commented 1 year ago

Thanks for the answer! I went after what you suggested and I believe that you actually meant to refer to the /live/startup message that is originally contained in the application.py file.

In fact, whenever I restart live, I can see the following log in Open Stage Control (the application I'm using to connect my iPad's layout):

(DEBUG, OSC) In: { address: '/live/startup', args: [] } From: 127.0.0.1:11000

However, I don't see any flags in the AbletonOSC log (and I think that's ok)

Your answer was also helpful for me to look at the functionality of the /live/reload message. The documentation says

"Initiates a live reload of the AbletonOSC server code. Used in development only".

The /live/reload message is being useful for updating and checking the AbletonOSC code where I check if sending other messages (besides /live/startup) works when inserted in theapplication.py file.

Then I tried adding /live/song/start_listen/tempo in applications.py. In Open Stage Control I created a text widget to receive /live/song/get/tempo but however, nothing happens (I don't get the BPM value from Live). This message exchange also does not respond to changing the manual BPM (changing the BPM value with the mouse on the Ableton screen)

Back to Open Stage Control I experimented with creating a button that sends /live/song/start_listen/tempo. The BPM value still does not appear, however, when moving the mouse to change the BPM on the Live screen, the text widget receives the changes.

I will do more testing and come back. Until and thanks!

Coupe70 commented 1 year ago

I'm sorry, you are right of course, it's live/startup! Didn't use it for some time and when I had a quick look at the readme to answer your question, "reload" seemed to be the term that makes sense...

rimasinc commented 1 year ago

@ideoforms Congratulations on the work =D I just downloaded the updates made today. Do you have any suggestions for this topic? Thanks!

rimasinc commented 1 year ago

After a battery of tests, I returned.

I'm trying to figure out a way to receive messages when Live is opened based on the current AbletonOSC files.

In addition to the application.py file (which sends self.osc_server.send("/live/startup" every time Live is started) I discovered, for example, that in the song.py file it is also possible to send start messages (and they work in parts).

In the song.py file, I tried putting the following message on line 131: self.osc_server.send("/live/track/get/clips/name", [0])

With this message, when starting an empty live set or opening a live set from a previously saved project (with existing clips) the initial result is just the normal index of the widget I'm using in the Open Stage Control app, which returns the result 0 , 1, 2, 3 (I'm using a matrix widget to get the name of the first 4 clips on track 0)

To get the clip names, I need to create a GET CLIP NAMES button in my layout to send the message send("127.0.0.1:11000", "/live/track/get/clips/name", [0]). When I press this button, if the live set is empty, it will return (0, null, null, null, null)... If the set has previously saved clips, or if I record new clips, the names will appear correctly only after I press the GET CLIP NAMES button.

If after pressing GET CLIP NAMES I open a new blank set (or when opening another saved project containing clips) the result in the widget "updates" to 0, 1, 2, 3.

So the message sent through the song.py file works by changing the message content, but partially. The problem is that, when opening or restarting a live set, the message self.osc_server.send("/live/track/get/clips/name", [0]) does not return the clip names (you need to trigger a button with this message)

I tried on line 132 the same procedures for the message self.osc_server.send("/live/song/start_listener/tempo") this time using a text widget. The result is the same.

I'm very green in python, a toddler crawling (and I hope I'm not talking nonsense) but, I think an alternative would be a file, perhaps named osc_init.py containing the loops for the refresh state of elements that make sense for opening (or restarting) messages of a live set.

ideoforms commented 1 year ago

Hey @rimasinc, thanks for the feedback. If I understand correctly, you're looking to achieve a way to automatically send information to the client including multiple different properties of the set, each time the set is opened.

A simple and elegant way to do this would be to do the following:

I've just added some docs on get/track_data here. I have realised that, for OSC controllers, you might really be after a series of replies containing each property in return (/live/track/get/name 0, /live/track/get/name 1, ...)... but this will be a piece of work for the future.

Closing this issue as it's a "wontfix", but feel free to keep commenting.

rimasinc commented 1 year ago

I'm still at the very beginning of my studies, but for my purpose I created a new file in the abletonosc directory.

It automatically displays on my ipad the messages I want displayed as soon as Live is started (and when properties are changed):

opening.py

class OpenHandler(AbletonOSCHandler):

     def __init__(self, manager):
         super().__init__(manager)
         self.class_identifier = "opening"

     def init_api(self):
         self.song.add_tempo_listener(self.bpm_changed)
         self.song.add_tracks_listener(self.get_track_names)

  # Trigger BPM when a set is loaded
     def bpm_changed(self):
         self.last_bpm = self.song.tempo -1.0
         current_bpm = self.song.tempo
         if (current_bpm != self.last_bpm):
             self.osc_server.send("/live/song/get/tempo", [current_bpm])

     #Update track names when a set is loaded
     def get_track_names(self):
         amount_of_tracks = len(self.song.tracks) +1
         tr_nums_min, tr_nums_max = 0, len(self.song.tracks)
         if (tr_nums_max < amount_of_tracks):
             self.osc_server.send("/live/song/get/track_names", tuple(self.song.tracks[index].name for index in range(tr_nums_min, tr_nums_max)))