How To Use Template.io Standalone
- Install Template.io
- Launch Template.io
- Editing Tracks
- Editing Sub-Items
- Editing Settings
- Import, Export, and Flush DB
How To Use Template.io with Open Stage Control and Cubase
- Download the Custom Module and Generic Remote Files
- Follow the Instructions Above to Download and Setup Template.io
- Setup MIDI Ports
- Download Open Stage Control
- Launch Open Stage Control
- Setup Cubase
- Build Your Template!
- Custom Module in Action
- Full Example Using VEP
Requirements 1-3 are pulled from Tauri Prerequisites.
-
a. Download the Microsoft C++ Build Tools installer and open it to begin installation.
b. During installation check the “Desktop development with C++” option.
-
a. already installed on Windows 10 (from version 1803 onward) and later versions of Windows.
-
Node.js - Only version 18.20.x currently works
-
pnpm - Only version 9.15.x currently works
In a command prompt or terminal, navigate to the directory where you would like to download the repository and run the following commands:
-
git clone https://github.com/jpwogaman/template.io.git
-
cd template.io
-
pnpm install
-
pnpm tauri build
The binary should build relative to the repository:
C:\PATH\TO\template.io\src-tauri\target\release\template-io.exe
If you have already cloned the repository and would like to update it, navigate to the root of the repository in a command prompt or terminal and run the following commands:
-
git pull
-
pnpm install
-
pnpm tauri build
- Navigate to where you downloaded template-io.exe (e.g., C:\Users\USERNAME\Downloads\template-io.exe), or to where you built it from source (e.g., C:\PATH\TO\template.io\src-tauri\target\release\template-io.exe), and launch the app.
- Upon the very first startup, Template.io will generate a new directory relative to your systems' home directory (on Windows, this will be C:\Users\USERNAME\template.io) containing two files:
- settings.json
- database.sqlite
⚠️ Do not move or delete these files while Template.io is running, as this will cause the app to crash. If you do, simply restart the app and it will generate new files. However, if you do delete these files, you will lose all of your data.
Now that we've successfully launched Template.io, let's go over the layout of the app. This is what you will see when you first open the app:
The app is divided into two sections:
-
Track List on the left side - This is where you will see all of your tracks. You can add, edit, and delete tracks from this list.
-
Track Details on the right side - This is where you will see all of the details for the selected track. The Track Details are split into five sub-sections and the default number of sub-items for a new track can be adjusted in the settings.
- Instrument Ranges
- Articulations (Toggle)
- Articulations (Tap)
- Additional Layers
- Faders
The sub-items in the Track Details can be displayed as either a table or as cards by clicking on the icon above the section.
Icon | Description |
---|---|
✔️ | Directly used in the Open Stage Custom Module |
⚡ | Directly used in the Open Stage Custom Module and sent as MIDI commands to Cubase |
🔷 | Only used in Template.io |
🔶 | Only used in Template.io for reference, but is not part of the exportable schema, editable, or used in the Open Stage Custom Module |
- 🔷
color: string
track color - 🔷
locked: boolean
prevents editing or deleting the track - ✔️
id: T_{number}
unique and not editable - ✔️
name: string
- 🔷
channel: number
1-16, corresponds to a MIDI channel - 🔷
vep_instance: string
the name of the instance if using VEP,- TBD ADJUSTABLE IN SETTINGS
- 🔷
vep_out: string
the set of main outputs in VEP if using VEP, the number of available outputs can be adjusted in the settings - 🔷
smp_number: string
the number of the sampler if using VEP- TBD ADJUSTABLE IN SETTINGS
- 🔷
smp_instance: string
the name of the sampler if using VEP - 🔷
smp_out: string
the set of outputs in the sampler if using VEP, the number of available outputs can be adjusted in the settings - ✔️
base_delay: number
the positive or negative track delay in ms - ✔️
avg_delay: number
not editable, average of all articulation delays (in ms) if they differ from the base delay - 🔶
arts: number
not editable and not part of actual schema, this is a count of all of the articulations in the Track Details - ✔️
notes: string
editable in the Track Details
To add a track, right-click on a track in the list and select "Add # Track At End". To change the number of tracks you want to add, adjust the number in the input box. This can also be adjusted in the settings.
Icon | Description |
---|---|
✔️ | Directly used in the Open Stage Custom Module |
⚡ | Directly used in the Open Stage Custom Module and sent as MIDI commands to Cubase |
🔷 | Only used in Template.io |
🔶 | Only used in Template.io for reference, but is not part of the exportable schema, editable, or used in the Open Stage Custom Module |
- ✔️
id: T_{number}_FR_{number}
unique and not editable - ✔️
name: string
- ✔️
low: string
note names, with "Middle C" being C3- TBD ADJUSTABLE IN SETTINGS, but "Middle C" is always Note 60
- ✔️
high: number
note names, with "Middle C" being C3- TBD ADJUSTABLE IN SETTINGS, but "Middle C" is always Note 60
- ✔️
white_keys_only: boolean
- ✔️
id: T_{number}_AT_{number}
unique and not editable - ✔️
name: string
- ⚡
code_type: string
these correlate to Open Stage Control addresses - ⚡
code: number
- TBD number ranges will change depending on code_type, currently:
- if
code_type = /control
, then ⚡code: 0-127
- if
code_type = /note
, then ⚡code: 0-127
- if
- TBD number ranges will change depending on code_type, currently:
- ⚡
on: number
0-127 - ⚡
off: number
0-127 - ✔️
default: string
"On" or "Off" - ✔️
delay: number
the track delay (in ms) when this articulation is active - 🔷
change_type: string
- TBD actually employing this logic in the Open Stage Control custom module. Current logic is the Value 2 logic but the options would be:
- Value 1 = the ON and OFF values relate to the CODE itself (i.e. ON = CC18, OFF = CC35)
- Value 2 = the ON and OFF values relate to the CODE's second Value (i.e. CODE = C#3, ON = Velocity 20, OFF = Velocity 21)
- TBD actually employing this logic in the Open Stage Control custom module. Current logic is the Value 2 logic but the options would be:
- ✔️
ranges: string
list of Instrument Range ids, there must always be one Instrument Range linked to every Articulation (Tap or Toggle) - ✔️
art_layers_on: string
list of Additional Layer ids, these will fire all together in the Open Stage Control custom module - ✔️
art_layers_off: string
list of Additional Layer ids, these will fire all together in the Open Stage Control custom module
- ✔️
id: T_{number}_AT_{number}
unique and not editable, the count will always start after the last Toggle Articulation - ✔️
name: string
- ⚡
code_type: string
these correlate to Open Stage Control addresses - ⚡
code: number
- TBD number ranges will change depending on code_type, currently:
- if
code_type = /control
, then ⚡code: 0-127
- if
code_type = /note
, then ⚡code: 0-127
- if
- TBD number ranges will change depending on code_type, currently:
- ⚡
on: number
0-127 - ✔️
default: boolean
only one Tap Articulation may be default - ✔️
delay: number
the track delay (in ms) when this articulation is active - 🔷
change_type: string
- TBD actually employing this logic in the Open Stage Control custom module. Current logic is the Value 2 logic but the options would be:
- Value 1 = the ON value relates to the CODE itself (e.g. ON = CC18)
- Value 2 = the ON value relates to the CODE's second Value (e.g. CODE = C#3, ON = Velocity 20)
- TBD actually employing this logic in the Open Stage Control custom module. Current logic is the Value 2 logic but the options would be:
- ✔️
ranges: string
list of Instrument Range ids, there must always be one Instrument Range linked to every Articulation (Tap or Toggle) - ✔️
art_layers: string
list of Additional Layer ids - ✔️
layers_together: boolean
whether Additional Layers fire all together or one-at-a-time in the custom module. - ✔️
default_layer: string
default Additional Layer if the layers fire one-at-a-time, only one layer may be default. If no default layer is selected, the Open Stage Control custom module will use the first layer in the list for the default.
- ✔️
id: T_{number}_AL_{number}
unique and not editable - ✔️
name: string
- ⚡
code_type: string
these correlate to Open Stage Control addresses - ⚡
code: number
- TBD number ranges will change depending on code_type, currently:
- if
code_type = /control
, then ⚡code: 0-127
- if
code_type = /note
, then ⚡code: 0-127
- if
- TBD number ranges will change depending on code_type, currently:
- ⚡
on: number
0-127
- 🔷
id: T_{number}_FL_{number}
unique and not editable - ✔️
name: string
- ⚡
code_type: string
these correlate to Open Stage Control addresses - ⚡
code: number
- TBD number ranges will change depending on code_type, currently:
- if
code_type = /control
, then ⚡code: 0-127
- if
code_type = /note
, then ⚡code: 0-127
- if
- TBD number ranges will change depending on code_type, currently:
- ⚡
default: number
0-127 - 🔷
change_type: string
- TBD actually employing this logic in the Open Stage Control custom module. Current logic is the Value 2 logic but the options would be:
- Value 1 = the DEFAULT value relates to the CODE itself (e.g. DEFAULT = CC11)
- Value 2 = the DEFAULT value relates to the CODE's second Value (e.g. CODE = C#3, DEFAULT = Velocity 20)
- TBD actually employing this logic in the Open Stage Control custom module. Current logic is the Value 2 logic but the options would be:
To add a sub-item, right-click on a sub-item and follow the same procedure as above for tracks. The number input for adding sub-items is independent from the number input for adding tracks, and can also be adjusted in the settings.
Template.io imports and exports JSON files with this schema (TBD).
Importing a new track list will overwrite the database.sqlite file referenced above. Clicking "Flush DB / Clear All", and it will also clear the database.sqlite file, and will go back to the initial 1 track and the settings-defined default number of sub-items.
- /assets/for-osc/template-io-workfile.json
- /assets/for-osc/template-io-custom-module.js
- /assets/for-cubase/template-io-generic-remote.xml
- /assets/for-cubase/template-io-empty-tracks.cpr
Once you have exported a track list, you must now add it to top of the template-io-custom-module.js file.
const allTrack_jsn = loadJSON('YOUR_TRACK_LIST.json')
-
For Mac, use your IAC driver to create 4 virtual MIDI ports named OSC1, OSC2, OSC3, and OSC4
-
For Windows, download loopMIDI and create the same 4 virtual MIDI ports
Navigate to Open Stage Control and download the most recent open-stage-control_X.XX.X_win32-x64.zip file.
Requirements 1-2 are pulled from Running from sources. Requirements 3-4 are pulled from MIDI configuration
-
Node.js - version 16 and above
In a command prompt or terminal, navigate to the directory where you would like to download the repository and run the following commands:
-
git clone https://github.com/jean-emmanuel/open-stage-control
-
cd open-stage-control/
- Optional! Only if you want the latest release instead of the current development version.
git checkout $(git describe --tags `git rev-list --tags --max-count=1`)
-
npm install
-
npm run build
If you have already cloned the repository and would like to update it, navigate to the root of the repository in a command prompt or terminal and run the following commands:
-
git pull
-
npm install
-
npm run build
If you would like to package an executable as opposed to running from a command prompt, you can open a command prompt or terminal, navigate to the root of the repository and run:
-
export PLATFORM=win32
-
export ARCH=x64
-
npm run package
The binary should build relative to the repository:
C:\PATH\TO\open-stage-control\dist\open-stage-control-win32-x64\open-stage-control.exe
When you launch the executable, a launcher GUI will pop-up. Add the locations for the template-io-workfile.json and the template-io-custom-module.js files in the 'load' and 'custom-module' inputs, respectively.
Add the MIDI ports that we created above by pasting the following into the MIDI input:
OSC1:-1,OSC1 OSC2:OSC2,OSC2 OSC3:OSC3,OSC3 OSC4:-1,OSC4
//device_name:input,output
//-1 will bypass the input/output
The in/out configuration works like this:
Open Stage Control | Cubase | Cubase "In All MIDI" | |
---|---|---|---|
OSC1 | → | OSC1 | No |
OSC2 | ↔ | OSC2 | No |
OSC3 | ↔ | OSC3 | Yes |
OSC4 | → | OSC4 | Yes |
Also, if you use a touchscreen that is connect via USB (as opposed to a tablet using WIFI), I recommend that you add nofocus=1
to the 'client-options' input.
If you have built Open Stage Control from source, you can open a command prompt or terminal, navigate to the root of the repository and run:
Option 1.
npm start
Option 2.
npm start [ -- options]
If you add the [-- options], the launcher GUI will not pop-up and you will need to pass all of the options in directly.
I have a .BAT script similar to this one loaded on a Stream Deck, so I just press one button and skip the launcher GUI. Note that I am still using the built executable in this script, but in theory, you could still use npm start
.
@echo off
set directory=C:\PATH\TO\open-stage-control\dist\open-stage-control-win32-x64
@REM set directory=C:\PATH\TO\open-stage-control
set workfile=C:\PATH\TO\template-io-workfile.json
set custom_module=C:\PATH\TO\template-io-custom-module.js
set midi=OSC1:-1,OSC1 OSC2:OSC2,OSC2 OSC3:OSC3,OSC3 OSC4:-1,OSC4
set client_options=nofocus=1
start "Open Stage Control" /d "%directory%" /min cmd /k "open-stage-control.exe -- --load %workfile% --custom-module %custom_module% --midi %midi% --client-options %client_options%"
@REM start "Open Stage Control" /d "%directory%" /min cmd /k "npm start -- --load %workfile% --custom-module %custom_module% --midi %midi% --client-options %client_options%"
Below is my personal template, located at assets/jps-template/template-io-workfile.json, with red boxes showing where Template.io maps to.
This is how it maps to the Template.io:
⚠️ Note: Template.io was built around Cubase version 11 and 12, it has not been tested on any of the later versions
Now in Cubase, open Studio Setup to setup your MIDI ports to match the schema above.
- Open Cubase and navigate to
Studio > Studio Setup
. - In the
Studio Setup
window, click onAdd Device
and selectGeneric Remote
. - Set the
MIDI Output
toOSC3
. - Click on
Import
and select thetemplate-io-generic-remote.xml
file.
This generic remote setup in Cubase is configured to transmit Control Code 126 at Value 1 on Channel 1 on Port OSC3
every time a MIDI track is selected.
In our custom module, every time Open Stage Control receives this exact MIDI signal, it will send Control Code 127 at Value 127 on Channel 1 on Port OSC4
back to Cubase.
////// ln. 65-76
/**
* @param {126} arg1_OSC3
* @param {0 | 1} arg2_OSC3
*/
function toggles_OSC3(arg1_OSC3, arg2_OSC3) {
if (arg1_OSC3 === 126 && arg2_OSC3 === 1) {
toggleSendUpdate = true
}
if (arg1_OSC3 === 126 && arg2_OSC3 === 0) {
toggleSendUpdate = false
}
}
////// ln. 78-81
function sendUpdateCode() {
send('midi', 'OSC4', '/control', 1, 127, 127)
}
////// ln. 196-206
if (port === 'OSC3' && address === '/control') {
toggles_OSC3(arg1, arg2)
if (toggleAutoUpdate && toggleSendUpdate) {
sendUpdateCode()
toggleSendUpdate = false
}
}
⚠️ Note: Unfortunately, for the time being (as of Cubase v12.0.7), since there is no MIDI send feature on instrument tracks, audio tracks, or any other track other than MIDI tracks, your template will have to primarily use MIDI tracks routed to rack instrument tracks in Cubase or to Vienna Ensemble Pro.
Create empty MIDI tracks and start naming and routing them as you would normally. I personally name these verbatim what I have in my Template.io track list.
This will receive the signal that Open Stage Control just sent (in response to the signal Cubase sent when the track was selected) and return a Polyphonic Key Pressure signal on port OSC3
that is completely unique to every track.
This number is calculated in the Custom Module like so:
if (address !== '/key_pressure') return data
const trkNumb = arg1 * 128 + arg2
// address = "Poly Pressure" in the "Event Transform Actions" in the picture above
// arg1 = "Value 1" in the "Event Transform Actions" in the picture above
// arg2 = "Value 2" in the "Event Transform Actions" in the picture above
The trkNumb
will reference the index of the tracks in the track list, which is ordered by the T_{number}
id that every track is given.
const track = items[trkNumb]
if (!track || trkNumb !== parseInt(track.id.split('_')[1])) {
receive('/selected_track_name', 'No Track Data!')
receive('/selected_track_delays', ' ')
receive('/selected_track_notes', ' ')
oscResetFads(0, 8)
oscResetArts(0, 18)
return data
}
I know adding transformers on each track seems like a hassle, so I took the liberty of setting up these transformers on 384 empty MIDI tracks so you wouldn't have to! These tracks are available in the template-io-empty-tracks.cpr file and are all disabled for you to activate anytime you wish to add a new instrument. I recommend you import these into your template, or use the file to start a new one.
Okay let's make sure we have everything we need:
- Track List populated with track parameters and loaded into the Open Stage Control custom module
- Open Stage Control launched with correct MIDI ports
- Cubase MIDI ports setup with correct in/out configuration
- Cubase Generic Remote setup with correct MIDI ports
- MIDI tracks created with Transformers and unique Polyphonic Key Pressure Signal correctly mapped to the
T_{number}
id of every track in the Track List
Great! Now every time we select a track, Open Stage Control will populate the correct parameters for that track and we can now finally start writing music!