moontide-mcp 2.1.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +54 -331
  2. package/dist/ask.js +59 -12
  3. package/dist/catalog.js +118 -9
  4. package/dist/instruments.js +83 -9
  5. package/dist/mcp.js +146 -20
  6. package/dist/midi-state.js +43 -1
  7. package/dist/protocol.js +123 -5
  8. package/dist/reference.js +12 -0
  9. package/dist/request-status.js +221 -0
  10. package/dist/session.js +203 -7
  11. package/dist/vocabulary.js +420 -0
  12. package/package.json +3 -2
  13. package/page/ask-view.js +118 -50
  14. package/page/ask.js +139 -169
  15. package/page/bar-model.js +44 -2
  16. package/page/change-timing.js +35 -0
  17. package/page/index.html +6 -2
  18. package/page/instruments-model.js +50 -3
  19. package/page/instruments-view.js +14 -2
  20. package/page/jam-analysis.js +68 -23
  21. package/page/jam-model.js +185 -29
  22. package/page/jam-resolve.js +271 -0
  23. package/page/jam-view.js +165 -10
  24. package/page/lights.js +29 -7
  25. package/page/main.js +807 -66
  26. package/page/midi-clock.js +22 -3
  27. package/page/midi-in.js +407 -0
  28. package/page/midi-out.js +557 -48
  29. package/page/midi-plan.js +42 -6
  30. package/page/midi-rig.js +286 -13
  31. package/page/midi-settings.js +507 -0
  32. package/page/mixer-view.js +7 -1
  33. package/page/note-panel.js +29 -8
  34. package/page/note-tag.js +19 -4
  35. package/page/page-rewrite.js +1 -1
  36. package/page/played-buffer.js +208 -0
  37. package/page/query-check.js +21 -2
  38. package/page/request-status-view.js +419 -0
  39. package/page/request-status.js +686 -0
  40. package/page/scratch-gesture.js +239 -0
  41. package/page/scratch-listen.js +260 -0
  42. package/page/scratch-model.js +1696 -0
  43. package/page/scratch-view.js +2176 -0
  44. package/page/song-grid-model.js +15 -0
  45. package/page/song-grid-view.js +1 -1
  46. package/page/structure.js +2 -1
  47. package/page/style.css +1243 -43
  48. package/page/tempo-glide.js +330 -0
  49. package/page/tempo-guard.js +62 -10
  50. package/page/timeline-edit-view.js +1 -1
  51. package/page/timeline.js +2 -1
  52. package/page/top-bar-view.js +18 -13
  53. package/page/views.js +20 -7
  54. package/reference/midi.md +2 -0
  55. package/reference/motif.md +83 -0
  56. package/reference/settings.md +161 -0
  57. package/reference/transitions.md +13 -1
@@ -0,0 +1,83 @@
1
+ # Motif requests
2
+
3
+ Applies to @strudel/repl 1.3.0, the version the Moontide page loads (it bundles @strudel/core 1.2.6, @strudel/mini 1.2.6, @strudel/tonal 1.2.6, @strudel/transpiler 1.2.6, @strudel/webaudio 1.3.0 and @strudel/soundfonts 1.3.0).
4
+
5
+ How to read a motif request: the fields it holds, the moment, the grid, the phrase form, the velocities and the source words. Every fact here is a plain possibility, not a suggestion of what to do with a motif.
6
+
7
+ ## What a motif request holds
8
+
9
+ A motif is a short pattern of notes made on the Scratchpad of the page. The user sends it to Claude with the Send to Claude button, as a page request whose context has kind motif. The request text is the user's words, prefilled with a short default sentence they can change. The motif rides in the motif field, which holds these names:
10
+
11
+ ```names
12
+ source cycle cycles grid notes phrase
13
+ ```
14
+
15
+ source says how the notes got onto the Scratchpad. cycle is the moment and may be absent. cycles is the length of the motif in cycles, from 1 to 8. grid is the grid of the steps. notes is how many notes the motif holds. phrase is the motif written as Strudel code.
16
+
17
+ ## The moment
18
+
19
+ For a played motif, cycle is the cycle of the music where the motif's first cycle was played. For any other motif, cycle is the cycle where the first cycle of the roll stood when the user pressed the button, a whole multiple of the cycles of the motif.
20
+
21
+ An absent cycle means the motif has no moment: the music was stopped, or the notes of a played motif came from more than one run of the music.
22
+
23
+ A pattern repeats from the start of the count, so a phrase that spans its cycles starts again at every multiple of them. A time shift moves it; the functions topic of get_strudel_reference lists the time functions.
24
+
25
+ ## The grid
26
+
27
+ The grid is one of five ids:
28
+
29
+ ```names
30
+ quarter eighth sixteenth eighth-triplet sixteenth-triplet
31
+ ```
32
+
33
+ The steps of each grid in one cycle:
34
+
35
+ - `quarter`: 4 steps per cycle
36
+ - `eighth`: 8 steps per cycle
37
+ - `sixteenth`: 16 steps per cycle
38
+ - `eighth-triplet`: 12 steps per cycle
39
+ - `sixteenth-triplet`: 24 steps per cycle
40
+
41
+ A cycle holds 4 beats. Every note starts on a step and lasts a whole number of steps.
42
+
43
+ ## The phrase
44
+
45
+ One lane is `note("…").velocity("…")`, and several lanes are `stack(…)` of lanes. The ellipses stand for the strings of a lane; this topic gives no example phrase.
46
+
47
+ - A string holds one entry per step of the whole motif, from its first step to its last.
48
+ - `~` is a step where no note of that lane starts.
49
+ - `_` is a step the note before still holds.
50
+ - Voices that start together with the same length and velocity are one entry in square brackets with commas between the pitch names.
51
+ - Voices that differ go in separate lanes.
52
+ - A motif of more than one cycle is one sequence in square brackets followed by a slash and the value of cycles, so a note can be held across the line between two cycles.
53
+ - The velocity string mirrors the note string entry for entry: a note or a chord has its velocity at the same place, and ~ and _ stay the same.
54
+ - Pitch names are lowercase letters from a to g, with # for a sharp and the octave number after them; MIDI note 60 is written c4.
55
+
56
+ The phrase is the whole motif, and it is the text the page showed the user. The mini-notation topic of get_strudel_reference lists every symbol of the strings.
57
+
58
+ ## Velocities
59
+
60
+ A velocity runs from 0.01 to 1, written with at most two decimals. On a MIDI track `.gain()` multiplies the velocity; the midi topic of get_strudel_reference has the MIDI facts.
61
+
62
+ ## How the notes got there
63
+
64
+ The source words:
65
+
66
+ ```names
67
+ played drawn tapped mixed
68
+ ```
69
+
70
+ - played: the notes were played on a MIDI keyboard and put on the roll.
71
+ - drawn: the notes were placed on the roll by the user.
72
+ - tapped: the notes were tapped in time with the music.
73
+ - mixed: the notes got there in more than one of these ways.
74
+
75
+ Played notes can include very short, very quiet notes that the player may not have meant.
76
+
77
+ ## What the words can ask
78
+
79
+ The user's words decide what a motif request asks for. The motif can become a new labeled track, a change to a track that plays, or a replacement of what plays, or the answer can be in words. The page asks for none of these by itself.
80
+
81
+ ## What the page did
82
+
83
+ The page changed no code and no version. The music plays as it did until a change is written with set_code or edit_code. A change that passes the id of the request as answering links to it, and answer_request closes the request in words.
@@ -0,0 +1,161 @@
1
+ # Synth settings
2
+
3
+ Applies to @strudel/repl 1.3.0, the version the Moontide page loads (it bundles @strudel/core 1.2.6, @strudel/mini 1.2.6, @strudel/tonal 1.2.6, @strudel/transpiler 1.2.6, @strudel/webaudio 1.3.0 and @strudel/soundfonts 1.3.0).
4
+
5
+ What `.midi()` can do to set the settings of an instrument: the tone, part and scene options, as the Moontide page reads, checks and sends them. Every fact here is a plain possibility, not a suggestion of when to use it.
6
+
7
+ ## Where the names come from
8
+
9
+ Only an instrument whose model describes settings takes them: get_midi_instruments lists, under the Settings line of that instrument, the names and ranges each level takes, with the tone names given per engine. An instrument with no Settings line takes no setting.
10
+
11
+ The tone settings a sound takes are those of its engine. On an instrument whose model declares engines, search_midi_sounds and get_midi_instruments with instrument set give the engine of each sound or say it is in no engine, and a sound in no engine takes no tone setting. A catalog without an engine table places no sound in an engine.
12
+
13
+ A tone name may be a setting of several engines, each with its own range, and the engine of the sound in the call decides which one is sent; the code is written the same way whichever engine the sound has.
14
+
15
+ The names come from one vocabulary, listed below, and an instrument takes only the names its Settings line lists.
16
+
17
+ The tone names:
18
+
19
+ ```names
20
+ osc1Wave osc1Feet osc1Tune osc1Level osc2Wave osc2Feet osc2Tune osc2Level filterType filterCutoff filterResonance
21
+ filterEnvDepth env1Attack env1Decay env1Sustain env1Release env2Attack env2Decay env2Sustain env2Release lfoRate
22
+ lfoOscDepth lfoFilterDepth algorithm feedback partial1Cutoff toneCoarseTune partialMute partial1Level op1Level op1Ratio
23
+ op1Detune op1Attack op1Decay op1Sustain op1Release op2Level op2Ratio op2Detune op2Attack op2Decay op2Sustain op2Release
24
+ op3Level op3Ratio op3Detune op3Attack op3Decay op3Sustain op3Release op4Level op4Ratio op4Detune op4Attack op4Decay
25
+ op4Sustain op4Release op5Level op5Ratio op5Detune op5Attack op5Decay op5Sustain op5Release op6Level op6Ratio op6Detune
26
+ op6Attack op6Decay op6Sustain op6Release structure12 structure34 envMode partial1CoarseTune partial1FineTune
27
+ partial1PitchKeyFollow partial1BendSwitch partial1Wave partial1PcmWave partial1PulseWidth partial1PulseWidthVelocity
28
+ partial1PitchEnvDepth partial1PitchEnvVelocity partial1PitchEnvTimeKeyFollow partial1PitchEnvTime1 partial1PitchEnvTime2
29
+ partial1PitchEnvTime3 partial1PitchEnvTime4 partial1PitchEnvLevel0 partial1PitchEnvLevel1 partial1PitchEnvLevel2
30
+ partial1PitchEnvSustain partial1PitchEnvEnd partial1PitchLfoRate partial1PitchLfoDepth partial1PitchLfoModSens
31
+ partial1Resonance partial1CutoffKeyFollow partial1FilterBiasPoint partial1FilterBiasLevel partial1FilterEnvDepth
32
+ partial1FilterEnvVelocity partial1FilterEnvDepthKeyFollow partial1FilterEnvTimeKeyFollow partial1FilterEnvTime1
33
+ partial1FilterEnvTime2 partial1FilterEnvTime3 partial1FilterEnvTime4 partial1FilterEnvTime5 partial1FilterEnvLevel1
34
+ partial1FilterEnvLevel2 partial1FilterEnvLevel3 partial1FilterEnvSustain partial1LevelVelocity partial1AmpBiasPoint1
35
+ partial1AmpBiasLevel1 partial1AmpBiasPoint2 partial1AmpBiasLevel2 partial1AmpEnvTimeKeyFollow partial1AmpEnvTimeVelocity
36
+ partial1AmpEnvTime1 partial1AmpEnvTime2 partial1AmpEnvTime3 partial1AmpEnvTime4 partial1AmpEnvTime5 partial1AmpEnvLevel1
37
+ partial1AmpEnvLevel2 partial1AmpEnvLevel3 partial1AmpEnvSustain partial2CoarseTune partial2FineTune
38
+ partial2PitchKeyFollow partial2BendSwitch partial2Wave partial2PcmWave partial2PulseWidth partial2PulseWidthVelocity
39
+ partial2PitchEnvDepth partial2PitchEnvVelocity partial2PitchEnvTimeKeyFollow partial2PitchEnvTime1 partial2PitchEnvTime2
40
+ partial2PitchEnvTime3 partial2PitchEnvTime4 partial2PitchEnvLevel0 partial2PitchEnvLevel1 partial2PitchEnvLevel2
41
+ partial2PitchEnvSustain partial2PitchEnvEnd partial2PitchLfoRate partial2PitchLfoDepth partial2PitchLfoModSens
42
+ partial2Cutoff partial2Resonance partial2CutoffKeyFollow partial2FilterBiasPoint partial2FilterBiasLevel
43
+ partial2FilterEnvDepth partial2FilterEnvVelocity partial2FilterEnvDepthKeyFollow partial2FilterEnvTimeKeyFollow
44
+ partial2FilterEnvTime1 partial2FilterEnvTime2 partial2FilterEnvTime3 partial2FilterEnvTime4 partial2FilterEnvTime5
45
+ partial2FilterEnvLevel1 partial2FilterEnvLevel2 partial2FilterEnvLevel3 partial2FilterEnvSustain partial2Level
46
+ partial2LevelVelocity partial2AmpBiasPoint1 partial2AmpBiasLevel1 partial2AmpBiasPoint2 partial2AmpBiasLevel2
47
+ partial2AmpEnvTimeKeyFollow partial2AmpEnvTimeVelocity partial2AmpEnvTime1 partial2AmpEnvTime2 partial2AmpEnvTime3
48
+ partial2AmpEnvTime4 partial2AmpEnvTime5 partial2AmpEnvLevel1 partial2AmpEnvLevel2 partial2AmpEnvLevel3
49
+ partial2AmpEnvSustain partial3CoarseTune partial3FineTune partial3PitchKeyFollow partial3BendSwitch partial3Wave
50
+ partial3PcmWave partial3PulseWidth partial3PulseWidthVelocity partial3PitchEnvDepth partial3PitchEnvVelocity
51
+ partial3PitchEnvTimeKeyFollow partial3PitchEnvTime1 partial3PitchEnvTime2 partial3PitchEnvTime3 partial3PitchEnvTime4
52
+ partial3PitchEnvLevel0 partial3PitchEnvLevel1 partial3PitchEnvLevel2 partial3PitchEnvSustain partial3PitchEnvEnd
53
+ partial3PitchLfoRate partial3PitchLfoDepth partial3PitchLfoModSens partial3Cutoff partial3Resonance
54
+ partial3CutoffKeyFollow partial3FilterBiasPoint partial3FilterBiasLevel partial3FilterEnvDepth partial3FilterEnvVelocity
55
+ partial3FilterEnvDepthKeyFollow partial3FilterEnvTimeKeyFollow partial3FilterEnvTime1 partial3FilterEnvTime2
56
+ partial3FilterEnvTime3 partial3FilterEnvTime4 partial3FilterEnvTime5 partial3FilterEnvLevel1 partial3FilterEnvLevel2
57
+ partial3FilterEnvLevel3 partial3FilterEnvSustain partial3Level partial3LevelVelocity partial3AmpBiasPoint1
58
+ partial3AmpBiasLevel1 partial3AmpBiasPoint2 partial3AmpBiasLevel2 partial3AmpEnvTimeKeyFollow partial3AmpEnvTimeVelocity
59
+ partial3AmpEnvTime1 partial3AmpEnvTime2 partial3AmpEnvTime3 partial3AmpEnvTime4 partial3AmpEnvTime5 partial3AmpEnvLevel1
60
+ partial3AmpEnvLevel2 partial3AmpEnvLevel3 partial3AmpEnvSustain partial4CoarseTune partial4FineTune
61
+ partial4PitchKeyFollow partial4BendSwitch partial4Wave partial4PcmWave partial4PulseWidth partial4PulseWidthVelocity
62
+ partial4PitchEnvDepth partial4PitchEnvVelocity partial4PitchEnvTimeKeyFollow partial4PitchEnvTime1 partial4PitchEnvTime2
63
+ partial4PitchEnvTime3 partial4PitchEnvTime4 partial4PitchEnvLevel0 partial4PitchEnvLevel1 partial4PitchEnvLevel2
64
+ partial4PitchEnvSustain partial4PitchEnvEnd partial4PitchLfoRate partial4PitchLfoDepth partial4PitchLfoModSens
65
+ partial4Cutoff partial4Resonance partial4CutoffKeyFollow partial4FilterBiasPoint partial4FilterBiasLevel
66
+ partial4FilterEnvDepth partial4FilterEnvVelocity partial4FilterEnvDepthKeyFollow partial4FilterEnvTimeKeyFollow
67
+ partial4FilterEnvTime1 partial4FilterEnvTime2 partial4FilterEnvTime3 partial4FilterEnvTime4 partial4FilterEnvTime5
68
+ partial4FilterEnvLevel1 partial4FilterEnvLevel2 partial4FilterEnvLevel3 partial4FilterEnvSustain partial4Level
69
+ partial4LevelVelocity partial4AmpBiasPoint1 partial4AmpBiasLevel1 partial4AmpBiasPoint2 partial4AmpBiasLevel2
70
+ partial4AmpEnvTimeKeyFollow partial4AmpEnvTimeVelocity partial4AmpEnvTime1 partial4AmpEnvTime2 partial4AmpEnvTime3
71
+ partial4AmpEnvTime4 partial4AmpEnvTime5 partial4AmpEnvLevel1 partial4AmpEnvLevel2 partial4AmpEnvLevel3
72
+ partial4AmpEnvSustain keyShift toneFineTune bendRange assignMode toneReverbSwitch
73
+ ```
74
+
75
+ The part names:
76
+
77
+ ```names
78
+ level pan chorusSend reverbSend delaySend vibratoRateOffset vibratoDepthOffset vibratoDelayOffset cutoffOffset
79
+ resonanceOffset attackOffset decayOffset releaseOffset
80
+ ```
81
+
82
+ The scene names:
83
+
84
+ ```names
85
+ level chorusSwitch chorusType chorusLevel delaySwitch delayType delayLevel reverbSwitch reverbType reverbLevel
86
+ driveAmount driveSwitch masterTune reverbTime
87
+ ```
88
+
89
+ ## Writing them
90
+
91
+ The form is the tone, part and scene options of `.midi()`, each a plain object of names and values; every value below is a placeholder:
92
+
93
+ ```js
94
+ note("c3 e3").midi('port name', { midichannel: channel, sound: 'sound name', tone: { name: value }, part: { name: value }, scene: { name: value } })
95
+ ```
96
+
97
+ The level is the key of the option: a setting is tone, part or scene by where it is written, and each level is optional.
98
+
99
+ Each level is a plain object of names and whole numbers written as number literals in the call; a variable, a pattern or a value worked out while the code runs is refused.
100
+
101
+ Values are in the instrument's own units, inside the range get_midi_instruments lists for the name.
102
+
103
+ A setting is one value for the whole evaluation, never one per note.
104
+
105
+ ## What each level reaches
106
+
107
+ Tone reaches the tone of the part that the channel of the call plays.
108
+
109
+ On an instrument whose model declares engines, tone needs a sound named in the same call, and the engine of that sound must have the setting.
110
+
111
+ Part reaches that part only.
112
+
113
+ Scene reaches every part of the instrument, not only the channel of the call that carries it, and the reply says so.
114
+
115
+ A name belongs to one level, except `level`, which both part and scene have and which is a different setting at each: under part it is the level of that part, under scene the level of the whole scene.
116
+
117
+ A name ending in Offset moves the value of the instrument itself up or down, so its lowest value is the largest move down and never off or closed; the value that changes nothing is given with the instrument.
118
+
119
+ ## What is refused
120
+
121
+ A name the level does not have is refused, and the refusal lists the names that exist.
122
+
123
+ A name written at the wrong level is refused, and the refusal says the level it belongs to.
124
+
125
+ A value outside the range is refused, and the refusal gives the range.
126
+
127
+ Two values of one setting for one channel, or of one scene setting for one instrument, are refused, and the refusal names both.
128
+
129
+ Tone settings with no sound in the call, on a sound in no engine, or on a sound of another engine are refused, and each refusal gives its own reason; only the refusal for a sound of another engine names the engines, the one of the sound and those of the setting.
130
+
131
+ A setting Moontide cannot send safely to that instrument is refused, and the refusal says why.
132
+
133
+ A refused evaluation keeps the music that was playing and sends nothing.
134
+
135
+ ## When they are sent
136
+
137
+ Every evaluation sends every setting the code writes, not only what changed.
138
+
139
+ Each channel's bank select and program change go first, then its tone and part settings, and the scene settings follow after all the channels.
140
+
141
+ On an instrument whose model maps a setting to an NRPN, the setting goes out on the channel of its call as that NRPN, and Moontide never sends a value back by itself.
142
+
143
+ A change that waits for a bar or phrase line sends its settings at the line, with its sound.
144
+
145
+ They are sent again, with the sound, when the instrument is plugged in again.
146
+
147
+ ## Nothing is stored
148
+
149
+ Moontide stores no setting: the code is the only record.
150
+
151
+ A setting taken out of the code is not sent again and nothing undoes it; the instrument keeps the last value, and the reply says so.
152
+
153
+ A new sound on a channel loads that tone again, which undoes the channel's tone settings, and the tone settings still in the code are sent right after its program change.
154
+
155
+ ## What the instrument undoes
156
+
157
+ On the instrument, what undoes a setting differs from one instrument to another: the Notes line of the instrument in get_midi_instruments says what undoes each of its settings and what was heard on it.
158
+
159
+ ## System exclusive messages from the code
160
+
161
+ The MIDI messages the code sends itself are checked just before they leave, on every port, whatever instruments are described. A Roland system exclusive message of a model whose temporary areas Moontide knows is sent only as a complete data set whose whole address range lies inside those areas; otherwise the whole send is dropped and the reply gives the reason. A send whose data cannot be read as byte-like values is dropped too. System exclusive messages of other makers and of other Roland models pass.
@@ -133,8 +133,20 @@ A tempo set by the waiting code with `setcps` or `setcpm` applies in the schedul
133
133
 
134
134
  ## Tempo
135
135
 
136
- `setcps(n)` and `setcpm(n)` (or the chained `.cpm(n)`, see reference/functions.md "Tempo") take effect the instant the code is evaluated — each is a single value read once at that moment, not a control that is queried continuously like a signal. In a change that waits for a bar or phrase line, they take effect at the line (see "Taking over on a bar or phrase line", above). Whether tempo itself can follow a continuous signal: this bundle's own `setcps`/`setcpm` accept a plain number, and unwrap a single-value ("pure") pattern into that same plain number if given one, but do not continuously sample a changing signal.
136
+ `setcps(n)` and `setcpm(n)` (or the chained `.cpm(n)`, see reference/functions.md "Tempo") take effect the instant the code is evaluated — each is a single value read once at that moment, not a control that is queried continuously like a signal. In a change that waits for a bar or phrase line, they take effect at the line (see "Taking over on a bar or phrase line", above). Whether tempo itself can follow a continuous signal: this bundle's own `setcps`/`setcpm` accept a plain number, and unwrap a single-value ("pure") pattern into that same plain number if given one, but do not continuously sample a changing signal. A smooth change of tempo is asked for with `glideBars` (see "Tempo ramp", below).
137
137
 
138
138
  ```js
139
139
  setcpm(120) // takes effect immediately when this code evaluates, then stays fixed
140
140
  ```
141
+
142
+ ## Tempo ramp
143
+
144
+ `set_code` and `edit_code` take `glideBars`, a whole number of bars from 1 to 64, to move the tempo smoothly instead of jumping. The code states the tempo to reach with `setcps` or `setcpm`. The tempo moves from the tempo that plays to that tempo over that many bars, in equal small steps of BPM, and ends exactly on the code's tempo. It starts at once, or at the line when `at` is `'bar'` or `'phrase'`, from the tempo that plays then.
145
+
146
+ A ramp is refused, and nothing changes, when nothing plays or the music is stopped, when the code sets no tempo, and when the code sets the tempo that already plays. Send the change again without `glideBars`.
147
+
148
+ A ramp goes on through any later program that states the same tempo again, whatever its source (a Mixer move, a Song grid edit, a Timeline edit, a hand edit, `set_code`, `edit_code`, `restore_version`), and through a program that fails. A program that states another tempo cancels it, and that tempo plays at once. A program with no tempo statement cancels it, and the tempo stays where it was. A new `glideBars` replaces it, from the tempo that plays then. Stop cancels it, and the next Play starts at the code's tempo. A page reload, or another tab taking over, ends it, and the code's tempo plays.
149
+
150
+ The reply to a change states the ramp once. A ramp that ended early is told once, in a later reply; a ramp that finished is not announced. A ramp is an event of the page, never a version: it writes no tempo into the code or the history.
151
+
152
+ A pattern's `.cpm()` is worked out once, when the program runs, against the tempo at that moment (the code's own tempo when its `setcps` or `setcpm` comes first), and its length in cycles stays fixed after that, so it moves with a ramp like the rest of the music. The MIDI clock follows every step of a ramp. How a hardware instrument that follows the MIDI clock reacts to a ramp has not been verified.