Interpreting code affects MIDI timing

If i have running patterns sending MIDI and execute some code at the same time, the timing of the MIDI events can sometimes be radically affected. In my specific case it’s a preProcessor messing with my live coding syntax that causes the glitches. This illustrates the issue:

MIDIClient.init;
m = MIDIOut.newByName("IAC-drivrutin", "Buss 1");
(
p = Pbind(
	\type, \midi,
	\midiout, m,
	\dur, 0.0625
).play;
)

(
10000.do{
	var str;
	str = "this causes no disturbance in the MIDI timing ";
	str = str.replace("no", "a significant");
	str = str.replace("MIDI timing", "force");
	str.post; // commenting out the post reduces the issue to a minimum, but it's still there
};
)
p.stop;

My first thought was to defer things but that doesn’t seem to help. Is it possible to solve this for safer MIDI timing? I.e something like Overdrive in Max that prioritize MIDI timing over user interaction.

i run in to this problem all the time, and afaik there is no elegant solution – sclang has a kind of multi-threading with the `fork but all of these processes run on the same clock and there is no way to prioritise them. (happy to be corrected on this!)

the workaround is to fork your “less important” work and intersperse it with tiny waits;

this allows the more important work to get more attention.


(

fork{    

    10000.do{

        var str;

        str = "this causes no disturbance in the MIDI timing ";

        str = str.replace("no", "a significant");

        str = str.replace("MIDI timing", "force");

        str.post;

        0.01.wait;

    }

}

)

you can experiment with the wait amounts

cheers,

eddi

SuperCollider has a lot of mechanisms to enable threading and parallel sort of behavior, but at it’s core it’s single threaded: if you do something 10000 times, the language will finish all 10000 loop iterations before it does anything else, including sending MIDI messages.

This is ALMOST never a real problem: there’s very little, apart from loading files or serious number crunching operations - that will cause sclang to hang long enough to disrupt musical scheduling. So, if you’re not explicitly doing something you know will take a long time, it might be a bug.

If you ARE doing one of those long-running things (e.g. big number crunching), you can prevent SuperCollider from getting behind on timing-sensitive things by running that operation in a Routine / Task / fork, and yielding periodically:

fork {
     10000.do {
         |i|
         if ((i % 100) == 0) {
            0.0001.yield; // every 100 loops, yield to allow sclang to process other pending work
         };
         crunchSomeNumbers.value();
     } 
};

Ok, thanks. I’ll experiment with this. There is no heavy crunching going on in my preProcessor as far as i understand it. Just some regexp and string searching/manipulations.

Hmm… I can’t figure out a way to fork things inside the preProcessor with my current structure. The pp must return a string which is valid sc syntax. This is not happening if i fork it. My pp works like this (i removed all the string processing for clarity):

(
thisProcess.interpreter.preProcessor = {|codeBlock|
	fork{
		codeBlock.split($\n).collect{|line|
			line.postln;
		}.join;
	};
};
)

// just some stupid but valid sc syntax processed through the preProcessor
(
s.waitForBoot{
	e = Event();
	e.play;
};
)

Now, if i fork it, it won’t interpret the code because it’s not a string but a Routine:

(
thisProcess.interpreter.preProcessor = {|codeBlock|
	fork{
		codeBlock.split($\n).collect{|line|
			line.postln;
		}.join;
	};
};
)

// some valid sc syntax processed through the preProcessor
(
s.waitForBoot{
	e = Event();
	e.play;
};
)

Any ideas? I’m thinking that instead of creating a sub-thread using a fork i need to access the mainThread somehow and be able to set those yields in there. I don’t know how to do that though. If it’s even possible?

A possible workaround would be to return "" from the preprocessor, and then .interpret the result of the string processing at the end of the routine.

I have a large preprocessor, but I never tried high-density MIDI with it.

Out of curiosity, would you mind benchmarking your preprocessor?

thisProcess.interpreter.preProcessor = { |string|
    var result;
    bench {
        result = ... string processing stuff...
    };
    result
};

hjh

Ah, that works of course!
Benchmarking actually revealed what seems to be the problem. I get varying results between 0.00016714000230422 seconds up to as much as 0.43314695799927 seconds. My basic syntax structure is

receiver method args

and in the preProcessor these are handled one by one stored in local variables with the same name. The last thing that happens in the pp is this line:

MyPreProcessor.formatString(receiver, method, args);

This class method does the final touch-up on the string and makes sure it’s valid sc syntax.
However, it’s also possble to write:

receiver method args
method args
method args
method args
...

In this case, when a line doesn’t begin with a valid receiver, i do:

MyPreProcessor.findReceiver;

This method uses Document.current.string and Document.current.selectionStart and a while() to search backwards for the nearest receiver. This seems to be really slow because it is indeed the lines that lacks receivers that cause high benchmark times. And lines far from a receiver also takes longer to perform obviously.

My intention has actually been to investigate other approaches to this. I don’t like using Document since it’s editor specific. After seeing the demo of your system on YouTube, James, i was thinking that the best approach actually might be to write a dedicated GUI with a text editor.

I can imagine that repeatedly pattern matching a string could be slow. There’s probably a way to optimize it. E.g., if a receiver can only follow a newline, then scan backward for newlines and do the regexp check for receiver only just after those. Scanning single characters should be very fast. (It isn’t in my preprocessor, but I do have a part of my live coding editor that scans for the beginning of a statement. It does exactly that and it’s always performed well.)

hjh

I figured out a much better, much faster way to do this now. Instead of the slow iterative search i implemented a globalKeyUpAction that uses regexp to find all valid receivers in Document.current.string and store them in an array. It updates whenever i type something. Now, the only thing my *findReceiver-method needs to do is

array[array.indexOfGreaterThan(Document.current.selectionStart) - 1];

Sort of at least. This seems to work pretty good so far and benchmarking values are in the regions of 0.000x seconds.

Thanks to you all for kicking me in the right direction!