-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathgurdystring.cpp
347 lines (284 loc) · 11.7 KB
/
gurdystring.cpp
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
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
#include "gurdystring.h"
/// @brief Constructor. GurdyString manages turning "strings" on and off, determining its note, and interacting with the MIDI layer.
/// @param my_channel The MIDI channel to communicate over
/// @param my_note The base MIDI note of this string (0-127)
/// @param my_name A text label for this string (e.g. "Drone")
/// @param my_mode The secondary output mode (see setOutputMode() for more info)
/// @param my_vol The volume of this string (0-127)
GurdyString::GurdyString(int my_channel, int my_note, String my_name, int my_mode, int my_vol) {
midi_channel = my_channel;
name = my_name;
open_note = my_note;
midi_volume = my_vol;
trigger_volume = int((my_vol)/128.0 * 80 - 70);
note_being_played = open_note;
gros_mode = 0;
setOutputMode(my_mode);
clearVolArray();
};
// soundOn() sends sound on this string's channel at its notes
// optionally with an additional offset (e.g. a key being pressed)
//
// I DO NOT KNOW why I can just access usbMIDI here, but even when
// creating MIDI_obj globally, I could not access MIDI_obj the same way.
// Bringing in a pointer and working with MIDI_obj in this manner is
// because of that.
/// @brief Turns on sound over this string's MIDI channel at its current volume
/// @param my_offset The offset from the string's base note to make sound
/// @param my_modulation The amount of optional modulation (0-127) to apply to the sound. This is MIDI CC1. 0 == no modulation.
/// @warning The way this is currently written, only one note may be playing per string object. Don't call this twice in a row without calling soundOff() first.
void GurdyString::soundOn(int my_offset, int my_modulation) {
note_being_played = open_note + my_offset;
// If user has one of the second-drone options enabled, trigger them here.
if (gros_mode == 1) {
soundOn(0, 0, note_being_played - 5);
} else if (gros_mode == 2) {
soundOn(0, 0, note_being_played - 7);
} else if (gros_mode == 3) {
soundOn(0, 0, note_being_played - 12);
};
if (!mute_on) {
usbMIDI.sendNoteOn(note_being_played, midi_volume, midi_channel);
if (output_mode != 1) {
MIDI.sendNoteOn(note_being_played, midi_volume, midi_channel);
};
if (output_mode > 0) {
#if defined(USE_TRIGGER)
if (vol_array[note_being_played] != trigger_volume) {
trigger_obj.trackGain(note_being_played + (128 * (midi_channel - 1)), trigger_volume);
vol_array[note_being_played] = trigger_volume;
};
trigger_obj.trackPlayPoly(note_being_played + (128 * (midi_channel - 1)), true);
//trigger_obj.trackLoop(note_being_played + (128 * (midi_channel - 1)), true);
#elif defined(USE_TSUNAMI)
if (vol_array[note_being_played] != trigger_volume) {
trigger_obj.trackGain(note_being_played + (128 * (midi_channel - 1)), trigger_volume);
vol_array[note_being_played] = trigger_volume;
};
trigger_obj.trackPlayPoly(note_being_played + (128 * (midi_channel - 1)), TSUNAMI_OUT, true);
//trigger_obj.trackLoop(note_being_played + (128 * (midi_channel - 1)), true);
#endif
};
// If modulation isn't zero, send that as a MIDI CC for this channel
// This is meant to be configured to create a gentle vibrato.
if (my_modulation > 0) {
usbMIDI.sendControlChange(1, my_modulation, midi_channel);
if (output_mode != 1) {
MIDI.sendControlChange(1, my_modulation, midi_channel);
};
};
};
is_playing = true;
};
/// @brief Turns on a specific note over this string's MIDI channel at its current volume.
/// @details This is meant to be for the second drone option, and bypasses the note_being_played logic.
/// @param my_offset The offset from the string's base note to make sound. Unused here.
/// @param my_modulation The amount of optional modulation (0-127) to apply to the sound. This is MIDI CC1. 0 == no modulation. Unused here.
/// @param note The specific note to sound.
/// @warning my_modulation and my_offset only exist in this incarnation of the method because it keeps it from being ambiguous compared the other one. Maybe I should change this.
void GurdyString::soundOn(int my_offset, int my_modulation, int note) {
int note_to_play = note;
if (!mute_on) {
usbMIDI.sendNoteOn(note_to_play, midi_volume, midi_channel);
if (output_mode != 1) {
MIDI.sendNoteOn(note_to_play, midi_volume, midi_channel);
};
if (output_mode > 0) {
#if defined(USE_TRIGGER)
if (vol_array[note_to_play] != trigger_volume) {
trigger_obj.trackGain(note_to_play + (128 * (midi_channel - 1)), trigger_volume);
vol_array[note_to_play] = trigger_volume;
};
trigger_obj.trackPlayPoly(note_to_play + (128 * (midi_channel - 1)), true);
//trigger_obj.trackLoop(note_to_play + (128 * (midi_channel - 1)), true);
#elif defined(USE_TSUNAMI)
if (vol_array[note_to_play] != trigger_volume) {
trigger_obj.trackGain(note_to_play + (128 * (midi_channel - 1)), trigger_volume);
vol_array[note_to_play] = trigger_volume;
};
trigger_obj.trackPlayPoly(note_to_play + (128 * (midi_channel - 1)), TSUNAMI_OUT, true);
//trigger_obj.trackLoop(note_to_play + (128 * (midi_channel - 1)), true);
#endif
};
};
};
/// @brief Turns off the sound currently playing for this string, nicely.
void GurdyString::soundOff() {
// If user has one of the second-drone options enabled, trigger them here.
if (gros_mode == 1) {
soundOff(note_being_played - 5);
} else if (gros_mode == 2) {
soundOff(note_being_played - 7);
} else if (gros_mode == 3) {
soundOff(note_being_played - 12);
};
usbMIDI.sendNoteOff(note_being_played, midi_volume, midi_channel);
if (output_mode != 1) {
MIDI.sendNoteOff(note_being_played, midi_volume, midi_channel);
};
if (output_mode > 0) {
if (trigger_volume > -60) {
trigger_obj.trackFade(note_being_played + (128 * (midi_channel - 1)), trigger_volume - 10, 200, true);
} else {
trigger_obj.trackFade(note_being_played + (128 * (midi_channel - 1)), -70, 200, true);
}
//trigger_obj.trackStop(note_being_played + (128 * (midi_channel - 1)));
};
is_playing = false;
};
/// @brief Turns off sound to a specific note.
/// @param note The specific note to stop
void GurdyString::soundOff(int note) {
usbMIDI.sendNoteOff(note, midi_volume, midi_channel);
if (output_mode != 1) {
MIDI.sendNoteOff(note, midi_volume, midi_channel);
};
if (output_mode > 0) {
if (trigger_volume > -60) {
trigger_obj.trackFade(note + (128 * (midi_channel - 1)), trigger_volume - 10, 200, true);
} else {
trigger_obj.trackFade(note + (128 * (midi_channel - 1)), -70, 200, true);
};
};
};
/// @brief Issues a MIDI CC123 to the string's MIDI channel, killing all sound on it.
/// @note On Tsunami/Trigger units, this kills *all* tracks playing. This is not meant be the regular way to turn off sound, see soundOff() which does it more gently.
void GurdyString::soundKill() {
usbMIDI.sendControlChange(123, 0, midi_channel);
if (output_mode != 1) {
MIDI.sendControlChange(123, 0, midi_channel);
};
if (output_mode > 0) {
trigger_obj.stopAllTracks();
};
is_playing = false;
};
/// @brief Returns the string's open (base) note.
/// @return The string's base note as a MIDI note number 0-127
int GurdyString::getOpenNote() {
return open_note;
};
/// @brief Sets a new base note for this string.
/// @param new_note The new base MIDI note (0-127) for this string
void GurdyString::setOpenNote(int new_note) {
open_note = new_note;
};
// MIDI volume is an integer between 0 (off) and 127 (full volume).
/// @brief Sets a new volume for this string.
/// @param vol The new MIDI volume for this string. 0 = silent, 127 = full volume.
/// @note On Tsunami/Trigger units, this is translated to the -70 to +10 scale used on those internally.
/// * MIDI volume 112 = line-out volume level on Tsunami/Trigger units.
void GurdyString::setVolume(int vol) {
midi_volume = vol;
trigger_volume = int((vol)/128.0 * 80 - 70);
};
/// @brief Returns the string's MIDI volume.
/// @return The string's volume, 0-127
int GurdyString::getVolume() {
return midi_volume;
};
/// @brief Mutes/unmutes the string.
/// @param mute True = mute, false = unmute
/// @note While muted, soundOn/Off events do nothing. This exists for programming ease (calling sounOn() on all strings without checking if they are muted).
void GurdyString::setMute(bool mute) {
mute_on = mute;
};
/// @brief Returns the string's mute/unmute status.
/// @return True if muted, false if unmuted
bool GurdyString::getMute() {
return mute_on;
};
/// @brief Reports if the string is currently playing a note.
/// @return True if playing a note, false otherwise.
bool GurdyString::isPlaying() {
return is_playing;
};
/// @brief Send a MIDI Program Change to this string's MIDI channel.
/// @param program The program change value, 0-127.
/// @note This has no effect on Tsunami/Trigger units.
void GurdyString::setProgram(uint8_t program) {
usbMIDI.sendProgramChange(program, midi_channel);
if (output_mode != 1) {
MIDI.sendProgramChange(program, midi_channel);
};
};
/// @brief Sends a MIDI CC11 (Expression) value to this string's MIDI channel.
/// @param exp The expression value, 0-127.
/// @note This has no effect on Tsunami/Trigger units.
void GurdyString::setExpression(int exp) {
usbMIDI.sendControlChange(11, exp, midi_channel);
if (output_mode != 1) {
MIDI.sendControlChange(11, exp, midi_channel);
};
};
/// @brief Bends this string's sound to the specified amount.
/// @param bend The amount of pitch bend. 0 to 16383, where 8192 = no bend.
/// @note This has no effect on Tsunami/Trigger units.
void GurdyString::setPitchBend(int bend) {
usbMIDI.sendPitchBend(bend, midi_channel);
if (output_mode != 1) {
MIDI.sendPitchBend(bend, midi_channel);
};
};
/// @brief Sets the amount of modulation (vibrato) on this string.
/// @param vib The amount of modulation, 0-127.
/// @note This is MIDI CC1, the "mod wheel". Intended to used for a vibrato effect.
void GurdyString::setVibrato(int vib) {
usbMIDI.sendControlChange(1, vib, midi_channel);
if (output_mode != 1) {
MIDI.sendControlChange(1, vib, midi_channel);
};
};
/// @brief Returns the text name of this string.
/// @return The string's display name
String GurdyString::getName() {
return name;
};
/// @brief Sets the secondary output mode for this string
/// @param my_mode 0-2
/// @details Modes accepted:
/// * 0 - MIDI-OUT socket
/// * 1 - A Trigger/Tsunami device
/// * 2 - Both
void GurdyString::setOutputMode(int my_mode) {
output_mode = my_mode;
};
/// @brief Sets the "gros-mode" for this string.
/// @details Value of this should be 0-3:
/// * 0 = no additional sound
/// * 1 = a perfect fourth below (-5 semitones)
/// * 2 = a perfect fifth below (-7 semitones)
/// * 3 = an octave below (-12 semitones)
/// @param my_gros_mode
void GurdyString::setGrosMode(int my_gros_mode) {
gros_mode = my_gros_mode;
};
int GurdyString::getGrosMode() {
return gros_mode;
};
String GurdyString::getGrosString() {
if (gros_mode == 0) {
return String("None");
};
if (gros_mode == 1) {
return getLongNoteNum(getOpenNote() - 5);
};
if (gros_mode == 2) {
return getLongNoteNum(getOpenNote() - 7);
};
return getLongNoteNum(getOpenNote() - 12);
};
/// @brief Sets the Trigger/Tsunami loop mode on all of the tracks this string may use.
void GurdyString::setTrackLoops() {
#if defined(USE_TRIGGER) || defined(USE_TSUNAMI)
for (int x = 0; x <= 127; x++) {
trigger_obj.trackLoop(x + (128 * (midi_channel - 1)), true);
delay(5);
};
#endif
};
void GurdyString::clearVolArray() {
for (int x = 0; x < 128; x++) {
vol_array[x] = 0;
};
}