RomeoDespres / reapy

A pythonic wrapper for REAPER's ReaScript Python API
MIT License
110 stars 25 forks source link

Error when moving tracks to a new subproject #129

Open betov75 opened 2 years ago

betov75 commented 2 years ago

Hi!

I'm having some problems when selecting tracks in Reaper and sending them to a new subproject. Reaper hangs until I shut down my python program. It works fine all the time, except in that case.

Here's the error message in command prompt:

--- COMMAND PROMPT --- File "C:\Users\Patrick\AppData\Local\Programs\Python\Python39\lib\site-packages\reapy\to machines.get_selected_client().request("HOLD") machines.get_selected_client().request("HOLD") File "C:\Users\Patrick\AppData\Local\Programs\Python\Python39\lib\site-packages\reapy\to File "C:\Users\Patrick\AppData\Local\Programs\Python\Python39\lib\site-packages\reapy\to result = self._get_result() result = self._get_result() File "C:\Users\Patrick\AppData\Local\Programs\Python\Python39\lib\site-packages\reapy\to

File "C:\Users\Patrick\AppData\Local\Programs\Python\Python39\lib\site-packages\reapy\to

return json.loads(s) s = self.recv(timeout=None).decode() File "C:\Users\Patrick\AppData\Local\Programs\Python\Python39\lib\site-packages\reapy\to File "C:\Users\Patrick\AppData\Local\Programs\Python\Python39\lib\site-packages\reapy\to return json.loads(s, object_hook=object_hook) return self._socket.recv(length, socket.MSG_WAITALL) File "C:\Users\Patrick\AppData\Local\Programs\Python\Python39\lib\json__init__.py", lin moryError return cls(**kw).decode(s) File "C:\Users\Patrick\AppData\Local\Programs\Python\Python39\lib\json\decoder.py", line raise JSONDecodeError("Extra data", s, end) on.decoder.JSONDecodeError: Extra data: line 1 column 10 (char 9)

... and what's appearing in Reaper's console:

--- REAPER --- Deferred script execution error

Traceback (most recent call last): File "defer", line 1, in File "C:\Users\Patrick\AppData\Local\Programs\Python\Python39\lib\site-packages\reapy\core\reaper\defer.py", line 48, in run callback(*args, **kwargs) File "activate_reapy_server.py", line 29, in run_main_loop results = SERVER.process_requests(requests) File "C:\Users\Patrick\AppData\Local\Programs\Python\Python39\lib\site-packages\reapy\tools\network\server.py", line 105, in process_requests result = self._process_request(request, address) File "C:\Users\Patrick\AppData\Local\Programs\Python\Python39\lib\site-packages\reapy\tools\network\server.py", line 64, in _process_request return self._hold_connection(address) File "C:\Users\Patrick\AppData\Local\Programs\Python\Python39\lib\site-packages\reapy\tools\network\server.py", line 54, in _hold_connection self._send_result(connection, result) File "C:\Users\Patrick\AppData\Local\Programs\Python\Python39\lib\site-packages\reapy\tools\network\server.py", line 79, in _send_result connection.send(result) File "C:\Users\Patrick\AppData\Local\Programs\Python\Python39\lib\site-packages\reapy\tools\network\socket.py", line 60, in send self._socket.sendall(length) OSError: [WinError 10038] An operation was attempted on something that is not a socket

Am I doing something wrong?

Thank you for your help on that matter.

Levitanus commented 2 years ago

Without the code (it can be formatted by ```Python [your code]```) it is hard to tell what is wrong.

But I would suggest a couple of things:

betov75 commented 2 years ago

Hi Timofey,

A sincere thank you for your help. Very useful. It helped me understand some aspects.

I've been testing my code with an extremely simplified version of my code. And it still crashed. But I understand everything better.

I will test some ideas and come back here to comment. But what I think is happening:

My code is a control surface multi purpose tool for the blind.

1: I use OSC to detect track's change. 2: When tracks are sent to a subfolder, strangely, Reaper sends track's change info via OSC before the API seems to have identified the new Project() object. 3: At times, when OSC is too much in advance, it sends info to my script then, my script asks for a new Project().track object. 4: So, at this point, when I try to get Project(), it looks like it doesn't exist yet. So I add a time.sleep() of 0.5 to avoid Reapy to ask before the API is ready.

That delay is caused by Reaper being in render mode 'maybe' before updating the API information.

I'm sure it can be coded a better way. I will clean everything and send a simplified code that replicates the bug. Maybe I should use Reapy to identify track change but I'm afraid the loop inside Reaper while slow down the DAW.

(English is not my native language...)

No need to keep the issue open but I would love to be able to publish updates in that thread. I think it's not a problem with Reapy but a mix of concepts that aren't in sync.

Have a great day!

Le mer. 13 avr. 2022 à 13:19, Timofey Kazantsev @.***> a écrit :

Without the code (it can be formatted by Python [your code] ) it is hard to tell what is wrong.

But I would suggest a couple of things:

  • You somehow remember tracks and "listen" them by the corresponding objects. When tracks are moved (removed) from project — your reapy.Track() objects starts to be linked with something else (anything else). So result unpredictable.
  • If You move tracks by the script — there is some mistake in API usage, it is better to show the code produces the mistake.

— Reply to this email directly, view it on GitHub https://github.com/RomeoDespres/reapy/issues/129#issuecomment-1098298210, or unsubscribe https://github.com/notifications/unsubscribe-auth/ACZ7PJ6PWSFU7BVNTFFQWVDVE36ZLANCNFSM5TISNTTA . You are receiving this because you authored the thread.Message ID: @.***>

Levitanus commented 2 years ago

Well, I still think that a brief look at the code will be more useful.

If the issue is connected to several project tabs — there can be conflicts in track ids, as You have to specify the project directly. For example:

import reapy as rpr

main_pr = rpr.Project('main')
sub_pr = rpr.Project('sub_vocals')

If everything happens inside one project — I cannot see any reasons to the behavior You've described...

Actually, I haven't used reapy as script, being triggered by OSC or MIDI, but in case of «one-shot» execution there should not be any problems with ids, which seems to be Your problem.

betov75 commented 2 years ago

Thanks a lot,

Everything works fine within a single project tab. When using subprojects, tabs are automatically used and that's where it gets complicated. But withing a single tab, no problem.

You're absolutely right, showing code is the only real way to know. Being blind, it's hard to strip down massive amount of lines but here's the simplest way to reproduce the problem out of my 4500 lines of code. I'm bad with github so I hope it's displaying properly. The comments are in relation with my problem.

import reapy from reapy import reascript_api as RPR

If I initialize project here, it's not working at all with project tabs but works fine with a single project tab.

project = reapy.Project()

import socket hostname = socket.gethostname() localip = socket.gethostbyname(hostname) import re import time from threading import Thread from pythonosc import udp_client from pythonosc import dispatcher from pythonosc import osc_server

class Track: """ Manage selected track data and behaviour """

def OSC(self,*args):

    svp = re.search(r'/track/(.+)', args[0])
    if svp:

        # Detect track's change via OSC
        if svp.group(1) == 'select' and args[1] == 0:
            getReapyData()
            client.send_message('/device/track/follows/last_touched',1.0)

Functions

def getReapyData():

with reapy.inside_reaper():

    # If I initialize project here, it sometimes work with project tabs but it's very unstable. Works with a single project tab.
    project = reapy.Project()

    track = project.selected_tracks[0] if len(reapy.Project().selected_tracks) > 0 else project.master_track
    print(track.name)

Startup

if name == "main":

Track = Track()

dispatcher = dispatcher.Dispatcher()
dispatcher.map("/track*",Track.OSC)

# OSC
server = osc_server.ThreadingOSCUDPServer(('127.0.0.1',9000), dispatcher)
client = udp_client.SimpleUDPClient(localip,8000)
Thread(target=server.serve_forever).start()

# Ask Reaper to send fresh OSC data
client.send_message('/action',41743)
Levitanus commented 2 years ago

Well. I ask You again to put Your code inside ``` symbols :)

I feel no powers now to try to reproduce Your bug live. But I feel the problem in the line:

track = project.selected_tracks[0] if len(reapy.Project().selected_tracks) > 0 else project.master_track
print(track.name)

If You do really nothing within OSC data — this implementation looks strange. And, again, I repeat the previous thought:

import reapy

project = reapy.Project(reapy.Project().id)

This way, your project object would be strongly connected to the particular project tab. In the most cases it is not necessary, but I feel it important here.

I'll try to run Your piece tomorrow. I feel it should be executed via OSC. But in the particular case of tracking selected tracks, I feel the daemon (e.g. script with defer()) would be better and more lightweight.

Levitanus commented 2 years ago

OK, I've read the whole topic. And now I'm much more interesting in the way You run the script. If You run it from the outside — it looks stranger: why do You use OSC, and what is the goal of your script?

May You provide technical task of what You're implementing? Maybe I can suggest a simpler solution.