I don’t want to get too specific on exactly how I am using this, but it provides automation data to and from the host for controlling how the audio is processed. So, it is an internal value used by the processBlock() that is automatable! The whole point is that it is calculated automatically, and therefore there is no physical control associated with it.
My code inside the processBlock() is very tight. So, maybe it has not been an issue on other DAWs because of the way they handle the processing and automation. But, I know Logic does certain things differently, and so I ask the question as to whether this is just unique to Logic?
I wouldn’t know if this is specific to Logic, but I have a suggestion:
If the value is calculated and used internally, then why do you need to tell the host and let these internal calculations be recorded etc.? Why not keep it all internal? If you have one control-value that can be automated by the user, and your algorithm is deterministic, then the same internal values would be generated, so there is really no need for the host to even know about these internal values.
If my understanding is correct, you can simply remove a chunk of code and not worry about it anymore. It would also fix the problem. The fastest code is the one that doesn’t need to be executed.
You are misunderstanding how it is used. The value is usually not exposed in other plugins. It is a unique feature of my plugins that this value is exposed and automatable! So, the purpose here is to give the user access to this value, when that is normally not an option. In other words, the user can record the automation of this value, and then choose to edit the automation to tailor the value to best suit the track, or simply view what the plugin is doing.
This is not an experiment. It works. And, I have been shipping plugins with this feature for almost a year.
My post was prompted because a Logic user reported artifacts when using the automation recording/playback feature. Since it has not been reported by any other users, and I know from my own testing that it works as expected in other DAWs, I am simply enquiring as to whether this is just a Logic thing?
Just because you haven’t heard of any problems doesn’t mean it’s safe. You’ve no idea what the host will do when you invoke that call, but it’s very likely that some of them will allocate memory or post a message, which will occasionally cause problems depending on what else the host is doing at a particular moment.
The only safe way to send events from your process method is to have your own lock-free fifo to post events which you later pick up in another thread (or on a timer) and do the work there.
I don’t know about any particular quirks in Logic, but you need to make sure that anything you do in AudioProcessor::setValue() or AudioProcessorListener::audioProcessorXxxxx is thread-safe, fast and non-blocking as these are called as a result of the methods you mention.
It may just be that Logic is less forgiving of something going on in there…
Yes, thank you. I was afraid that I have just been “lucky” until now. I always strive to make things better, however, so this is a great opportunity to refactor the code into something more robust!
The lock-free fifo idea sounds like it may very well be the way to go. I’ll be looking at that as my new approach.
In the mean time, I’ve been analyzing my code, out of curiosity, to try to determine why it works as well as it does? I mean, as I’ve mentioned three or four times, it works great in Pro Tools. You’d think it would fail there, if it was going to fail anywhere. And, it also works great in Tacktion/Waveform (you may have some insight here).
I make a point of making all my code in the processBlock() as tight as possible. And, for example, I bypass code that is not changing, which includes the setValueNotifyingHost() calls. In fact, in a steady signal, dozens, if not hundreds, of blocks can go by without setValueNotifyingHost() being called. So maybe this has something to do with my “getting away with it”.
So, I’ve got work to do. And, the lock-free fifo idea is not too different from the way I feed data to my meters (although, they are on a much more relaxed schedule!). We are always learning. I think that is one of the reasons we code!
Maybe it is even only a problem in a combination, like having a certain amount of plugins, you can’t know… still a good idea to improve it.
It doesn’t sound, like it’s ever going to be sample accurate, so why not simply use callAsync with the setValueNotifyingHost()? Sounds easier to implement…
@t0m maybe while you’re working on the audioprocessor stuff, we should throw in some assertions to make sure people don’t call unsafe stuff from the audio thread? We could have a thread-local flag set during the process method, and have assertions that check for it, kind of like we do in some places for the message thread.
I don’t think this will work reliably, because there are hosts that call initialization methods from realtime threads before calling any process callback with them.
And therefore, all the methods called in those initialization phases will have a wrong indication because said hypotetical isRealtimeThread() method will return false at that point, but it will start returning true later on after the first processing callback has been called.
To be fair, on the other hand this may be a tolerated behavior, if we accept that “blocking” behavior can happen on a soon-to-be realtime thread, as long as it has not yet actually entered the processing loop
Playing the “devil’s advocate” now: what happens if the DAW decides to stop using a certain thread of its pool for realtime jobs, and assigns it to background stuff that still call into the plug-in?
That thread would still be marked as realtime while not really being anymore.
Maybe while designing this, allow for another method to query whether the current DAW is to be trusted or not. I suppose most DAWs will be implemented in a sensible way and the answer will be “yes”, but you may never know.
I think you misunderstood what I meant: I was suggesting having a thread-local variable “isAudioThread”, which is set to true at the start of process() and set to false at the end. That way it doesn’t matter if the thread changes.