Fixing Song Management Part III
Previously I’ve written about plans to improve Cantabile’s song management. After a couple of weeks coding and a further few weeks stabilizing it, it’s now in pretty good shape. This post attempts to sum up all the changes and provide a high-level overview of some new concepts and ideas.
These new capabilities all center around a new feature called shared Racks which provide an easy way to share plugins across songs, provide for fast song switching (including set list pre-loading), simpler song management and other improvements.
Songs vs Racks
Cantabile now supports two kinds of files — Songs and Racks. Both are very similar to what used to be called a “Session” — but each serve slightly different purpose.
Racks in Cantabile 3 are a completely different concept to the racks in Cantabile 2. In Cantabile 2 they were simply a chain of plugins. In version 3 a rack is essentially an entire session that is saved as a separate file and referenced from the song file.
Racks support plugins, media players, MIDI assignments, triggers as well as linking to other racks. Racks also have a set of input and output ports that can be used to send and receive audio and MIDI.
A song is basically a special kind of rack:
- Songs have tempo and time signature settings
- Songs have a global transpose setting
- Songs don’t have their own input and output ports — they can only access the globally defined environment ports (ie: the ports defined in Options)
- Only one song can be active at a time
- Only a song can receive signals back from the output ports of a rack.
The other important difference between songs and racks is that only songs can load racks. ie: the set of loaded racks is controlled entirely by the current song.
Although a rack can’t load another rack, it can route to the inputs of other racks loaded by the song.
Say you had a song with three racks “Piano”, “Hammond” and “Effects” — the Piano and Hammond racks might send their output to the Effects rack.
Racks Can Outlive Songs
Another important feature of racks is that they can continue processing across song boundaries. So for example, if you had a second song that also referenced the Piano and Effects racks, loading this second song would re-use the existing two racks and not need to load them.
Not only can this dramatically improve song switching times, the shared racks also remain running during the song transition so sounds can continue sound out and harsh cuts are avoided.
You can even configure your racks so that you can continue playing an instrument across song boundaries — if you need that.
Environment vs Rack Ports
Racks have access to two types of ports:
- Rack ports — the ports defined on the rack itself and used by the parent song to send audio and MIDI to/from the rack.
- Environment ports — the globally visible ports defined in Options which are typically mapped to physical hardware devices.
One important difference between rack and environment ports is that rack ports are disconnected immediately when the parent song is closed. Held notes are released immediately and audio ports are soft muted.
In contrast, environment ports are left connected across a song transition. If you need an instrument to be playable across song transitions, the rack holding the instrument must be connected via the environment ports — not the rack ports.
States
States let you save the state of a song or rack and quickly switch between them — see here for more. (“States” are the new name for what used to be called “Sub-Sessions”)
Although both songs and racks support states the typical usage for each is quite different:
- Rack states are normally used to represent different sounds. eg: you might have a piano rack, with states “Grand”, “Studio” etc…
- Song states can be used for the parts (or sections) of a song. eg:
“Intro”, “Verse 1”, “Chorus”, “Verse 2” etc…
Set List Simplifications
In Cantabile 2, each entry in the set list specified a session and sub-session to use.
In previous builds of Cantabile 3, each set list entry specified a session file and list of session states (one for each song part) and a transpose setting.
Since song parts can now be defined using song states the set list entries can be simplified and now they only store the name of the song file and a program number (used to load the song via a MIDI assignment).
This is a subtle but important change because now the entire song is now defined in one place (the song file) rather than in multiple places (the session and the set list entry). Building a set list is now just a simple matter of selecting the song files.
Set List Pre-loading
Another important new feature is “set list pre-loading”.
When switching songs by far the slowest operation is loading plugins. When you pre-load a set list (Tools menu -> Preload Set List) all songs in the set list and all racks referenced by those songs, including plugins are all loaded into memory.
This can take a while and use quite a bit of memory for a large set list with many plugins, but once loaded switching songs can be very fast. There’s still a small delay while the old plugins are stopped and the new ones started but often it’s possible to switch songs in about one second.
There’s one caveat to this: if your songs are using different plugin presets, there can be a delay while the new preset is loaded. The time taken is very plugin dependant — some switch almost instantly while others can be quite slow. Sometimes you can work around this by using multiple instances of those plugins, or re-arranging your set list to use the same sounds.
The trick to this working effectively and not requiring too much memory is to use shared racks across the songs so that heavy plugins can be re-used and not require separate instances for each song.
Side-Note: it’s been suggested many times that Cantabile should pre-load the next song in the list while playing the current one. This is something I’ve investigated however there are various technical reason why this is not feasible and it introduces a real risk of audio and control problems while playing. This is not an approach I agree with and I’m convinved it’s better to just throw more memory at the problem.
Controlling Racks from Songs
While sometimes you’ll want to re-use a rack in exactly the same state across multiple songs, more often than not each song will have the rack configured slightly differently.
Cantabile provides a few different ways to control the rack from the parent song.
- Rack States- save the different rack configurations as rack states and the parent song file can select the appropriate state when the song (or song part) is loaded.
- Exported Settings — similarly to how a state can control various settings in a rack, exported settings are saved in the parent song file. eg: you might boost the gain on a single plugin in one song and export that to the parent song. Often this is simpler and quicker than defining a whole new rack state for one setting.
- Triggers and MIDI Assignments — although more work to setup this gives the most flexibility. Since racks support MIDI assignments, you can create assignments from the rack MIDI input ports and then use triggers in the parent song to re-configure the rack when it’s loaded.
Embedded Racks
Normally racks are saved as separate files so they can be shared across songs. However Cantabile also supports embedded racks. These racks are saved as part of the parent song file and have a few different features and limitations to shared racks.
- Embedded racks can be loaded not only into a song file, but into other racks. You can even recursively nest racks multiple levels deep.
- An embedded rack loaded into a song has both environment and rack ports. An embedded rack loaded into another rack can only access it’s own rack ports.
- Embedded racks can’t be shared across songs and other racks can’t link to them — only the parent song/rack has access to its ports.
Two interesting things that embedded racks provide:
- Ability to change a sound without having to update routing to it. eg: if you delete one plugin and insert another, you’ll typically have to recreate the routing to it. If you have the plugin in an embedded rack, with routing to the rack, you can replace what’s in the rack and routes to the rack are left unaffected.
- Internal MIDI Assignments — since a rack can create assignments from its MIDI in ports, you can create MIDI assignments from media players and MIDI generating plugins. ie: Media Player -> Rack MIDI In Port -> Rack -> MIDI Assignment.
Cantabile includes commands to convert embedded racks to shared racks and vice-versa.
Moving Over
This set of changes is one of the biggest and most important set of changes ever made to Cantabile. Unfortunately, moving to this new approach is not straight forward and will require some reconfiguration of your session files.
At a minimum it will require converting your session files to racks and creating a containing song file for each song. Cantabile will include tools to do this automatically. This will work but you’ll be missing some of the benefits of these new features.
A better approach is to spend the time to break your session files into smaller racks and reconfigure your songs to use these smaller and simpler rack files. It’s more work but in the end I think the benefits will be well worth it — especially for song management and maintenance.
Thanks
Finally, I really want to thank everyone who provided feedback on the previous blog posts on this topic. In all, there were probably several hundred emails discussing this topic and I can’t overstate how useful I found these discussions. To all involved — thank you.
Availability
All this is available now in the latest Cantabile preview build.