ableton-js
ableton-js copied to clipboard
session_record listener timeout
When I have the following script running:
ableton.on("connect", function(e) {
console.log("Connect", e);
ableton.song.addListener("session_record", function(s) {
session_record_did_change(s);
})
})
Sometimes the script crashes after changing the project or re-opening Ableton:
/node_modules/ableton-js/index.js:328
rej(new TimeoutError(`The command ${cls}.${command.name}(${arg}) timed out after ${timeout} ms.`, payload));
^
TimeoutError: The command song.add_listener({"prop":"session_record","eventId":"7f287dc5-85fb-4ddb-8afc-7bb6dc8383e7"}) timed out after 2000 ms.
at Timeout._onTimeout (node_modules/ableton-js/index.js:328:21)
at listOnTimeout (node:internal/timers:573:17)
at process.processTimers (node:internal/timers:514:7) {
payload: {
uuid: 'f012d8f5-f1f3-4462-a23b-45caa7065786',
ns: 'song',
nsid: undefined,
name: 'add_listener',
args: {
prop: 'session_record',
nsid: undefined,
eventId: '7f287dc5-85fb-4ddb-8afc-7bb6dc8383e7'
}
}
}
It seems to be very random and is hard to re-produce.
Is there anything I do wrong on my side?
Hey @eliaskg,
This happens sometimes, unfortunately. I haven't quite been able to figure out what's causing this, but if you'd like to make extra sure that the connection is stable, you could use something like this:
/**
* Calls the callback function after the timeout.
*
* If another function callback is still running,
* waits until it's finished before calling it again.
*/
function debouncePromise(func: () => Promise<unknown>, trailing = true) {
let currentFunction: Promise<unknown> | null = null;
let callCount = 0;
return async function (this: any) {
const context = this;
callCount++;
if (currentFunction) {
return currentFunction;
}
const later = async function () {
if (callCount > 0) {
callCount = 0;
currentFunction = func.apply(context, []);
await currentFunction.catch(() => {});
currentFunction = null;
if (trailing) {
later();
}
}
};
await later();
};
}
ableton.on(
"connect",
debouncePromise(async () => {
console.log("Got a connect event from Live, waiting for project file to finish loading...");
// Try to send a ping to Ableton to check if it's available
for (let i = 0; ableton.isConnected(); i++) {
try {
console.log("Checking if Live is actually reachable...", { try: i });
for (let i = 0; i < 5; i++) {
console.log("Pinging Live...", { try: i });
await ableton.internal.get("ping");
}
log.info("Ableton is connected");
break;
} catch (e) {
console.warn("Couldn't reach Live", { error: String(e) });
await new Promise(res => setTimeout(res, 500));
}
}
// Your actual setup code goes here
}),
);
It's a bit complex, but I hope this helps!
Thank you @leolabs, I will give this a try!
Tried out the code you provided. Worked great for a couple of days. Today when I opened Ableton while my script was running it crashed again with the following error output:
Got a connect event from Live, waiting for project file to finish loading... Checking if Live is actually reachable... { try: 0 } Pinging Live... { try: 0 } Couldn't reach Live { error: 'Error: The command internal.get_prop({"prop":"ping"}) timed out after 2000 ms.' } Disconnect heartbeat /Users/elias/Documents/Code/Scripts/ableton-js/node_modules/ableton-js/index.js:328 rej(new TimeoutError(
The command ${cls}.${command.name}(${arg}) timed out after ${timeout} ms., payload)); ^TimeoutError: The command song.add_listener({"prop":"session_record","eventId":"5b706c82-e659-4dce-83f9-e6e9cee1de3a"}) timed out after 2000 ms. at Timeout._onTimeout (/Users/elias/Documents/Code/Scripts/ableton-js/node_modules/ableton-js/index.js:328:21) at listOnTimeout (node:internal/timers:573:17) at process.processTimers (node:internal/timers:514:7) { payload: { uuid: '9757e123-534c-4f6b-8e1e-63e0e212e414', ns: 'song', nsid: undefined, name: 'add_listener', args: { prop: 'session_record', nsid: undefined, eventId: '5b706c82-e659-4dce-83f9-e6e9cee1de3a' } } }
Seems to happen super randomly, sometimes when opening Ableton, sometimes after opening a different project.
Hmm, that's strange! I wonder if this happens because Live disconnects while checking for a connection. The following might be able to fix this:
/**
* Calls the callback function after the timeout.
*
* If another function callback is still running,
* waits until it's finished before calling it again.
*/
function debouncePromise(func: () => Promise<unknown>, trailing = true) {
let currentFunction: Promise<unknown> | null = null;
let callCount = 0;
return async function (this: any) {
const context = this;
callCount++;
if (currentFunction) {
return currentFunction;
}
const later = async function () {
if (callCount > 0) {
callCount = 0;
currentFunction = func.apply(context, []);
await currentFunction.catch(() => {});
currentFunction = null;
if (trailing) {
later();
}
}
};
await later();
};
}
ableton.on(
"connect",
debouncePromise(async () => {
console.log("Got a connect event from Live, waiting for project file to finish loading...");
// Try to send a ping to Ableton to check if it's available
for (let i = 0; ableton.isConnected(); i++) {
try {
console.log("Checking if Live is actually reachable...", { try: i });
for (let i = 0; i < 5; i++) {
console.log("Pinging Live...", { try: i });
await ableton.internal.get("ping");
}
log.info("Ableton is connected");
break;
} catch (e) {
console.warn("Couldn't reach Live", { error: String(e) });
await new Promise(res => setTimeout(res, 500));
}
}
// If Ableton Live has disconnected during connection check, don't do anything
if (!ableton.isConnected()) {
return;
}
// Your actual setup code goes here
}),
);
Let me know if that works for you!