-
Notifications
You must be signed in to change notification settings - Fork 1
/
Chordpeggio
324 lines (309 loc) · 9.62 KB
/
Chordpeggio
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
// Chordpeggio, by rrc2soft. v1.1
// AU Parameter User 0: Change current preset
@Description
Chordpeggio v1.1 by rrc2soft
Input four notes using the keyboard, which (with user led not flashing) will be ordered from lower to higher.
Then, every new bar and for every note, the chordpeggio will play as many notes as their respective NPM (notes per measure).
Shift knobs change when the notes are played. The Size knob indicates the duration of the notes. Finally, use the Random knob to give the chordpeggio a chance to evolve.
Use the Left Pads to choose the current preset.
Use SHIFT to change input mode (OFF: order notes, ON: do not order notes).
@End
@OnLoad
//VARIABLES
// Initialize array of temporal notes, to be saved to the ordered notes
FillArray tmpNotes, 0
FillArray tmpVelocity, 0
FillArray tmpChannel, 0
tmpNotesStored = 0
// Initialize the current preset
// Only if unassigned
if Unassigned preset
preset = 0
pFactor = 0
order = YES
FillArray pKnobs, 64
FillArray pX, 64
FillArray pY, 64
endif
// Initialize arrays used in the app
// Those with *preset depend on the preset, and need to use
// pFactor to access the array.
// The others are calculated from the knobs, and there is no
// need to use pFactor.
// Only if unassigned
if Unassigned notesOrderedPreset
FillArray notesOrderedPreset, 0 // ordered notes,[0]low,[3]high
FillArray velocityOrderedPreset, 0 // velocity of ordered notes
FillArray channelOrderedPreset, 0 // channel of ordered notes
FillArray notesOrderedFullPreset, NO
FillArray knobValue, 0 // measures where notes are placed
FillArray knobVariance, 0 // shift knobValues around
FillArray barVariance, 0 // knobVariance every measure
noteSize = 1 // overall size of the note (1 = 100%)
rndFactor = 0 // Chance (rnd*3) to change the Variance
endif
//GUI - Basic
// We will actually update the GUI labels every 125ms
//...if needed, of course
LabelKnobs {Chordpeggio Parameters}
LabelXY {X (Size) Y (Randomness Factor)}
LabelPads {Presets}
SetShortName {ChArp}
//OPERATIONS
SetMetroPPQN 4
// FINAL (Timer for GUI and changes to internal arrays)
// Internal variables will be updated in the Timer
// ...and yes, this is a hack to enable a single function call :-P and a couple of things more
SetTimerInterval 125
StartTimer
updatePRESET = YES
@End
@OnTimer
// In “order notes” mode, flash the LEDs
if not order
FlashUserLed
endif
// First, in case of preset change, we change everything
if (updatePRESET)
for _it = 0 to 3
LatchPad _it, (_it = preset)
endfor
for _it = 0 to 9
SetKnobValue _it, pKnobs[pFactor + _it]
endfor
SetXYValues pX[preset], pY[preset]
updateXY = YES
updateKNOB = YES
// This task is done
updatePRESET = NO
endif
// We will update the internal arrays, variables and GUI here.
// This script does not play live notes, thus we can
// do this to improve script readability.
if (updateXY)
// noteSize
_knob = GetKnobValue 4
if (_knob < 60)
noteSize = 6 - RoundDown(_knob / 10)
noteSize = (10 - noteSize) * 0.1
elseif (_knob > 68)
noteSize = 1 + RoundDown((_knob - 69) / 10)
noteSize = (10 - noteSize) * 0.1
else
noteSize = 1
endif
// rndFactor
_knob = GetKnobValue 9
if (_knob < 60)
rndFactor = 6 - RoundDown(_knob / 10)
elseif (_knob > 68)
rndFactor = 1 + RoundDown((_knob - 69) / 10)
else
rndFactor = 0
endif
// noteSize GUI
if (noteSize = 0.9)
LabelKnob 4, {Size: 0.9}
elseif (noteSize = 0.8)
LabelKnob 4, {Size: 0.8}
elseif (noteSize = 0.7)
LabelKnob 4, {Size: 0.7}
elseif (noteSize = 0.6)
LabelKnob 4, {Size: 0.6}
elseif (noteSize = 0.5)
LabelKnob 4, {Size: 0.5}
elseif (noteSize = 0.4)
LabelKnob 4, {Size: 0.4}
else
LabelKnob 4, {Size:}, noteSize
endif
// rndFactor GUI
LabelKnob 9, {Rnd:}, rndFactor
// No more updates
updateXY = NO
endif
if (updateKNOB)
// knobValue, var and GUI
for _curr = 0 to 3
_knob = GetKnobValue _curr
if (_knob <= 32)
LabelKnob _curr , {NPM: 1}
knobValue[_curr] = 16
elseif (_knob <= 64)
LabelKnob _curr , {NPM: 2}
knobValue[_curr] = 8
elseif (_knob > 96)
LabelKnob _curr , {NPM: 4}
knobValue[_curr] = 4
else
LabelKnob _curr , {NPM: 3}
knobValue[_curr] = 6
endif
endfor
// knobVariance, barVariance, var and GUI
for _curr = 5 to 8
_knob = GetKnobValue _curr
if (_knob <= 20)
LabelKnob _curr , {Shift: -3}
knobVariance[_curr - 5] = -3
elseif (_knob <= 40)
LabelKnob _curr , {Shift: -2}
knobVariance[_curr - 5] = -2
elseif (_knob <= 60)
LabelKnob _curr , {Shift: -1}
knobVariance[_curr - 5] = -1
elseif (_knob >= 108)
LabelKnob _curr , {Shift: 3}
knobVariance[_curr - 5] = 3
elseif (_knob > 88)
LabelKnob _curr , {Shift: 2}
knobVariance[_curr - 5] = 2
elseif (_knob > 68)
LabelKnob _curr , {Shift: 1}
knobVariance[_curr - 5] = 1
else
LabelKnob _curr , {Shift: 0}
knobVariance[_curr - 5] = 0
endif
barVariance[_curr - 5] = knobVariance[_curr - 5]
endfor
// No more updates
updateKNOB = NO
endif
@End
@OnMidiNoteOn
if (tmpNotesStored = 4)
// Start from the beginning storing notes
tmpNotesStored = 0
endif
if (tmpNotesStored < 4)
// Save new note
tmpNotes[tmpNotesStored] = MIDINote
tmpVelocity[tmpNotesStored] = MIDIVelocity
tmpChannel[tmpNotesStored] = MIDIChannel
tmpNotesStored = tmpNotesStored + 1
if (tmpNotesStored = 4)
if order
// Copy the notes to the notesOrderedArray, ordered
for _itOrd = 3 to 0
_highestNote = 0
_index = 0
for _itTmp = 0 to 3
if (tmpNotes[_itTmp] >= _highestNote)
_index = _itTmp
_highestNote = tmpNotes[_itTmp]
_highestNoteVel = tmpVelocity[_itTmp]
_highestNoteCh = tmpChannel[_itTmp]
endif
endfor
notesOrderedPreset[pFactor + _itOrd] = _highestNote
velocityOrderedPreset[pFactor + _itOrd] = _highestNoteVel
channelOrderedPreset[pFactor + _itOrd] = _highestNoteCh
tmpNotes[_index] = 0
endfor
else
// Do not order the notes
for _itOrd = 0 to 3
_highestNote = tmpNotes[_itOrd]
_highestNoteVel = tmpVelocity[_itOrd]
_highestNoteCh = tmpChannel[_itOrd]
notesOrderedPreset[pFactor + _itOrd] = _highestNote
velocityOrderedPreset[pFactor + _itOrd] = _highestNoteVel
channelOrderedPreset[pFactor + _itOrd] = _highestNoteCh
endfor
endif
// Now we have all the notes we need
notesOrderedFullPreset[preset] = YES
endif
endif
@End
@OnMetroPulse
if notesOrderedFullPreset[preset] = YES
for _it = 0 to 3
_playNote = NO
if CurrentMetroPulse = 0
// Play the note in measure 0
// Also, change the bar variance according to random
barVariance[_it] = knobVariance[_it]
_playNote = YES
if ((Random 0, 99) < (rndFactor * 3))
barVariance[_it] = barVariance[_it] + ((Random 0, 4) - 2)
barVariance[_it] = Clip barVariance[_it], -3, 3
endif
else
// Play the note only on its corresponding pulse
_pulse = CurrentMetroPulse - barVariance[_it]
if _pulse > 0 and _pulse < 16
if _pulse % knobValue[_it] = 0
_playNote = YES
endif
endif
endif
if (_playNote = YES)
// Play the note for a duration changed by noteSize
_dur = knobValue[_it] * noteSize
if (_dur + CurrentMetroPulse >= 16)
_dur = 16 - CurrentMetroPulse
endif
_note = notesOrderedPreset[pFactor + _it]
_vel = velocityOrderedPreset[pFactor + _it]
_ch = channelOrderedPreset[pFactor + _it]
SendMIDINoteOn _ch, _note, _vel, 0
SendMIDINoteOff _ch, _note, 0, (_dur * (QuarterNote / 4)) - 0.1
endif
endfor
endif
@End
@OnKnobChange
// Update preset Value
pKnobs[LastKnob + pFactor] = GetKnobValue LastKnob
// Change both labels and internal values
updateKNOB = YES
// Special case with knobs 4 and 9: update XY
_curr = LastKnob
if (_curr = 4 or _curr = 9)
// Update XY
SetXYValues (GetKnobValue 4), (GetKnobValue 9)
pX[preset] = GetKnobValue 4
pY[preset] = GetKnobValue 9
// Change both labels and internal values
updateXY = YES
endif
@End
@OnXYChange
// Special case: Update the knobs
SetKnobValue 4, GetXValue
SetKnobValue 9, GetYValue
pKnobs[pFactor + 4] = GetXValue
pKnobs[pFactor + 9] = GetYValue
// Update preset value
pX[preset] = GetXValue
pY[preset] = GetYValue
// Change both labels and internal values
updateKNOB = YES
updateXY = YES
@End
@OnPadDown
// Change the current preset, updating AU parameter
preset = LastPad
pFactor = preset * 10
updatePRESET = YES
SetAUParameter 0, preset * 32
@End
@OnShiftDown
// Change order mode
order = not order
@End
@OnAUParameter
// AU parameter User0: Change the presets
// As User0 is 0-127, we will consider intervals of 32
if (LastAUParameter = 0)
_newPreset = RoundDown ((GetAUParameter LastAUParameter) / 32)
if (_newPreset >= 0 and _newPreset <= 3 and _newPreset <> preset)
// Change preset
preset = _newPreset
pFactor = preset * 10
updatePRESET = YES
endif
endif
@End