Cantabile 2026 Development Update

Hey Everyone!

I thought I’d post a quick update on what I’ve been working on. As I’ve mentioned elsewhere I’ve been working on an update to Cantabile’s real-time audio engine.

This has taken a lot longer than I originally anticipated - partly because I’ve been side tracked by some personal stuff, but more so because it’s a significant set of changes and I’ve switched approaches couple of times.

I’m also paying off some technical debt - some of the code I’m reworking is 15+ years old and has been patched a number times - those patches need to be made more permanent.

Since these changes are going to take quite a bit of testing, I’ve decided to take this opportunity to also clean up, fix, improve several other areas and then stabilize it all together.

After pulling everything apart, I’m now putting everything back together and things are slowly starting to work again. Audio and MIDI routes are working, input/output ports are working, plugins are working and media players are almost finished. I’m hoping to get media players and racks finished this week.

After that I’ll be doing more clean up and miscellaneous improvements followed by lots of testing and bug-fixing.

All that’s to say: I’ve been quiet, but busy (mostly). Stay tuned!

Brad


For the technically inclined, if you’re curious what these changes are:

  • Removed the Route Manager. The route manager was responsible for coordinating updates to routes and object whenever anything changed. It was a complicated sequence of 40+ steps across all racks and songs. This has been replaced with a cleaner, more focused asynchronous “invalidation model” where only objects that have actually changed need to be updated.

  • Previously the audio engine was synchronous, blocking and re-entrant. In the new approach, changes to the UI objects are synchronous but updates are pushed down the audio engine asynchronously. This should mean faster updates and cleaner code (not having to handle re-entrancy). It also fixes some obscure edge cases with bindings.

  • Removed all memory allocations from the audio thread. There used to be a lock-free heap used by the audio thread. I’m rearranging things so memory allocations are removed from the audio thread entirely. Also, switching to an amoritized, delayed free mechanism. Cleaner code, less chance for random stalls.

  • Improved UI thread/audio thread messaging including better support for collection management across threads, removal of blocking messages, everything async. Cleaner code, faster.

  • Replacing some inter-thread messaging with atomic (aka interlocked) operations.

  • Some reworking of the parallel execution thread pool, execution groups and execution nodes - should result in cleaner, simpler and leaner execution on each audio cycle.

  • Removing possibly problematic redundant code. There’s various pieces of code in the engine that are somewhat redundant but risky to remove. Now seems like a good time to remove them and deal with any consequences during the stabilization of all these other changes.

Plus, lots of other minor fixes, cleanup, etc…

One thing to note - these changes are all restricted to how the real-time object graph is constructed, updated and executed. The logic implemented by the objects themselves hasn’t changed. Similarly the actual layout of the graph hasn’t changed. This means any bugs (and there will be bugs) are almost certainly going to be order/timing related and not in the object’s themselves. (eg: this shouldn’t introduce bugs to the MIDI router, the audio mixer, the midi filters, plugin hosting etc…).

17 Likes

A couple of thoughts/suggestions once you have this work stabilised:

  1. moving from .NET 8 (which is end of support later this year) to .NET 10 (current LTS version) which may also give some further performance improvements;

  2. adding support for the new MIDI/MIDI 2.0 Services for Windows 11 (alongside the old APIs) once the new Windows 11 MIDI services SDK is released in the next few months.

The new MIDI stack provides improved and more deterministic MIDI performance and accessing it directly rather than through the legacy MIDI APIs would potentially further optimise this.

Obviously, given its scope, the MIDI 2.0 features is probably something that will need to evolve over time: maybe starting with enabling the higher-resolution control it supports so that plugins that can take advantage of this capability, with the more interesting topics like “how to make use of MIDI-CI” probably needing more consideration before implementation.

2 Likes

And the new midi stack will eventually provide Bluetooth Midi integration rather than requiring 3rd party. It’s about time!

2 Likes

And the Network MIDI stuff which is currently in the earlier stages of development as well.

2 Likes

Good progress this week. As hoped, media players and racks are now working with this new approach.

The main area left that’s just not working is some bindings and various events and lots of tuning and testing.

This is a non-trivial set of changes. (And still going and none done by AI).

image

10 Likes

Question, out of curiosity: is this going to effect load/latency?

Not primarily. These changes are more about how the real-time audio graph is constructed and less about how it’s processed.

That said I am making some minor improvements in processing too, but I expect the differences to be negligible and are more about clean up and maintainability.

Brad

2 Likes

Another week of good progress.

  • Everything related to the real-time graph update logic is now in place and seems to be working.
  • Bindings and event triggering is also now back in place and working.
  • Lots of other loose ends cleaned up.

Next week I’ll be doing some basic confidence testing and some more low-level refinements to the core engine.

image

7 Likes

Another week of good, but slower than hoped progress:

  • Rework the way animated mix levels (ramp up/down) works
  • Finished Replace Plugin to work with new engine
  • Cleaned up much of the logging code
  • Reworked state switching and the state unload event to only block when necessary
  • Reworked the way events, bindings and rack/song start/stop sequence works.
  • Tested and fixed binding points to MIDI ports on routable objects.

As mentioned previously… I’m taking this opportunity before a big stabilization phase to clean-up and rework areas that have long been neglected.

11 Likes

Hi @brad, sounds good - if you’re looking at logging could you maybe think about human-readable timestamps, something ISO 8601 perhaps? Thanks!

1 Like

What’s the use case for this? Usually with timings in the log you’re trying to work out how long something takes and milliseconds seems a more easily calculable value to me.

If you want real-world times, the log includes a start time at the top - just add the line timestamp to that.

Are there specific events you need real-world time for?

Brad

It’s a good challenge I guess. I’m from a background where logs all look the same - essentially RFC 5424, which specifies ISO 8601 with optional microsecond precision - any log that isn’t that just looks strange to me. But that’s not a reason why you should use that format.

My use cases are generally things like:

  • Something funny happened at the gig last night, of all the rows of data is there anything in the log to help on that date.
  • Did that event happen yesterday or today? A ms offset doesn’t help without tracing back
  • I’m optimising load times, and seeing relative time in human format is easier than ms differences

At worst I guess I could do the conversion with a script.

Hi just wanna say that I love Cantabile and I use it every day. I’m not on here very much because it’s intuitive and it just works. So thanks for all the hard work!

1 Like

You could.

I’m not against including some kind of standardized date format, but only if there’s a real need for it. Curious if anyone else would find this useful.

Thank you, glad you’re enjoying Cantabile :slight_smile:

More good progress this week mostly focused on tying up some loose ends, stabilization and adding support for global pre/post racks (see here).

image

5 Likes

Waiting with bated breath :smile: