ideoforms / AbletonOSC

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

Request: Send has_clip response on create_clip and delete_clip messages #60

Closed steve-meyer closed 11 months ago

steve-meyer commented 1 year ago

It appears that my client can send messages faster than Ableton can process them and/or they do not queue up. For example, in quick succession I send a create clip message immediately followed by an add notes message. However, I am getting this response back:

/live/error Error handling OSC message: 'NoneType' object has no attribute 'add_new_notes'

I am confident my add new notes message is OK because I can subsequently resend the add notes message and the notes are created in the new clip and no error message comes back.

I can wait until the clip is created, but I don't know how long to wait. I would like to be notified that the clip was created. Therefore, could same response that is used for the message /live/clip_slot/get/has_clip also be sent after the create and delete clips actions have been completed?

Thanks for considering.

-Steve

ideoforms commented 11 months ago

Hi @steve-meyer, I've not been able to reproduce this here. I created a simple test script in Python that creates a clip and then add notes to it immediately:

    client = AbletonOSCClient(args.hostname, args.port)
    client.send_message("/live/clip_slot/create_clip", (0, 0, 4.0))
    client.send_message("/live/clip/add/notes", (0, 0, 60, 0.0, 0.5, 64, 0))
    client.send_message("/live/clip/add/notes", (0, 0, 62, 1.0, 0.5, 64, 0))

It runs as expected. What client are you using? I'm wondering whether the events are being queued in the wrong order, which could result in the behaviour you mention, but nothing on the server-side would cause this.

No waiting should be required, as long as the messages are sequenced correctly.

steve-meyer commented 11 months ago

I can still replicate this, though it behaves inconsistently. Sometimes it works and sometimes it does not.

The quick test code in JavaScript:

const OscEmitter  = require("osc-emitter");
const OscReceiver = require("osc-receiver");

const processMessage = (...response) => console.log(response[0], response.slice(1).join(", "));

// OSC transmitter
const emitter  = new OscEmitter();
emitter.add("localhost", 11000);

// OSC receiver
const receiver = new OscReceiver();
receiver.bind(11001, "localhost");
receiver.on("message", processMessage);

// Create a clip
let params = [{type: "integer", value: 0}, {type: "integer", value: 0}, {type: "double", value: 16.0}];
emitter.emit("/live/clip_slot/create_clip", ...params);

// Add a note to the clip
let firstTrackFirstClip = [{type: "integer", value: 0}, {type: "integer", value: 0}];

let noteParams = [
  {type: "integer", value: 60},
  {type: "double",  value: 0.0},
  {type: "double",  value: 0.25},
  {type: "float",   value: 64},
  {type: "boolean", value: false}
];

emitter.emit("/live/clip/add/notes", ...firstTrackFirstClip, ...noteParams);

If you want to try to run this, see the terminal log below. You will first need node installed.

Note that I ran the code above (via the script ableton_osc_test.js) 6 times and first 5 succeeded. Between each time running $ node ableton_osc_test.js I have to Control+C to stop the script because in this simple version, the JS OSC libraries hold the socket open. Also, between each time running the script I am deleting the Live Clip created for each successful run.

Maybe this is something local to my setup (older Intel iMac). But given that I get some successes before failure/error, maybe you can replicate it by simply running your Python script repeatedly?

~/Documents/programming/javascript/ableton-osc-test $ npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help init` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (ableton-osc-test) 
version: (1.0.0) 
description: 
entry point: (index.js) 
test command: 
git repository: 
keywords: 
author: 
license: (ISC) 
About to write to ~/Documents/programming/javascript/ableton-osc-test/package.json:

{
  "name": "ableton-osc-test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

Is this OK? (yes) 
~/Documents/programming/javascript/ableton-osc-test $ npm install osc-emitter 

added 5 packages, and audited 6 packages in 2s

found 0 vulnerabilities
~/Documents/programming/javascript/ableton-osc-test $ npm install osc-receiver 

added 2 packages, and audited 8 packages in 689ms

found 0 vulnerabilities
~/Documents/programming/javascript/ableton-osc-test $ code . 
~/Documents/programming/javascript/ableton-osc-test $ node ableton_osc_test.js 
^C
~/Documents/programming/javascript/ableton-osc-test $ node ableton_osc_test.js
^C
~/Documents/programming/javascript/ableton-osc-test $ node ableton_osc_test.js
^C
~/Documents/programming/javascript/ableton-osc-test $ node ableton_osc_test.js
^C
~/Documents/programming/javascript/ableton-osc-test $ node ableton_osc_test.js
^C
~/Documents/programming/javascript/ableton-osc-test $ node ableton_osc_test.js
/live/error Error handling OSC message: 'NoneType' object has no attribute 'add_new_notes'
^C
ideoforms commented 11 months ago

That's so strange. I've just tried running the Python script repeatedly (100x) and I couldn't replicate:

    client.send_message("/live/clip_slot/create_clip", (0, 0, 4))
    client.send_message("/live/clip/add/notes", (0, 0, 60, 0.0, 0.5, 64, False))
    rv = client.query("/live/clip/get/notes", (0, 0))
    print(rv)
    client.send_message("/live/clip_slot/delete_clip", (0, 0))

However, I've just run your Node script, and I do indeed see this happen every so often. I ran a tcpdump to look at the network traffic, and it looks like, in some instances, the messages are sent out of order:

19:11:46.276129 IP localhost.63698 > localhost.irisa: UDP, length 68
E..`pZ..@.............*..L._/live/clip/add/notes....,iiiddfF...............<........?.......B...
19:11:46.276146 IP localhost.63698 > localhost.irisa: UDP, length 52
E..P.>..@.............*..<.O/live/clip_slot/create_clip.,iid............@0......
19:11:46.330650 IP localhost.irisa > localhost.metasys: UDP, length 96
E..|.   ..@...........*.*..h.{/live/error.,s..Error handling OSC message: 'NoneType' object has no attribute 'add_new_notes'..

(See the /live/clip/add/notes appearing before /live/clip_slot/create_clip) This is definitely the underlying issue. Not sure why the message order isn't preserved by osc-emitter, though...

steve-meyer commented 11 months ago

Thanks for confirming via the network logs that the messages are arriving in the wrong order. I can't see anything obvious on the Node website's docs for Socket or EventEmitter that would cause this.

Since this is entirely on my client side, I'll mark this as closed. Apologies for the wild goose chase!

ideoforms commented 11 months ago

@steve-meyer Yes, quite a mystery how they lose their order!

I think a good solution to this would be to use an OSC bundle, which can be used to package together multiple OSC messages in sequence, exactly for cases like this where the order matters. AbletonOSC is able to parse incoming bundles. It does look like you'd need an alternative OSC client for Node, but it would definitely solve the problem. Let me know if you have any more success.

A simpler but less elegant/robust solution would be to pause one for the duration of one Live tick, which is 100ms.