Differences
This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
mozaic_tips_and_tricks [2020/03/01 22:03] – Added Knob double-tap Support _ki | mozaic_tips_and_tricks [2021/07/01 21:41] (current) – Added „ About State-Saving“ section _ki | ||
---|---|---|---|
Line 1: | Line 1: | ||
====== Mozaic: Scripting Tips & Tricks ====== | ====== Mozaic: Scripting Tips & Tricks ====== | ||
- | {{tag> | + | ~~NOTOC~~ |
This wiki page contains programming tips & tricks for [[mozaic_plugin_engine|Mozaic]] | This wiki page contains programming tips & tricks for [[mozaic_plugin_engine|Mozaic]] | ||
+ | * [[#NoteOn Velocity 0 Special Case]] | ||
* [[#Two dimensional Arrays]] | * [[#Two dimensional Arrays]] | ||
* [[#Multi dimensional Arrays]] | * [[#Multi dimensional Arrays]] | ||
* [[#Store two positive Values into a single Variable]] | * [[#Store two positive Values into a single Variable]] | ||
* [[#Store three unsigned Bytes into a single Variable]] | * [[#Store three unsigned Bytes into a single Variable]] | ||
+ | * [[#Output Fixed Point Values in Labels]] | ||
* [[#Some Best Practice Tips]] | * [[#Some Best Practice Tips]] | ||
+ | * [[#About State-Saving]] | ||
+ | * [[#Detect Long or Short Pad Taps]] | ||
+ | * [[#Detect Long or Short SHIFT Button Taps]] | ||
* [[# | * [[# | ||
+ | * [[#Dynamic Letters for Labeling]] | ||
* [[#Use the SHIFT Button to toggle to HELP View]] | * [[#Use the SHIFT Button to toggle to HELP View]] | ||
* [[#Use a Knob to toggle 16 Pads View to HELP View]] | * [[#Use a Knob to toggle 16 Pads View to HELP View]] | ||
* [[#Knob double-tap Support]] | * [[#Knob double-tap Support]] | ||
+ | * [[#Remove or add an entry inside an array]] | ||
+ | * [[#Using Logic Operators in Expressions instead of IF cases]] | ||
+ | * [[#Using Inc and Dec in Expressions]] | ||
* [[# | * [[# | ||
+ | * [[#Include Snippets]] | ||
+ | |||
+ | ===== NoteOn Velocity 0 Special Case===== | ||
+ | < | ||
+ | The Midi Spec allows to use a NoteOn command with velocity zero instead of a NoteOff command. If this combination is received by Mozaic, this midi command is automatically converted to
NoteOff vel 0 - even for the @OnMidiInput event and when checking MidiByte1. | ||
+ | |||
+ | Therefore scripts don‘t need to check for this ‚NoteOn Vel 0‘ case, as that special case is handled by Mozaic. Even when using MidiThrough, | ||
+ | |||
===== Two dimensional Arrays ===== | ===== Two dimensional Arrays ===== | ||
Line 77: | Line 93: | ||
varB = 124 // allowed range 0 .. ~16000 | varB = 124 // allowed range 0 .. ~16000 | ||
| | ||
- | combinedValue = valA + valB * maxA | + | combinedValue = valA + (valB * maxA) |
</ | </ | ||
+ | |||
+ | For single-byte MIDI values, use 128 for maxA | ||
To later extract the values from their packed format use | To later extract the values from their packed format use | ||
Line 113: | Line 131: | ||
</ | </ | ||
+ | |||
+ | \\ | ||
+ | ===== Output Fixed Point Values in Labels ===== | ||
+ | < | ||
+ | When outputting floating point values into labels (knobs, pads, titles) Moazic generates either a 4 digit fraction string | ||
+ | like 42.5000 (if a remainder present) or an integer value without fraction. | ||
+ | |||
+ | \\ | ||
+ | To always output a positive float with two digits (even when integer), one needs to split the input value and output the computed integer parts individually: | ||
+ | < | ||
+ | n = Round value * 100 | ||
+ | f = Div n, 100 | ||
+ | d = n-f*100 | ||
+ | r1 = Div d, 10 | ||
+ | r2 = d % 10 | ||
+ | LabelKnob 0, {B: | ||
+ | </ | ||
+ | |||
+ | \\ | ||
+ | To always output a negative and positive float with a single digit, the split needs to work on the absolute value | ||
+ | and a conditional statement is used to output both variants: | ||
+ | < | ||
+ | n = Round Abs(value * 10) | ||
+ | f = Div n, 10 | ||
+ | r = (n-f*10) % 10 | ||
+ | if value >=0 | ||
+ | LabelKnob 1, {N: +},f,{.},r | ||
+ | else | ||
+ | LabelKnob 1, {N: },f,{.},r | ||
+ | endif | ||
+ | </ | ||
\\ | \\ | ||
Line 141: | Line 190: | ||
</ | </ | ||
+ | \\ | ||
+ | ===== About State-Saving ===== | ||
+ | < | ||
+ | All assigned variables of a script are automatically included in a saved preset and restored when reloading. To not overwrite them, use the | ||
+ | < | ||
+ | if Unassigned channel | ||
+ | channel = 10 | ||
+ | bank = 1 | ||
+ | .... | ||
+ | endif | ||
+ | </ | ||
+ | construct in the scripts **OnLoad** that only set the default value if a script is freshly run using ‚Upload‘. If reloaded (and therefor some value is already assigned to ‚channel‘ ) the initialization block is skipped. | ||
+ | |||
+ | |||
+ | Mozaic stores some more more information in the presets/ | ||
+ | * Layout | ||
+ | * Pad labels, color, latch-state | ||
+ | * Knob labels and position | ||
+ | * XY position | ||
+ | * Title label above pads, knobs, XY | ||
+ | * Rootnote and scale | ||
+ | |||
+ | Things **not** restored during session reload: | ||
+ | * ShortName | ||
+ | * Timer state and settings | ||
+ | * LFO settings | ||
+ | |||
+ | |||
+ | \\ | ||
+ | ===== Detect Long or Short Pad Taps ===== | ||
+ | < | ||
+ | |||
+ | < | ||
+ | @OnLoad | ||
+ | FillArray downStart, | ||
+ | pressTime = 250 | ||
+ | @End | ||
+ | |||
+ | @OnPadDown | ||
+ | downStart[LastPad] = SystemTime | ||
+ | @End | ||
+ | |||
+ | @OnPadUp | ||
+ | pad = LastPad | ||
+ | if SystemTime - downStart[pad] < pressTime | ||
+ | Log {Short tap pad }, pad | ||
+ | else | ||
+ | Log {Long tap pad }, pad | ||
+ | endif | ||
+ | @End | ||
+ | </ | ||
+ | |||
+ | \\ | ||
+ | |||
+ | ===== Detect Long or Short SHIFT Button Taps ===== | ||
+ | < | ||
+ | |||
+ | < | ||
+ | @OnLoad | ||
+ | shiftStart = 0 | ||
+ | pressTime | ||
+ | @End | ||
+ | |||
+ | @OnShiftDown | ||
+ | shiftStart = SystemTime | ||
+ | @End | ||
+ | |||
+ | @OnShiftUp | ||
+ | if SystemTime - shiftStart < pressTime | ||
+ | Log {Short tap SHIFT} | ||
+ | else | ||
+ | Log {Long tap SHIFT} | ||
+ | endif | ||
+ | @End | ||
+ | </ | ||
\\ | \\ | ||
Line 154: | Line 278: | ||
LabelPad 0,{Line 1},{ | LabelPad 0,{Line 1},{ | ||
LabelPad 1,{Upper},{ -------------- },{Lower} | LabelPad 1,{Upper},{ -------------- },{Lower} | ||
+ | @End | ||
+ | </ | ||
+ | |||
+ | \\ | ||
+ | ===== Dynamic Letters for Labeling ===== | ||
+ | < | ||
+ | The NoteName function of Moazic can be used to dynamically output the letters A-G inside the label string definitions of Knobs, Pads or Logs. This allows to construct labels like ‚Bank A‘ to ‚Bank D‘, or ‚Preset A-1‘ to ‚Preset F-8‘. | ||
+ | |||
+ | < | ||
+ | @OnLoad | ||
+ | ShowLayout 2 | ||
+ | ABCDEFG | ||
+ | |||
+ | for i = 0 to 15 | ||
+ | row = 1 + (i>7) | ||
+ | id = i%8 | ||
+ | if (id < 7) | ||
+ | LabelPad i, {Pad },(NoteName ABCDEFG[id], | ||
+ | endif | ||
+ | endfor | ||
@End | @End | ||
</ | </ | ||
Line 224: | Line 368: | ||
- | If a knob is only used to toggle between two states like a switch, the code for double-tap detection would look like: | + | If a knob is only used to toggle between two states like a switch |
< | < | ||
+ | @OnKnobChange | ||
+ | _knob = LastKnob | ||
+ | _val = GetKnobValue _knob | ||
+ | | ||
+ | if _knob = TOGGLE_KNOB | ||
+ | if _val = 64 | ||
+ | isOn = not isOn | ||
+ | SetKnobValue TOGGLE_KNOB, | ||
+ | else | ||
+ | isOn = _val > 64 | ||
+ | endif | ||
+ | | ||
+ | if isOn | ||
+ | LabelKnob TOGGLE_KNOB, | ||
+ | else | ||
+ | LabelKnob TOGGLE_KNOB, | ||
+ | endif | ||
+ | endif | ||
+ | @End | ||
+ | |||
@OnLoad | @OnLoad | ||
ShowLayout 2 | ShowLayout 2 | ||
Line 246: | Line 410: | ||
else | else | ||
LabelKnob TOGGLE_KNOB, | LabelKnob TOGGLE_KNOB, | ||
- | endif | ||
- | @End | ||
- | |||
- | @OnKnobChange | ||
- | _knob = LastKnob | ||
- | _val = GetKnobValue _knob | ||
- | | ||
- | if _knob = TOGGLE_KNOB | ||
- | if _val = 64 | ||
- | isOn = not isOn | ||
- | SetKnobValue TOGGLE_KNOB, | ||
- | else | ||
- | isOn = _val > 64 | ||
- | endif | ||
- | | ||
- | if isOn | ||
- | LabelKnob TOGGLE_KNOB, | ||
- | else | ||
- | LabelKnob TOGGLE_KNOB, | ||
- | endif | ||
endif | endif | ||
@End | @End | ||
Line 275: | Line 419: | ||
* //isOn = _val > 64// assigns either 0 or 1 to //isOn// depending on _val | * //isOn = _val > 64// assigns either 0 or 1 to //isOn// depending on _val | ||
+ | |||
+ | \\ | ||
+ | ===== Remove or add an entry inside an array ===== | ||
+ | < | ||
+ | Instead of running through an array with a FOR loop to shift entries to either insert or remove an intermedeate enry, it is way faster to use the CopyArray Mozaic function: | ||
+ | |||
+ | < | ||
+ | |||
+ | \\ | ||
+ | === Remove an entry of an array (shift left) === | ||
+ | |||
+ | < | ||
+ | @OnLoad | ||
+ | for i=0 to 6 | ||
+ | a[i] = i | ||
+ | endfor | ||
+ | Log {Before },a[0],{ },a[1],{ },a[2],{ },a[3],{ },a[4],{ },a[5],{ },a[6] | ||
+ | |||
+ | // Remove entry a[2] by left shift of the following entries | ||
+ | CopyArray a[3], a[2], 6 | ||
+ | | ||
+ | Log {After | ||
+ | @End | ||
+ | </ | ||
+ | |||
+ | \\ | ||
+ | === Insert an entry into array (shift right) === | ||
+ | |||
+ | Due to the inner working of CopyArray, the above trick doesn‘t work for right shifts - but one can use a temporary array to be more efficient than iterating over the array: | ||
+ | |||
+ | < | ||
+ | @OnLoad | ||
+ | for i=0 to 6 | ||
+ | a[i] = i | ||
+ | endfor | ||
+ | Log {Before | ||
+ | |||
+ | // Insert a new a[2] | ||
+ | CopyArray a[2], _tmp, 6 | ||
+ | CopyArray _tmp, a[3], 6 | ||
+ | a[2] = 9 | ||
+ | | ||
+ | Log {After | ||
+ | @End | ||
+ | </ | ||
+ | ===== Using Logic Operators in Expressions instead of IF cases ===== | ||
+ | < | ||
+ | |||
+ | Boolean expressions compute to either 1 or 0, which are the internal values for TRUE/FALSE or YES/NO. | ||
+ | Using this behavior in expressions allows to get rid of some IFs, but on the other hand makes the script a bit more complicated to read and understand for novices. | ||
+ | |||
+ | Instead of the lengthy | ||
+ | < | ||
+ | // showHelp | ||
+ | |||
+ | If showHelp | ||
+ | ShowLayout 4 | ||
+ | Else | ||
+ | ShowLayout 2 | ||
+ | Endif | ||
+ | </ | ||
+ | |||
+ | On can write | ||
+ | < | ||
+ | ShowLayout 2 + 2*showHelp | ||
+ | </ | ||
+ | |||
+ | . | ||
+ | |||
+ | Here second example where in the 22 knob layout the left most 8 knobs of both rows are used as channel knobs for channels 1 to 16. Since there are 11 knobs per row, the channel computed for the lower row needs to be adjusted: | ||
+ | < | ||
+ | chan = LastKnob + 1 | ||
+ | If LastKnob >= 8 | ||
+ | chan = chan - 3 | ||
+ | Endif | ||
+ | </ | ||
+ | |||
+ | This can be written shorter as: | ||
+ | < | ||
+ | chan = LastKnob +1 - 3*(LastKnob> | ||
+ | </ | ||
+ | |||
+ | \\ | ||
+ | ===== Using Inc and Dec in Expressions ===== | ||
+ | < | ||
+ | |||
+ | TheOriginalPaulB discovered that Inc and Dec are working as functions. Both Inc and Dec are ‚pre-increment‘ operations like ++var in C, as the function returns the already incremented value. This allows for several interesting language constructs: | ||
+ | |||
+ | In case of a ring buffer index, one could use | ||
+ | < | ||
+ | index = (Inc index) % 64 | ||
+ | </ | ||
+ | instead of the longer construct | ||
+ | |||
+ | < | ||
+ | Inc index | ||
+ | index = index % 64 | ||
+ | </ | ||
+ | |||
+ | \\ | ||
+ | Sometimes this trick is also applicable in array initialization, | ||
+ | < | ||
+ | idx = -1 | ||
+ | for i = 10 to 19 | ||
+ | array[Inc idx] = 2*i | ||
+ | endfor | ||
+ | </ | ||
+ | The example fills array[0] to array[9] with the values 20 to 38. To compensate for pre-increment, | ||
+ | |||
+ | \\ | ||
+ | It is also possible to construct a post-increment operation by using the inc in the expression itself, but discarding it using multiplication with 0: | ||
+ | |||
+ | < | ||
+ | idx = 0 | ||
+ | for i = 10 to 19 | ||
+ | array[idx] = 2*i + 0*(Inc idx) | ||
+ | endfor | ||
+ | </ | ||
\\ | \\ | ||
Line 293: | Line 555: | ||
@End | @End | ||
</ | </ | ||
+ | |||
+ | |||
+ | \\ | ||
+ | ===== Include Snippets ===== | ||
+ | < | ||
+ | |||
+ | On PatchStorage you can find several ' | ||
+ | |||
+ | The snippets can be seen as 'well tested library functions' | ||
+ | The snippet in turn will later call event that you need to define in your code (callbacks). | ||
+ | All of the current available snippets need a timer event to do their work, the snippets are coded in a way that allows to also to work with a timer event that you defined in your script. | ||
+ | |||
+ | I tried to make them as ' | ||
+ | |||
+ | ^PatchStorage | ||
+ | | [[https:// | ||
+ | | [[https:// | ||
+ | | [[https:// |