Re-engineering Cantabile's Bindings Framework

Not much progress yesterday because:

  1. I realized there was small problem with what I’d already done but it required reworking it with a new approach. Not the core interfaces but the way I was planning on implementing them. That’s sorted now.
  2. got a new laptop and needed to set it up.

Fun fact: you can’t clean install Microsoft Windows from USB on a Microsoft Surface Laptop - the keyboard and track pad don’t work and you need to use an external hub and devices. It was easier to install Windows on my Apple MacBook than on a Microsoft laptop.


OK, third time lucky.

Today I re-did implementation of binding points two more times. It’s important I get this right early because Cantabile has alot of binding points and I need to make sure it’s right before I convert everything over.

  • The first attempt made it too difficult/verbose/messy for the binding points to setup and remove event handlers (and what I realized yesterday was the event handlers weren’t getting removed - which would’ve leaked memory pretty badly)
  • The second attempt was much cleaner, but was creating tables of binding points for every instance of an object. eg: each plugin instance would have it’s own in-memory list of available binding points - which is silly when they’re the same on all instances.
  • Third attempt I think is pretty good.

The breakthrough was to use attributes and reflection. To explain a little more, there’s two parts to declaring a binding point:

  1. The meta data that’s required up front to build a list of binding points. This consists of three pieces of data:
    a. The id used for serialization (non-localized)
    b. The display name used for populating lists (localized)
    c. Flags indicating if the binding point is a source or target (or both)

  2. The other part is the actual binding point implementation - this is an object that gets created once the user selects to use a binding point.

A typical binding point now looks like this in code:

        [BindingPoint("gain", "Gain")]
        IBindingPoint Gain() => new MonitoredPluginBindingPoint(this)
            Kind = BindingPointKind.GainLevel,
            SetValue = (value) => plugin.Gain = (float)value,
            GetValue = () => plugin.Gain,


  • The id and display name are declared using the [BindingPoint] attribute
  • Each binding point function is a factory for the binding point implementation.
  • The source/target flags are determined by whether the function returns an IBindingPoint, ITargetBindingPoint or ISourceBindingPoint

Also, since I didn’t want to have to write a separate class for every binding point, there’s a default implementation that implements the interface by forwarding to callback function properties. This lets me inline most of the code for most binding points like shown above. In other words, I don’t need to write an explicit PluginGainBindingPoint class - instead I can just use a generic binding point class and hook up things up via callbacks.

And I think that’s as concise as I can get it. The code above is everything required for the plugin gain binding point. This approach might need some more tweaking, but I’m pretty confident it’s correct enough to proceed
Time to move on to implementing the actual bindings manager and binding classes.

(I may not post anything here for the next week or so as I’m travelling interstate to visit family)


Well we planned to but our flight got cancelled and replacement flight is two days later, so…

I spent a good part of yesterday and today working on:

  • New bindings manager class for managing the set of bindings and binding groups on a rack.
  • Integrating the new bindings manager with the rack class.
  • Started implementing the Binding class that manages the source, mapping and target objects.
  • Parts of the serialization code.
  • More tweaking of the way binding points are implemented.

It’s coming along nicely, but there’s still a long way to go. I’m trying to do this without breaking the build. I’m effectively building a new binding system that for a short period during development will run in parallel to the old system. The hope is that I can then move things over piece by piece and check each part works as I go - as opposed to breaking everything and having to fix everything before I can test anything.

Pressing on regardless…


you love living on the ragged edge of disaster, don’t you? :joy:


OK, so the week away turned into two weeks due to some personal stuff, but I’m back at it this week. Yesterday was just catching up on support emails and getting build 4051 out. Today I got back into this bindings work.

Currently I’m working on the various mapping objects, and so far have implemented:

  • Switch to Command - including the auto-repeat and inverted modes
  • Switch to Switch - including the inverted mode
  • Switch to Value - mapping a switch to an on/off target value
  • Value to Value - including source/target ranges, out of range modes and curve.
  • Value to Gain - same as value to value but performs a linear to decibel mapping for the target values
  • Gain to Value - same as value to value but performs a decibel to linear mapping to the source values

Mostly this work involves extracting, cleaning up and refactoring code from the old binding implementation.

Tomorrow I’ll continue with the rest of the mappings.


Quick update… spent today finishing off the mapping implementations including porting jump prevention and relative encoder support (well part of it, since I’m splitting it between the source and the mapper now).

Also trying to figure out how this fits into things.


Didn’t know custom curves were available and doubt I’d have a need for them.

Well that was an unwanted distraction but the issue with control curves is now resolved by building a native implementation of the expression evaluation.

Tomorrow I’ll be back to working on bindings proper…


Update: spent the last couple of days working on the actual binding framework including things like the binding object that manages the three sub-objects (source, mapping, target), serialization. Also I’ve started on the new UI: setting up the new table view and starting on the new form view at the bottom of the bindings panel.

Slow, but steady progress.


I’m really struggling with this work. Probably a few things going on:

  • The scope of the work is a bit daunting and there’s lots of moving parts
  • Lots of support enquiries lately which is taking more time than usual
  • It’s been really, really cold here the last few weeks which I always find demotivating - not sure why, but I’m really looking forward to Spring.
  • Physically tired because I’m also doing work around the house (painting and yardwork)
  • Mentally tired (I think just after effects from personal stuff from a few weeks ago)

I think I’m just not in the right frame of mind at the moment and my usual anti-procrastination tricks aren’t cutting it. But, I’m making sure to at least get a little done each day and it is progressing - just slowly. This happens every now and then and I expect in the next week or two I’ll get over it and things will take off again.

Anyway, better get back to it… …or, maybe I need more coffee first.


It means you actually have a life. Nothing wrong with that.


Hey, make sure you take care of yourself mentally! Not worth it to burn yourself out on complexity - maybe time to tackle a simpler area of Cantabile to move forward.

I can so completely relate - some things are just so complex that you need to let them simmer for a while, study some aspects of them now and then, before suddenly things fall into place and you can slay the dragon with a burst of high-energy coding…

I’m in such a place with getting LivePrompter networked with Cantabile - I have bits and pieces that I am sure about, but a lot of moving parts, things to work around, and uncharted territory (for me) that I need to come to grips with before getting to some serious progress. Given that coding is just a hobby for me, and there are a couple of other issues demanding attention, this is sloooow going. But I’m confident that it will happen eventually…

So careful with the coffee - maybe some nice and calming tea :wink:


Hi Brad,

I’m curious how many of the support inquiries could probably have been fielded by us users here on the forum?



That’s tempting but I’ve found if I set aside a big chunk of work like this I tend not to return to it. I just need to take it slowly and keep chipping away at it I think.

Hit me up with any questions - happy to help with that.

If you’ve ever seen Ted Lasso, I’m with him when it comes to tea :slight_smile:

Fairly regularly I’ll suggest people post questions here because I know they’ll get a better answer from you guys. Other times it’s account issues, lost my key etc… things that I need to do and it’s not enough to be worth outsourcing. Also there’s crash reports that need to be reviewed. Usually it’s all very manageable, but sometimes there’ll be an influx.

Since yesterday I’ve decided to just give myself the rest of the week off (a long weekend) and get back into it next week.


It is so important to be able to manage your own well being, I learnt it the hard way being hit by stress a couple of times.

So if you can benefit from delegating some tasks, please do.

Agree with all of the support above. Take your time and look after yourself. My work life is nuts right now, so work on my librarians proceeds as and when. I have only been working on montage.factory for five years now!

Hey Guys,

Thanks everyone for the supportive messages. Four days of painting retaining walls, gardening and reading (fiction) I’m feeling a bit refreshed.

Also, a small opportunity arose over the weekend which is going to require a week or so of work on Cantabile… so I’m setting aside the bindings work for a bit (despite saying I’m reluctant to do that above) and will hopefully get back to it soon.



Absolutely agree with the above sentiments - health is more important than anything.

When you do pick this up again, is there any chance that the State Index variable could be made available to Sys-Ex please (plus any other similar integers). Notice it’s available in the API, but can’t seem to find it internally (forgive me if it’s there).

I’ve got a couple of use cases using a conditional operator in a sys-ex to change a statement based on the current state.

You’re right, there’s no song state index variable - I’ve logged it here.

1 Like

Just a quick update: it’s been a couple of weeks and time to get back into this. Spent today reminding myself where I was up to and did some work on the mapper part of the UI.

This scope of this work is still a bit daunting, but determined to see it through.

1 Like