====== Mozaic: Include Snippet - Migration Manager ======
{{tag>Mozaic midi_scripting}}
The Migration Manager (Include) is a code snippet that allows to migrate script parameters between different script versions.
You just need to add 2 event calls and define 4 events for callbacks.
==== Problem Solved by the Snippet ====
One can easily state-safe a script and reload its full settings including the scripts source into another Mozaic instance - but if you load a newer version of the script or minimaly change the source and do an 'upload', all your precious settings are lost. The Migration Manager allows to recover all settings from another instance.
==== Migration Instructions for 'End Users' ====
* Before loading a new script version, save the to-be-preseved configuration to a new preset name (either in the host or in Mozaic)
* Update the script to the new version by loading the script - this will come up with the scripts default settings
* Turn the IMPORT knob of the script
* Open a second Moazic instance and load the saved configuration, you have ten seconds to do this
* The new version will pick up the configuration and you are ready to go
==== Use-Case for Script-Devs ====
If you are a script dev, you can use this feature to fast-restre your settings after you minimally changed code and hit 'UPLOAD'. Just keep the 'sender' instance open and reload it when importing to the receiver.
==== Suitable Scripts ====
* Your script should have many parmeters, more than you would like to dial in manually either when updating or for every test during development the script
* Updating of the script should be intended or likely
==== Scripts using the Migration Manager Snippet ====
* The [[https://patchstorage.com/migration-manager-include/|Migration Manager (Include) Demo]]
* The [[https://patchstorage.com/midi-multicast/|MIDI MultiCast]] script transfers its up to 7x16 routing scenes and config variables
* The[[https://patchstorage.com/mutator/|MutatoR]] script transfers around 12KB of data in 13 migration steps
* The[[https://patchstorage.com/ms6-sysex-performance-editor-and-spatial-processor/|MS6 Performance Editor and Spatial Processor]] script transfers its settings in 2 migrations steps
\\
===== Adding the 'Migration Manager' to own scripts ====
You can either grab the snippet from one of the example scripts where you find the to-be-included snippet below the line
>>>>>>>> MIGRATION MANAGER INCLUDE <<<<<<<<
at the end of the example - check that you found the current v1.2 snippet code.
Or you copy the code from the large code block at the end of this page to the end of your own script.
* In the @OnLoad, right at the start call @MigrationManagerInit and at the end setup your unique script's id and call @MigrationManagerOnLoad.
* Add an 'IMPORT' knob in your scripts GUI and call @MigrationManagerOnImport to start importing if the knob is turned.
* Your scipt also needs to define the 4 event functions @MigrateSend, @MigrateRead, @MigrateImportDone,@MigrateExportDone that will be called by the Migration Manager.
* Choose a unique **mmScriptId** for your script, see [[#Reserved mmScriptId‘s]]
==== @OnLoad ====
Right at the start of this event function add
Call @MigrationManagerInit
to initialize all variables.
At the end of your @OnLoad, setup you scripts unique id and
Call @MigrationManagerOnLoad
for the optinal sending of data
There are three types of scripts
- ) Your script doesn't define an own OnTimer
- ) Your script defines an own OnTimer, which is already running at this point and which has an intervall less than 200
- ) Your script defines an own OnTimer, but its either with a slower interval (>200) or it only runs conditional
Only scripts of type 3) need to add the IF block after the Call @MigrationManagerOnLoad that temporary sets up a
faster timer timer interval and start the timer if upon this 'upload' another instance is waiting for a transmission.
\\
After migration is finished (takes several timer events), the @MigrateExportDone event is called, where you can undo these temporary changes
mmScriptId = 0xDEAA0 // Please choose an new unique number up to 24bits !
mmScriptTimer = YES // YES if script defines an own timer. Remove the line or
// set to NO if Migration Manager should manage the timer
Call @MigrationManagerOnLoad
if mmIsMigrating
// This code is of type 3) (own slower timer, and not yet running)
SetTimerInterval 50
StartTimer
endif
==== Choose a unique mmScriptId ====
Each script needs to use an own unique mmScriptId. There could be several totally different scripts including the Migration Manager Snippet loaded in a single AUv3 host session, therefore the scripts unique id is used to identify other instances of this specific script and to ensure the scripts ‚own‘ data is received. The id can be understood as „sender/receiver name or address“ of a script, the Migration Manager only connects scripts with identical mmScriptId. A later change of the id for a script already in use is not advisable as the data of older saved instances can no longer be imported.
The maximum value for mmScriptId is 24bits or 6 hex chars (0x000100 to 0xFFFF80)
\\
=== Reserved mmScriptId‘s ===
The following mmScriptId ranges are already in use by other scripts and should be avoided:
^ ^ mmScriptId ^ end of range ^ script ^
| | 0x4200 | 0x420F | MS6 SysEx Performance Editor and Spatial Processor | |
| | 0xDEAA0 | 0xDEAAF | Migration Manager Demo | |
| | 0x4D4D43 | 0x4D4D4F | Midi Multicast | |
| | 0x50C001 | 0x50C01F | MutatoR | |
==== @OnTimer ====
When your script uses an own timer like in type b) or c), make sure to wrap your original OnTimer code into an IF block
like shown below.
During both migration directions (send of read part) the timer needs to be running and calling the Migration Manger
@OnTimer
if mmIsMigrating
Call @MigrationManagerOnTimer
else
// Code you already had in your timer
endif
@End
==== @OnKnobChange ====
If the knob ideally labeled 'IMPORT' is turned, you can add the following code to initiate the receiving of
data and additionally support double-tap
_knob = LastKnob
_val = GetKnobValue _knob
if _knob = 1
...
elseif _knob = 2 and _val >= 64 // IMPORT knob with double-tap feature
SetKnobValue 2, 127 // Turn knob to 'on' position
Call @MigrationManagerOnImport // Call the import manager
// For a script of type 3) you need to temporary modify the timer interval and start it
SetTimerInterval 50
StartTimer
endif
\\
==== Migration Manager Callbacks Events ====
The transmission happens in the two user events @MigrateSend and @MigrateRead by copying to/from the global98 array, one set of variables on each call.
Internally the MigrationManager uses the global97 array to drive the communication, calling your event functions several times until all data is transmitted.
==== @MigrateSend ====
Input: pIdx
\\
Output: pType, global98
The pIdx identifies the parameter to be transfered, the index starts with your unique script-id
and is incremented on every transmission step. You code needs to fill in global98 and copy pIdx into
pType - to indicate the end of transmission, set pType to MIGRATION_DONE
@MigrateSend // params: pIdx output: pType, global98
pType = pIdx // Transfer preset data one by one
if pIdx = mmScriptId
CopyArray data,global98,16 // First transfer 16 entries of 'data'
elseif pType = mmScriptId +1
CopyArray things,global98,16 // Then transfer 16 entries of 'things'
elseif pType = mmScriptId +2
CopyArray stuff,global98,16 // On third call transfer 16 entries of 'stuff'
elseif pType = mmScriptId +3
global98[0] = color_code // On fourth call transfer single entry for colorCode
else
pType = MIGRATION_DONE // The last call signals end of transmission
endif
@End
==== @MigrateRead ====
Input: pType, global98x
The MigrationRead event function is called after the sender updated the data supplying a pType parameter specifiying the content of global98 array.
The MIGRATION_DONE is not forwarded to this event, but instead @MigrateImportDone is called
@MigrateRead // params: pType, global98
if pType = mmScriptId // Receive is analogous
CopyArray global98,data,16 // First call copy into 16 'data' entries
elseif pType = mmScriptId +1
CopyArray global98,things,16 // Second call copy into 16 'things' entries
elseif pType = mmScriptId +2
CopyArray global98,stuff, 16 // Third call copy into 16 'stuff' entries
elseif pType = mmScriptId +3
color_code = global98[0] // Last call restores the colorCode entry
endif
@End
If you need to transfer more variables, just add them accordingly to both MigrateRead and MigrateSend, using the same offset in both events.
For version updates, only add new variables at the end of the IF-cascade and take care of filling in default values for variables that an older script version might not supply.
==== @MigrateImportDone ====
At the end of an importing transmission started by a call to @MigrationManagerOnImport, the @MigrateImportDone event is called by the Migration Manager.
If your script is of type 3), then this is the place to undo the temporary timer settings
@MigrateImportDone // param pType (either MIGRATION_DONE or MIGRATION_ERROR)
if pType = MIGRATION_DONE
// Call your scripts redraw functions to show the changed state to the user
endif
// Don't forget to re-position the IMPORT knob
SetKnobValue 2,0
// This sample code is for type 3) (own slower timer, not running
// after OnLoad), we need to restore the original settings
SetTimerInterval 1000
StopTimer
@End
==== @MigrateExportDone ====
At the end of an exportingtransmission started by a call to @MigrationManagerOnLoad in the @OnLoad, the @MigrateExportDone event is called by the Migration Manager.
If your script is of type 3), then this is the place to undo the temporary timer settings
@MigrateExportDone // param pType (either MIGRATION_DONE or MIGRATION_ERROR)
// This sample code is for type c) (own slower timer, not running
// after OnLoad), we need to restore the original settings
SetTimerInterval 1000
StopTimer
@End
\\
\\
===== Migration Manager Include Snippet ====
// ╔╦╗┬┌─┐┬─┐┌─┐┌┬┐┬┌─┐┌┐┌ ╔╦╗┌─┐┌┐┌┌─┐┌─┐┌─┐┬─┐
// ║║║││ ┬├┬┘├─┤ │ ││ ││││ ║║║├─┤│││├─┤│ ┬├┤ ├┬┘
// ╩ ╩┴└─┘┴└─┴ ┴ ┴ ┴└─┘┘└┘ ╩ ╩┴ ┴┘└┘┴ ┴└─┘└─┘┴└─
// =================== V 1.2 ======================
@MigrationManagerInit
MIGRATION_RUNNING = -1 // mmState and message type
MIGRATION_ERROR = -2
MIGRATION_DONE = -7
MM_MAGIC = 0 // Offsets in global97[ ]
MM_READER = 1
MM_SCRIPT = 2
MM_TYPE = 3
MM_ACK = 4
MM_SENDER = 5
MM_MAGIC_ID = 0x15EEC0DE // Magic number for MigrationManager
mmActive = NO
mmRead = NO
mmState = MIGRATION_DONE
mmInstance = Random 1, 1000
if Unassigned mmScriptTimer
mmScriptTimer = NO
endif
mmIsMigrating = NO
@End
@MigrationManagerOnLoad
if Unassigned mmScriptId
Log {📦 MigrationManager v1.2: ‼️ mmScriptId not initialized}
elseif Unassigned mmIsMigrating
Log {📦 MigrationManager v1.2: ‼️ @MigrationManagerInit not called before @MigrationManagerOnLoad}
else
mmIsMigrating = NO
// Starting with about 7 hex digits, Mozaic can no longer correctly add small
// values due to its limited internal number range
// As the unique script id is later incremented for each message, better test
// beforehand, if the value is still in range for math operations
mmIdx = mmScriptId +1
if mmIdx - mmScriptId <> 1
Log {📦 MigrationManager v1.2: ‼️ mmScriptId value too high}
else
mmIdx = mmScriptId
if global97[MM_MAGIC]<>MM_MAGIC_ID
Log {📦 MigrationManager v1.2: OnLoad - No receiver waiting}
elseif global97[MM_SCRIPT]<>mmScriptId
Log {📦 MigrationManager v1.2: OnLoad - Wrong receiver waiting}
elseif global97[MM_SENDER]<>0 or global97[MM_TYPE]<>0
Log {📦 MigrationManager v1.2: OnLoad - Another migration ongoing}
else
global97[MM_SENDER] = mmInstance
mmState = MIGRATION_RUNNING
mmActive = YES
mmRead = NO
mmTimeout = SystemTime + 500
mmIsMigrating = YES
if mmScriptTimer = NO
Log {📦 MigrationManager v1.2: Start Migration with own timer event}
SetTimerInterval 50
StartTimer
else
Log {📦 MigrationManager v1.2: Start Migration using scripts timer event}
endif
endif
endif
endif
@End
@MigrationManagerOnImport
if Unassigned mmScriptId
Log {📦 MigrationManager v1.2: ‼️ mmScriptId not initialized}
elseif Unassigned mmIdx
Log {📦 MigrationManager v1.2: ‼️ @MigationManagerOnLoad was not called}
elseif mmState<>MIGRATION_RUNNING
FillArray global97, 0
FillArray global98, 0
global97[MM_MAGIC] = MM_MAGIC_ID
global97[MM_READER] = mmInstance
global97[MM_SCRIPT] = mmScriptId
// MM_SENDER, MM_TYPE and MM_ACK are zero fromm the FillArray above
mmActive = YES
mmRead = YES
mmState = MIGRATION_RUNNING
mmTimeout = SystemTime + 10000
if mmScriptTimer = NO
Log {📦 MigrationManager v1.2: Waiting for data with own timer event}
SetTimerInterval 50
StartTimer
else
Log {📦 MigrationManager v1.2: Waiting for data using scripts timer event}
endif
mmIsMigrating = YES
endif
if not mmActive
mmIsMigrating = NO
pType = MIGRATION_ERROR
Call @MigrateImportDone
endif
@End
@OnTimer
Call @MigrationManagerOnTimer
@End
@MigrationManagerOnTimer
if Unassigned mmActive
mmActive = NO
endif
if mmActive
Call @MMTimerVerifyConnection
if mmState <> MIGRATION_ERROR
if mmRead
Call @MMTimerRead
else
Call @MMTimerSend
endif
endif
if SystemTime > mmTimeout
Log {📦 MigrationManager v1.2: ‼️ Timeout}
mmState = MIGRATION_ERROR
if mmScriptTimer = NO
StopTimer
endif
endif
if mmState <> MIGRATION_RUNNING
mmIsMigrating = NO
if mmRead
pType = mmState
Call @MigrateImportDone
else
pType = mmState
Call @MigrateExportDone
endif
if mmScriptTimer = NO
StopTimer
endif
if mmRead or mmState = MIGRATION_ERROR
FillArray global97,0
FillArray global98,0
mmActive = NO
endif
endif
endif
@End
@MMTimerVerifyConnection
if global97[MM_MAGIC]<>MM_MAGIC_ID
mmState = MIGRATION_ERROR
Log {📦 MigrationManager v1.2: ‼️ Communication broken - Magic of msg block changed}
elseif global97[MM_SCRIPT]<>mmScriptId
mmState = MIGRATION_ERROR
Log {📦 MigrationManager v1.2: ‼️ Communication broken - ScriptId of msg block changed}
elseif (not mmRead) and global97[MM_SENDER]<>mmInstance
mmState = MIGRATION_ERROR
Log {📦 MigrationManager v1.2: ‼️ Communication broken - SenderId of msg block changed}
elseif mmRead and global97[MM_READER]<>mmInstance
mmState = MIGRATION_ERROR
Log {📦 MigrationManager v1.2: ‼️ Communication broken - ReaderId of msg block changed}
endif
@End
@MMTimerSend // OnLoad
if global97[MM_TYPE]=global97[MM_ACK]
pIdx = mmIdx
pType = MIGRATION_ERROR
Call @MigrateSend
global97[MM_TYPE] = pType
if pType = MIGRATION_ERROR
mmState = pType
Log {📦 MigrationManager v1.2: ‼️ MigrateSend didn't set pType for pIdx=},pIdx
elseif pType = MIGRATION_DONE
mmState = pType
Log {📦 MigrationManager v1.2: Migration succesful}
else
Log {📦 MigrationManager v1.2: - Send },mmIdx - mmScriptId
mmIdx = mmIdx + 1 // Inc clips at 65536 per default
mmTimeout = SystemTime + 250
endif
endif
@End
@MMTimerRead // OnImport
pType = global97[MM_TYPE]
if pType=MIGRATION_DONE
mmState = pType
Log {📦 MigrationManager v1.2: Migration succesful}
elseif pType=MIGRATION_ERROR
mmState = pType
Log {📦 MigrationManager v1.2: ‼️ Communication broken}
elseif pType<>0 and pType<>global97[MM_ACK]
Call @MigrateRead
global97[MM_ACK] = pType
Log {📦 MigrationManager v1.2: - Read },pType - mmScriptId
mmTimeout = SystemTime + 250
endif
@End
// =================== END ============================
// ╔╦╗┬┌─┐┬─┐┌─┐┌┬┐┬┌─┐┌┐┌ ╔╦╗┌─┐┌┐┌┌─┐┌─┐┌─┐┬─┐
// ║║║││ ┬├┬┘├─┤ │ ││ ││││ ║║║├─┤│││├─┤│ ┬├┤ ├┬┘
// ╩ ╩┴└─┘┴└─┴ ┴ ┴ ┴└─┘┘└┘ ╩ ╩┴ ┴┘└┘┴ ┴└─┘└─┘┴└─