11namespace tracktion {
inline namespace engine
17static inline bool isRoman (
const juce::String& str)
noexcept
19 auto firstChar = str[0];
20 return firstChar ==
'i' || firstChar ==
'I' || firstChar ==
'v' || firstChar ==
'V';
30 if (str.substring (0, 1).containsAnyOf (
"ABCDEFG"))
31 str = str.substring (1);
33 if (str.substring (0, 1) ==
"b" || str.substring (0, 1) ==
"#")
34 str = str.substring (1);
44 auto note = name.substring (0, 1);
45 auto sharpFlat = name.substring (1, 2);
47 if (sharpFlat ==
"b" || sharpFlat ==
"#")
48 return note + sharpFlat;
57 auto note = name.substring (0, 1);
58 auto sharpFlat = name.substring (1, 2);
60 if (sharpFlat ==
"b" || sharpFlat ==
"#")
61 return name.substring (2);
63 return name.substring (1);
68 if (chordNameHasSymbol (oldName,
"maj"))
return chordNote (oldName) + Chord (Chord::majorTriad).getSymbol();
69 if (chordNameHasSymbol (oldName,
"min"))
return chordNote (oldName) + Chord (Chord::minorTriad).getSymbol();
70 if (chordNameHasSymbol (oldName,
"dim"))
return chordNote (oldName) + Chord (Chord::diminishedTriad).getSymbol();
71 if (chordNameHasSymbol (oldName,
"aug"))
return chordNote (oldName) + Chord (Chord::augmentedTriad).getSymbol();
72 if (chordNameHasSymbol (oldName,
"maj6"))
return chordNote (oldName) + Chord (Chord::majorSixthChord).getSymbol();
73 if (chordNameHasSymbol (oldName,
"min6"))
return chordNote (oldName) + Chord (Chord::minorSixthChord).getSymbol();
74 if (chordNameHasSymbol (oldName,
"dom7"))
return chordNote (oldName) + Chord (Chord::dominatSeventhChord).getSymbol();
75 if (chordNameHasSymbol (oldName,
"maj7"))
return chordNote (oldName) + Chord (Chord::majorSeventhChord).getSymbol();
76 if (chordNameHasSymbol (oldName,
"min7"))
return chordNote (oldName) + Chord (Chord::minorSeventhChord).getSymbol();
77 if (chordNameHasSymbol (oldName,
"aug7"))
return chordNote (oldName) + Chord (Chord::augmentedSeventhChord).getSymbol();
78 if (chordNameHasSymbol (oldName,
"dim7"))
return chordNote (oldName) + Chord (Chord::diminishedSeventhChord).getSymbol();
79 if (chordNameHasSymbol (oldName, oslash))
return chordNote (oldName) + Chord (Chord::halfDiminishedSeventhChord).getSymbol();
80 if (chordNameHasSymbol (oldName, oslash +
"7"))
return chordNote (oldName) + Chord (Chord::minorMajorSeventhChord).getSymbol();
85Chord::Chord (ChordType c) : type (c)
90 : type (Chord::customChord), steps (steps_), symbol (symbol_)
101 res +=
juce::String (s);
111 if (tokens.size() >= 2)
113 chord.type = (ChordType) tokens[0].getIntValue();
114 chord.symbol = tokens[1];
116 for (
int i = 2; i < tokens.size(); i++)
117 if (tokens[i].isNotEmpty())
118 chord.steps.add (tokens[i].getIntValue());
122 return { Chord::invalidChord };
129 for (
int i = majorTriad; i <= diminishedMinorNinthChord; i++)
130 res.
add ((ChordType) i);
139 case majorTriad:
return TRANS(
"Major Triad");
140 case minorTriad:
return TRANS(
"Minor Triad");
141 case diminishedTriad:
return TRANS(
"Diminished Triad");
142 case augmentedTriad:
return TRANS(
"Augmented Triad");
143 case majorSixthChord:
return TRANS(
"Major Sixth");
144 case minorSixthChord:
return TRANS(
"Minor Sixth");
145 case dominatSeventhChord:
return TRANS(
"Dominant Seventh");
146 case majorSeventhChord:
return TRANS(
"Major Seventh");
147 case minorSeventhChord:
return TRANS(
"Minor Seventh");
148 case augmentedSeventhChord:
return TRANS(
"Augmented Seventh");
149 case diminishedSeventhChord:
return TRANS(
"Diminished Seventh");
150 case halfDiminishedSeventhChord:
return TRANS(
"Half Diminished Seventh");
151 case minorMajorSeventhChord:
return TRANS(
"Minor Major Seventh");
152 case powerChord:
return TRANS(
"Power");
153 case suspendedSecond:
return TRANS(
"Suspended Second");
154 case suspendedFourth:
return TRANS(
"Suspended Fourth");
155 case majorNinthChord:
return TRANS(
"Major Ninth");
156 case dominantNinthChord:
return TRANS(
"Dominant Ninth");
157 case minorMajorNinthChord:
return TRANS(
"Minor Major Ninth");
158 case minorDominantNinthChord:
return TRANS(
"Minor Dominant Ninth");
159 case augmentedMajorNinthChord:
return TRANS(
"Augmented Major Ninth");
160 case augmentedDominantNinthChord:
return TRANS(
"Augmented Dominant Ninth");
161 case halfDiminishedNinthChord:
return TRANS(
"Half Diminished Ninth");
162 case halfDiminishedMinorNinthChord:
return TRANS(
"Half Diminished Minor Ninth");
163 case diminishedNinthChord:
return TRANS(
"Diminished Ninth");
164 case diminishedMinorNinthChord:
return TRANS(
"Diminished Minor Ninth");
175 case majorTriad:
return TRANS(
"major");
176 case minorTriad:
return TRANS(
"minor");
177 case diminishedTriad:
return TRANS(
"dim");
178 case augmentedTriad:
return TRANS(
"aug");
179 case majorSixthChord:
return TRANS(
"major 6");
180 case minorSixthChord:
return TRANS(
"minor 6");
181 case dominatSeventhChord:
return TRANS(
"dom 7");
182 case majorSeventhChord:
return TRANS(
"major 7");
183 case minorSeventhChord:
return TRANS(
"minor 7");
184 case augmentedSeventhChord:
return TRANS(
"aug 7");
185 case diminishedSeventhChord:
return TRANS(
"dim 7");
186 case halfDiminishedSeventhChord:
return TRANS(
"half dim 7");
187 case minorMajorSeventhChord:
return TRANS(
"min maj 7");
188 case powerChord:
return TRANS(
"power");
189 case suspendedSecond:
return TRANS(
"sus 2");
190 case suspendedFourth:
return TRANS(
"sus 4");
191 case majorNinthChord:
return TRANS(
"major 9");
192 case dominantNinthChord:
return TRANS(
"dom 9");
193 case minorMajorNinthChord:
return TRANS(
"min maj 9");
194 case minorDominantNinthChord:
return TRANS(
"min dom 9");
195 case augmentedMajorNinthChord:
return TRANS(
"aug maj 9");
196 case augmentedDominantNinthChord:
return TRANS(
"aug dom 9");
197 case halfDiminishedNinthChord:
return TRANS(
"half dim 9");
198 case halfDiminishedMinorNinthChord:
return TRANS(
"half dim min 9");
199 case diminishedNinthChord:
return TRANS(
"dim 9");
200 case diminishedMinorNinthChord:
return TRANS(
"dim min 9");
211 case majorTriad:
return "M";
212 case minorTriad:
return "m";
213 case diminishedTriad:
return "o";
214 case augmentedTriad:
return "+";
215 case majorSixthChord:
return "6";
216 case minorSixthChord:
return "m6";
217 case dominatSeventhChord:
return "7";
218 case majorSeventhChord:
return "M7";
219 case minorSeventhChord:
return "m7";
220 case augmentedSeventhChord:
return "+7";
221 case diminishedSeventhChord:
return "o7";
222 case halfDiminishedSeventhChord:
return oslash +
"7";
223 case minorMajorSeventhChord:
return "mM7";
224 case suspendedSecond:
return "sus2";
225 case suspendedFourth:
return "sus4";
226 case powerChord:
return "5";
227 case majorNinthChord:
return "M9";
228 case dominantNinthChord:
return "9";
229 case minorMajorNinthChord:
return "mM9";
230 case minorDominantNinthChord:
return "m9";
231 case augmentedMajorNinthChord:
return "+M9";
232 case augmentedDominantNinthChord:
return "+9";
233 case halfDiminishedNinthChord:
return oslash +
"9";
234 case halfDiminishedMinorNinthChord:
return oslash + flat +
"9";
235 case diminishedNinthChord:
return "o9";
236 case diminishedMinorNinthChord:
return "o" + flat +
"9";
247 case majorTriad:
return { 0, 4, 7 };
248 case minorTriad:
return { 0, 3, 7 };
249 case diminishedTriad:
return { 0, 3, 6 };
250 case augmentedTriad:
return { 0, 4, 8 };
251 case majorSixthChord:
return { 0, 4, 7, 9 };
252 case minorSixthChord:
return { 0, 3, 7, 9 };
253 case dominatSeventhChord:
return { 0, 4, 7, 10 };
254 case majorSeventhChord:
return { 0, 4, 7, 11 };
255 case minorSeventhChord:
return { 0, 3, 7, 10 };
256 case augmentedSeventhChord:
return { 0, 4, 8, 10 };
257 case diminishedSeventhChord:
return { 0, 3, 6, 9 };
258 case halfDiminishedSeventhChord:
return { 0, 3, 6, 10 };
259 case minorMajorSeventhChord:
return { 0, 3, 7, 11 };
260 case suspendedSecond:
return { 0, 2, 7 };
261 case suspendedFourth:
return { 0, 5, 7 };
262 case powerChord:
return { 0, 7 };
263 case majorNinthChord:
return { 0, 4, 7, 11, 14 };
264 case dominantNinthChord:
return { 0, 4, 7, 10, 14 };
265 case minorMajorNinthChord:
return { 0, 3, 7, 11, 14 };
266 case minorDominantNinthChord:
return { 0, 3, 7, 10, 14 };
267 case augmentedMajorNinthChord:
return { 0, 4, 8, 11, 14 };
268 case augmentedDominantNinthChord:
return { 0, 4, 8, 10, 14 };
269 case halfDiminishedNinthChord:
return { 0, 3, 6, 10, 14 };
270 case halfDiminishedMinorNinthChord:
return { 0, 3, 6, 10, 13 };
271 case diminishedNinthChord:
return { 0, 3, 6, 9, 14 };
272 case diminishedMinorNinthChord:
return { 0, 3, 6, 9, 13 };
273 case customChord:
return steps;
281 auto res = getSteps();
285 for (
int i = 0; i < inversion; i++)
291 else if (inversion < 0)
293 for (
int i = 0; i < -inversion; i++)
304Scale::Scale (ScaleType type_) : type (type_)
310 steps = { Whole, Whole, Half, Whole, Whole, Whole, Half };
311 triads = generateTriads (0);
312 sixths = generateSixths (0);
313 sevenths = generateSevenths (0);
316 steps = { Whole, Half, Whole, Whole, Whole, Half, Whole };
317 triads = generateTriads (1);
318 sixths = generateSixths (1);
319 sevenths = generateSevenths (1);
322 steps = { Half, Whole, Whole, Whole, Half, Whole, Whole };
323 triads = generateTriads (2);
324 sixths = generateSixths (2);
325 sevenths = generateSevenths (2);
328 steps = { Whole, Whole, Whole, Half, Whole, Whole, Half };
329 triads = generateTriads (3);
330 sixths = generateSixths (3);
331 sevenths = generateSevenths (3);
334 steps = { Whole, Whole, Half, Whole, Whole, Half, Whole };
335 triads = generateTriads (4);
336 sixths = generateSixths (4);
337 sevenths = generateSevenths (4);
341 steps = { Whole, Half, Whole, Whole, Half, Whole, Whole };
342 triads = generateTriads (5);
343 sixths = generateSixths (5);
344 sevenths = generateSevenths (5);
347 steps = { Half, Whole, Whole, Half, Whole, Whole, Whole };
348 triads = generateTriads (6);
349 sixths = generateSixths (6);
350 sevenths = generateSevenths (6);
353 steps = { Whole, Half, Whole, Whole, Whole, Whole, Half };
354 triads = { Chord::minorTriad, Chord::minorTriad, Chord::augmentedTriad, Chord::majorTriad, Chord::majorTriad, Chord::diminishedTriad, Chord::diminishedTriad };
355 sixths = { Chord::invalidChord, Chord::invalidChord, Chord::invalidChord, Chord::invalidChord, Chord::invalidChord, Chord::invalidChord, Chord::invalidChord };
356 sevenths = { Chord::minorMajorSeventhChord, Chord::minorSeventhChord, Chord::augmentedSeventhChord, Chord::dominatSeventhChord, Chord::dominatSeventhChord, Chord::halfDiminishedSeventhChord, Chord::halfDiminishedSeventhChord };
359 steps = { Whole, Half, Whole, Whole, Half, WholeHalf, Half };
360 triads = { Chord::minorTriad, Chord::diminishedTriad, Chord::augmentedTriad, Chord::minorTriad, Chord::majorTriad, Chord::majorTriad, Chord::diminishedTriad };
361 sixths = { Chord::invalidChord, Chord::invalidChord, Chord::invalidChord, Chord::invalidChord, Chord::invalidChord, Chord::invalidChord, Chord::invalidChord };
362 sevenths = { Chord::minorMajorSeventhChord, Chord::halfDiminishedSeventhChord, Chord::augmentedSeventhChord, Chord::minorSeventhChord, Chord::dominatSeventhChord, Chord::majorSeventhChord, Chord::diminishedSeventhChord };
371 for (
int i = major; i <= harmonicMinor; i++)
372 res.
add ((ScaleType)i);
381 for (
int i = major; i <= harmonicMinor; i++)
382 res.
add (getNameForType ((ScaleType) i));
387Scale::ScaleType Scale::getTypeFromName (
juce::String name)
389 for (
int i = major; i <= harmonicMinor; i++)
390 if (name == getNameForType ((ScaleType) i))
391 return (ScaleType) i;
399 return getNameForType (type);
404 return getShortNameForType (type);
411 case ionian:
return TRANS(
"Ionian");
412 case dorian:
return TRANS(
"Dorian");
413 case phrygian:
return TRANS(
"Phrygian");
414 case lydian:
return TRANS(
"Lydian");
415 case mixolydian:
return TRANS(
"Mixolydian");
416 case aeolian:
return TRANS(
"Aeolian");
417 case locrian:
return TRANS(
"Locrian");
418 case major:
return TRANS(
"Major");
419 case minor:
return TRANS(
"Minor");
420 case melodicMinor:
return TRANS(
"Melodic Minor");
421 case harmonicMinor:
return TRANS(
"Harmonic Minor");
430 case ionian:
return TRANS(
"Ion");
431 case dorian:
return TRANS(
"Dor");
432 case phrygian:
return TRANS(
"Phr");
433 case lydian:
return TRANS(
"Lyd");
434 case mixolydian:
return TRANS(
"Mix");
435 case aeolian:
return TRANS(
"Aeo");
436 case locrian:
return TRANS(
"Loc");
437 case major:
return TRANS(
"Maj");
438 case minor:
return TRANS(
"Min");
439 case melodicMinor:
return TRANS(
"Mel");
440 case harmonicMinor:
return TRANS(
"Har");
448 juce::Array<Chord> base { Chord::majorTriad, Chord::minorTriad, Chord::minorTriad, Chord::majorTriad, Chord::majorTriad, Chord::minorTriad, Chord::diminishedTriad };
450 for (
int i = 0; i < base.size(); i++)
451 res.
add (base[(i + offset) % base.size()]);
459 juce::Array<Chord> base { Chord::majorSixthChord, Chord::minorSixthChord, Chord::invalidChord, Chord::majorSixthChord, Chord::majorSixthChord, Chord::invalidChord, Chord::invalidChord };
461 for (
int i = 0; i < base.size(); i++)
462 res.
add (base[(i + offset) % base.size()]);
470 juce::Array<Chord> base { Chord::majorSeventhChord, Chord::minorSeventhChord, Chord::minorSeventhChord, Chord::majorSeventhChord, Chord::dominatSeventhChord, Chord::minorSeventhChord, Chord::halfDiminishedSeventhChord };
472 for (
int i = 0; i < base.size(); i++)
473 res.
add (base[(i + offset) % base.size()]);
483 for (
int i = 0; i < steps.size() - 1; i++)
488 case Whole: inc = 2;
break;
489 case Half: inc = 1;
break;
490 case WholeHalf: inc = 3;
break;
499 const int itemsInScale = res.
size();
500 for (
int o = 1; o < octaves; o++)
501 for (
int i = 0; i < itemsInScale; i++)
502 res.
add (res[i] + o * 12);
510 return {
"i",
"ii",
"iii",
"iv",
"v",
"vi",
"vii"};
513juce::String Scale::getIntervalName (Intervals interval)
const
515 auto name = getIntervalNames()[(
int)interval];
517 switch (triads[(
int)interval].getType())
519 case Chord::majorTriad: name = name.toUpperCase();
521 case Chord::minorTriad:
523 case Chord::augmentedTriad: name = name.toUpperCase() +
"+";
527 case Chord::customChord:
528 case Chord::invalidChord:
529 case Chord::majorSixthChord:
530 case Chord::minorSixthChord:
531 case Chord::dominatSeventhChord:
532 case Chord::majorSeventhChord:
533 case Chord::minorSeventhChord:
534 case Chord::augmentedSeventhChord:
535 case Chord::diminishedSeventhChord:
536 case Chord::halfDiminishedSeventhChord:
537 case Chord::minorMajorSeventhChord:
538 case Chord::suspendedSecond:
539 case Chord::suspendedFourth:
540 case Chord::powerChord:
541 case Chord::majorNinthChord:
542 case Chord::dominantNinthChord:
543 case Chord::minorMajorNinthChord:
544 case Chord::minorDominantNinthChord:
545 case Chord::augmentedMajorNinthChord:
546 case Chord::augmentedDominantNinthChord:
547 case Chord::halfDiminishedNinthChord:
548 case Chord::halfDiminishedMinorNinthChord:
549 case Chord::diminishedNinthChord:
550 case Chord::diminishedMinorNinthChord:
574 return v.hasType (IDs::PROGRESSIONITEM);
589 void objectOrderChanged()
override {}
598 : generator (g), state (s)
602 chordName.referTo (state, IDs::chordName, um);
603 pitches.referTo (state, IDs::pitches, um);
604 lengthInBeats.referTo (state, IDs::length, um, BeatDuration::fromBeats (4));
605 octave.referTo (state, IDs::octave, um);
606 inversion.referTo (state, IDs::inversion, um);
610 if (oldName.
isNotEmpty() && chordName.get().isEmpty())
612 chordName = fixLegacyChordNames (oldName);
613 state.removeProperty (IDs::name,
nullptr);
617PatternGenerator::ProgressionItem::~ProgressionItem() noexcept
621bool PatternGenerator::ProgressionItem::operator== (
const ProgressionItem& other)
const noexcept
623 return chordName == other.chordName
624 && pitches == other.pitches
625 && lengthInBeats == other.lengthInBeats;
628void PatternGenerator::ProgressionItem::setChordName (
juce::String name)
631 chordName = name.toLowerCase().retainCharacters (
"iv7");
644juce::String PatternGenerator::ProgressionItem::getChordName()
const
646 return generator.formatChordName (chordName);
649void PatternGenerator::ProgressionItem::setRoot (
int root)
654void PatternGenerator::ProgressionItem::setChord (
int root, Chord::ChordType type)
660Chord PatternGenerator::ProgressionItem::getChord (
const Scale& scale)
const
662 if (isRoman (chordName))
664 auto progressionsOpts = Scale::getIntervalNames();
665 auto interval = progressionsOpts.indexOf (chordName.get().retainCharacters (
"iv"));
667 return chordName.get().contains (
"7") ? scale.getSevenths()[interval] : scale.getTriads()[interval];
670 if (pitches.get().isNotEmpty())
674 for (
auto p :
juce::StringArray::fromTokens (pitches.get(),
", ", {}))
676 steps.
add (p.getIntValue());
678 return Chord (steps, chordSymbol (chordName));
681 for (Chord::ChordType type : Chord::getAllChordType())
685 if (chordNameHasSymbol (chordName.get(), chord.getSymbol()))
689 return Chord (Chord::invalidChord);
692bool PatternGenerator::ProgressionItem::isRomanNumeral()
const
694 return isRoman (chordName);
697juce::String PatternGenerator::ProgressionItem::getChordSymbol()
701 for (
auto itm : generator.getChordProgression())
706 beat = beat + itm->lengthInBeats;
709 Scale scale = generator.getScaleAtBeat (beat);
710 const int root = getRootNote (generator.getNoteAtBeat (beat), scale);
711 Chord chord = getChord (scale);
712 bool sharp = generator.clip.edit.pitchSequence.getPitchAtBeat (generator.clip.getStartBeat() +
toDuration (beat)).accidentalsSharp;
717int PatternGenerator::ProgressionItem::getRootNote (
int key,
const Scale& scale)
719 jassert (chordName.get().isNotEmpty());
721 if (isRoman (chordName))
723 auto progressionsOpts = Scale::getIntervalNames();
724 auto progressionSteps = scale.getSteps (3);
726 const int interval = progressionsOpts.indexOf (chordName.get().retainCharacters (
"iv"));
727 const int chordRoot = key + progressionSteps[interval];
732 auto name = chordName.get();
733 auto noteName = name.substring (1, 2) ==
"#" ? name.substring (0, 2) : name.substring (0, 1);
735 for (
int note = 0; note < 12; note++)
752 owner.clip.state.addListener (
this);
757 owner.clip.state.removeListener (
this);
760 void valueTreeChanged()
override
765 if (Clip::isClipState (p))
766 if (c == IDs::start || c == IDs::length || c == IDs::offset)
767 if (! owner.clip.edit.getUndoManager().isPerformingUndoRedo())
768 triggerAsyncUpdate();
771 void handleAsyncUpdate()
override
773 if (owner.getAutoUpdate())
774 owner.generatePattern();
784const int PatternGenerator::scaleRootGlobalTrack = -1;
785const int PatternGenerator::scaleRootChordTrack = -2;
789 auto um = c.getUndoManager();
793 mode.referTo (state, IDs::mode, um, Mode::off);
794 allNotes.referTo (state, IDs::allNotes, um);
795 octaveUp.referTo (state, IDs::octaveUp, um);
796 octaveDown.referTo (state, IDs::octaveDown, um);
797 spread.referTo (state, IDs::spread, um);
798 autoUpdate.referTo (state, IDs::autoUpdate, um,
true);
799 patternHash.referTo (state, IDs::hash, um);
800 octave.referTo (state, IDs::octave, um, 4);
801 velocity.referTo (state, IDs::velocity, um, 100.0f);
802 arpUpDown.referTo (state, IDs::arpUpDown, um,
false);
803 arpPlayRoot.referTo (state, IDs::arpPlayroot, um,
true);
804 arpPatternLength.referTo (state, IDs::arpPatternLength, um, 4.0f);
805 arpStyle.referTo (state, IDs::arpStle, um,
"0123");
806 arpSteps.referTo (state, IDs::arpSteps, um, 4);
807 melodyNoteLength.referTo (state, IDs::melodyNoteLength, um, BeatDuration::fromBeats (1.0 / 4.0f));
808 gate.referTo (state, IDs::gate, um, 100.0f);
809 scaleType.referTo (state, IDs::scaleType, um, Scale::major);
810 scaleRoot.referTo (state, IDs::scaleRoot, um, -1);
812 state.addListener (
this);
817PatternGenerator::~PatternGenerator()
822MidiClip* PatternGenerator::getMidiClip()
const
824 return dynamic_cast<MidiClip*
> (&clip);
827void PatternGenerator::editFinishedLoading()
830 if (
auto mc = getMidiClip())
831 if (patternHash == hashNotes (mc->getSequence(), 1))
832 patternHash = hashNotes (mc->getSequence(), 2);
834 if (mode != Mode::off)
838BeatDuration PatternGenerator::getMinimumChordLength()
const
840 return BeatDuration::fromBeats (1.0);
843BeatDuration PatternGenerator::getMaximumChordLength()
const
853 return BeatDuration::fromBeats (1024.0);
857void PatternGenerator::validateChordLengths()
867 for (
auto itm : getChordProgression())
868 if (itm->lengthInBeats.get() > getMaximumChordLength())
869 itm->lengthInBeats = getMaximumChordLength();
876int PatternGenerator::getChordProgressionLength()
const
878 return progressionList->size();
883 return progressionList->objects;
894 dst.removeAllChildren (um);
896 for (
int i = 0; i < src.getNumChildren(); i++)
897 dst.addChild (src.getChild (i).createCopy(), i, um);
930 if (c == IDs::arpSteps || c == IDs::arpUpDown)
933 int length = arpUpDown ? (arpSteps * 2 - 2) : arpSteps;
935 if (arpStyle.
get().length() != length)
936 arpStyle = getArpStyles()[0];
938 else if (c == IDs::mode)
940 if (mode == Mode::off)
942 autoUpdateManager =
nullptr;
946 if (autoUpdateManager ==
nullptr)
952ChordClip* PatternGenerator::getChordClipAt (TimePosition t)
const
954 for (
auto c : clip.edit.getChordTrack()->getClips())
955 if (c->getPosition().
time.contains (t))
956 if (auto cc = dynamic_cast<ChordClip*> (c))
962Scale PatternGenerator::getScaleAtBeat (BeatPosition beat)
const
966 if (
dynamic_cast<ChordClip*
> (&clip) !=
nullptr)
968 if (scaleRoot == scaleRootGlobalTrack)
971 scale = Scale (clip.edit.
pitchSequence.getPitchAt (t).getScale());
975 scale = Scale (scaleType);
978 else if (mode == Mode::off || scaleRoot == scaleRootChordTrack)
982 if (
auto cc = getChordClipAt (t))
984 const auto b = cc->getContentBeatAtTime (t);
985 return cc->getPatternGenerator()->getScaleAtBeat (b);
988 scale = Scale (clip.edit.
pitchSequence.getPitchAt (t).getScale());
990 else if (scaleRoot == scaleRootGlobalTrack)
993 scale = Scale (clip.edit.
pitchSequence.getPitchAt (t).getScale());
997 scale = Scale (scaleType);
1003int PatternGenerator::getNoteAtBeat (BeatPosition beat)
const
1005 if (
dynamic_cast<ChordClip*
>(&clip) !=
nullptr)
1007 if (scaleRoot == scaleRootGlobalTrack)
1013 if (mode == Mode::off || scaleRoot == scaleRootChordTrack)
1017 if (
auto cc = getChordClipAt (t))
1019 auto b = cc->getContentBeatAtTime (t);
1020 return cc->getPatternGenerator()->getNoteAtBeat (b);
1026 if (scaleRoot == scaleRootGlobalTrack)
1036 auto scale = getScaleAtBeat (BeatPosition());
1038 for (
int interval =
int (Scale::Intervals::i); interval <=
int (Scale::Intervals::vii); interval++)
1039 res.
add (scale.getIntervalName ((Scale::Intervals) interval));
1047 auto scale = getScaleAtBeat (BeatPosition());
1049 for (
int interval =
int (Scale::Intervals::i); interval <=
int (Scale::Intervals::vii); interval++)
1050 res.
add (scale.getIntervalName ((Scale::Intervals) interval) +
"7");
1057 if (isRoman (simplifiedChordName))
1059 auto interval = (Scale::Intervals) Scale::getIntervalNames().
indexOf (simplifiedChordName.
retainCharacters (
"iv"));
1060 auto scale = getScaleAtBeat (BeatPosition());
1061 auto intervalStr = scale.getIntervalName (interval);
1063 if (simplifiedChordName.
contains (
"7"))
1064 return intervalStr +
"7";
1069 return simplifiedChordName;
1072juce::StringArray PatternGenerator::getChordProgressionChordNames (
bool simplified)
const
1076 for (
auto item : *progressionList)
1079 res.
add (item->chordName);
1080 else if (item->isRomanNumeral())
1081 res.
add (item->getChordName());
1083 res.
add (item->getChordSymbol());
1091 if (progressionItems.
strings.size() > maxChords)
1092 progressionItems.
strings.resize (maxChords);
1101 progression.removeAllChildren (&um);
1103 for (
auto itmName : progressionItems)
1106 if (isRoman (itmName))
1107 itmName = itmName.toLowerCase().retainCharacters (
"iv7");
1112 progression.addChild (item, -1, &um);
1116void PatternGenerator::duplicateChordInProgression (
int idx)
1121 auto vt = progression.getChild (idx).createCopy();
1122 progression.addChild (vt, idx + 1, &um);
1125void PatternGenerator::removeIndexFromProgression (
int idx)
1130 progression.removeChild (idx, &um);
1133void PatternGenerator::removeRangeFromProgression (
int startIndex,
int numberToRemove)
1138 auto endIndex =
juce::jlimit (0, progression.getNumChildren(), startIndex + numberToRemove);
1139 startIndex =
juce::jlimit (0, progression.getNumChildren(), startIndex);
1141 if (endIndex > startIndex)
1143 numberToRemove = endIndex - startIndex;
1145 for (
int i = 0; i < numberToRemove; ++i)
1146 progression.removeChild (endIndex - i - 1, &um);
1150MidiNote* PatternGenerator::addNote (MidiList& sequence,
int pitch, BeatPosition startBeat, BeatDuration lengthInBeats,
1153 if (pitch < 0 || pitch >= 128)
1156 if (startBeat + lengthInBeats <= BeatPosition())
1162 if (lengthInBeats < BeatDuration::fromBeats (0.00001))
1165 if (startBeat < BeatPosition())
1167 lengthInBeats = lengthInBeats +
toDuration (startBeat);
1168 startBeat = BeatPosition();
1174 return sequence.addNote (pitch, startBeat, lengthInBeats, vel, colourIndex, um);
1177PatternGenerator::NoteType PatternGenerator::getTypeForNote (
const MidiClip& mc,
const MidiNote& note)
1179 bool inKey =
false, inChord =
false;
1181 Scale s = getScaleAtBeat (note.getStartBeat());
1182 int root = getNoteAtBeat (note.getStartBeat());
1184 inKey = s.getSteps().contains ((note.getNoteNumber() - root) % 12);
1187 auto progressionOffset = getFlattenedChordProgression (progressionItems);
1189 auto curBeat = BeatPosition::fromBeats (-progressionOffset.inBeats());
1191 int progressionCur = 0;
1192 const auto clipLength =
toPosition (mc.isLooping() ? mc.getLoopLengthBeats() : mc.getLengthInBeats() + mc.getOffsetInBeats());
1194 while (curBeat < clipLength)
1199 if (
auto item = progressionItems[progressionCur])
1201 const auto len = item->lengthInBeats.get();
1203 if (note.getBeatPosition() + BeatDuration::fromBeats (0.0001) >= curBeat
1204 && note.getBeatPosition() + BeatDuration::fromBeats (0.0001) < curBeat + len)
1206 if (item->getChord (s).isValid())
1208 int chordRoot = item->getRootNote (root, s) % 12;
1210 auto chordSteps = item->getChord (s).getSteps();
1212 for (
int i = 0; i < chordSteps.size(); i++)
1213 chordSteps.set (i, chordSteps[i] % 12);
1215 inChord = chordSteps.contains ((note.getNoteNumber() - chordRoot) % 12);
1220 curBeat = curBeat + len;
1225 if (progressionCur >= progressionItems.
size())
1229 if (inChord && inKey)
1230 return ChordInKeyNote;
1232 if (inChord && ! inKey)
1233 return ChordNotInKeyNote;
1238 return NotInKeyNote;
1243 if (mode == Mode::off || scaleRoot == scaleRootChordTrack)
1250 for (
auto c : clip.edit.getChordTrack()->getClips())
1251 if (auto cc = dynamic_cast<ChordClip*> (c))
1252 chordClips.add (cc);
1255 for (
int i = chordClips.
size(); --i >= 0;)
1257 for (
int j = chordClips.
size(); --j >= 0;)
1261 auto c1 = chordClips[i];
1262 auto c2 = chordClips[j];
1264 if (c1->getStartBeat() >= c2->getStartBeat() &&
1265 c1->getEndBeat() <= c2->getEndBeat())
1277 for (
auto cc : chordClips)
1278 clipStarts.add (cc->getStartBeat());
1282 for (
auto cc : chordClips)
1285 if (cc->getStartBeat() > pos)
1287 auto length = cc->getStartBeat() - pos;
1289 auto newItem =
new ProgressionItem (*
this, s,
true);
1290 newItem->lengthInBeats = length;
1291 progression.add (newItem);
1292 pos = cc->getStartBeat();
1295 BeatDuration amountToDrop;
1297 if (cc->getStartBeat() < pos)
1298 amountToDrop = pos - cc->getStartBeat();
1300 auto& src = cc->getPatternGenerator()->getChordProgression();
1303 auto avaliableLength = cc->getLengthInBeats();
1306 for (
auto start : clipStarts)
1308 auto len = start - cc->getStartBeat();
1310 if (len > BeatDuration() && len < avaliableLength)
1311 avaliableLength = len;
1314 BeatDuration progressionLength;
1316 while (progressionLength < avaliableLength)
1318 for (
auto itm : src)
1320 auto s = itm->state.createCopy();
1323 auto len =
std::min (newItm->lengthInBeats.get(), avaliableLength - progressionLength);
1324 progressionLength = progressionLength + len;
1328 if (len < amountToDrop)
1330 if (progressionLength >= avaliableLength)
1333 amountToDrop = amountToDrop - len;
1336 if (len > amountToDrop && amountToDrop > BeatDuration())
1338 len = len- amountToDrop;
1339 amountToDrop = BeatDuration();
1342 newItm->lengthInBeats = len;
1344 progression.add (newItm.release());
1346 if (progressionLength >= avaliableLength)
1350 pos = pos + progressionLength;
1355 auto newItem =
new ProgressionItem (*
this, s,
true);
1356 newItem->lengthInBeats = BeatDuration::fromBeats (100000);
1357 progression.add (newItem);
1363 while (toChop > BeatDuration())
1365 auto itm = progression[0];
1367 if (itm->lengthInBeats <= toChop)
1369 toChop = toChop - itm->lengthInBeats;
1370 progression.remove (0);
1383 for (
auto itm : getChordProgression())
1384 progression.add (new ProgressionItem (*this, itm->state, true));
1390void PatternGenerator::clearProgression()
1396 progression.removeAllChildren (&um);
1399void PatternGenerator::insertChordIntoProgression (
int idx,
juce::String chordName)
1403 if (progression.getNumChildren() < maxChords)
1407 if (isRoman (chordName))
1408 chordName = chordName.toLowerCase().retainCharacters (
"iv7");
1411 item.setProperty (IDs::name, chordName, &um);
1413 progression.addChild (item, idx, &um);
1421 if (progression.getNumChildren() < maxChords)
1425 if (isRoman (chordName))
1426 chordName = chordName.toLowerCase().retainCharacters (
"iv7");
1428 auto item = createValueTree (IDs::PROGRESSIONITEM,
1429 IDs::name, chordName,
1430 IDs::pitches, pitches);
1432 progression.addChild (item, idx, &um);
1436void PatternGenerator::moveChordInProgression(
int srcIdx,
int dstIdx)
1440 auto itm = progression.getChild (srcIdx);
1441 progression.removeChild (srcIdx, &um);
1443 if (dstIdx > srcIdx)
1444 progression.addChild (itm, dstIdx - 1, &um);
1446 progression.addChild (itm, dstIdx, &um);
1454 for (
int i = 0; i < arpSteps; i++)
1464 for (
int i = 0; i < res.
size(); i++)
1467 auto suffix = s.substring (1, s.length() - 1);
1469 for (
int j = suffix.length() - 1; j >= 0; j--)
1479void PatternGenerator::clearPattern()
1484 if (
auto mc = getMidiClip())
1485 mc->getSequence().clear (um);
1488void PatternGenerator::generatePattern()
1490 if (
auto mc = getMidiClip())
1492 if (
auto at = mc->getAudioTrack())
1494 MidiMessageArray::notMPE);
1498 case Mode::off: clearPattern(); clearHash();
break;
1499 case Mode::arpeggio: generateArpPattern(); updateHash();
break;
1500 case Mode::chords: generateChordPattern(); updateHash();
break;
1501 case Mode::bass: generateBassPattern(); updateHash();
break;
1502 case Mode::melody: generateMelodyPattern(); updateHash();
break;
1507void PatternGenerator::generateArpPattern()
1509 auto mc = getMidiClip();
1519 auto progressionOffset = getFlattenedChordProgression (progressionItems);
1521 auto progressionLength = progressionItems.size();
1523 if (progressionLength == 0)
1526 auto um = mc->getUndoManager();
1527 auto& sequence = mc->getSequence();
1529 int progressionCur = 0;
1535 for (
int i = 0; i < arpStyle.
get().length(); i++)
1536 styleValues.
add (arpStyle.
get().substring (i, i + 1).getIntValue());
1538 const float lengthFactor = gate / 100.0f;
1539 const auto noteLengthBeat = BeatDuration::fromBeats (arpPatternLength / styleValues.
size());
1545 auto stepLengthLeft = progressionItems.getFirst()->lengthInBeats.get();
1546 const auto clipLength =
toPosition (mc->isLooping() ? mc->getLoopLengthBeats() : mc->getLengthInBeats() + mc->getOffsetInBeats());
1547 auto curBeat = BeatPosition::fromBeats (-progressionOffset.inBeats());
1548 bool stepStart =
true;
1551 while (curBeat < clipLength)
1556 auto stepLength =
std::min (stepLengthLeft, noteLengthBeat);
1558 auto scale = getScaleAtBeat (curBeat);
1559 auto steps = scale.getSteps (3);
1561 if (
auto progressionItem = progressionItems[progressionCur])
1563 if (progressionItem->isValid())
1565 const int chordRoot = progressionItem->getRootNote (getNoteAtBeat (curBeat), scale);
1566 const int octaveOffset = (octave + progressionItem->octave - 1) * 12;
1568 auto chord = progressionItem->getChord (scale);
1569 auto intervals = chord.getSteps (progressionItem->inversion);
1571 for (
auto v : chord.getSteps (progressionItem->inversion)) intervals.add (v + 12);
1572 for (
auto v : chord.getSteps (progressionItem->inversion)) intervals.add (v + 24);
1575 const int stepIndex = styleValues[stepCur];
1576 const int note = chordRoot + intervals[stepIndex] + octaveOffset;
1578 addNote (sequence, note, curBeat, stepLength * lengthFactor,
int (velocity / 100.0f * 127),
1579 mc->edit.engine.getEngineBehaviour().getDefaultNoteColour(), um);
1582 if (stepStart && arpPlayRoot)
1586 const int rootNote = chordRoot + octaveOffset - 12;
1587 addNote (sequence, rootNote, curBeat, stepLengthLeft,
int (velocity / 100.0f * 127),
1588 mc->edit.engine.getEngineBehaviour().getDefaultNoteColour(), um);
1593 stepLengthLeft = stepLengthLeft - stepLength;
1596 if (stepCur >= styleValues.
size())
1599 if (stepLengthLeft < 0.0001_bd)
1605 if (progressionCur >= progressionLength)
1608 stepLengthLeft = progressionItems[progressionCur]->lengthInBeats;
1611 curBeat = curBeat + stepLength;
1624 : start (start_), length (length_), velocity (velocity_) {}
1631void PatternGenerator::generateChordPattern()
1633 auto mc = getMidiClip();
1642 auto progressionOffset = getFlattenedChordProgression (progressionItems);
1644 const int progressionLength = progressionItems.size();
1645 if (progressionLength == 0)
1651 auto pattern = getChordPattern();
1655 for (
int i = 0; i < pattern.getNumChildren(); i++)
1659 auto bar = pattern.getChild (i);
1660 const int barLen =
std::max (1,
int (bar.getProperty (IDs::length, 4)));
1661 const int end =
static_cast<int> (getMaximumChordLength().
inBeats() / barLen);
1663 for (
int j = 0; j < bar.getNumChildren(); j++)
1665 auto c = bar.getChild (j);
1667 for (
int k = 0; k < end; k++)
1668 notes.
getReference (i).add (ChordNote (BeatPosition::fromBeats (
static_cast<double> (c.getProperty (IDs::start)) + barLen * k),
1669 BeatDuration::fromBeats (
static_cast<double> (c.getProperty (IDs::length))),
1670 c.getProperty (IDs::velocity)));
1677 notes.
getReference (0).add (ChordNote ({}, getMaximumChordLength(), 127.0f));
1680 auto um = mc->getUndoManager();
1681 MidiList& sequence = mc->getSequence();
1683 int progressionCur = 0;
1686 const float lengthFactor = gate / 100.0f;
1692 auto curBeat = BeatPosition::fromBeats (-progressionOffset.inBeats());
1693 const auto clipLength =
toPosition (mc->isLooping() ? mc->getLoopLengthBeats()
1694 : mc->getLengthInBeats() + mc->getOffsetInBeats());
1697 while (curBeat < clipLength)
1702 auto scale = getScaleAtBeat (curBeat);
1704 if (
auto progressionItem = progressionItems[progressionCur])
1706 const auto patternLength = progressionItem->lengthInBeats.get();
1708 if (progressionItem->isValid())
1710 const int chordRoot = progressionItem->getRootNote (getNoteAtBeat (curBeat), scale);
1711 const int octaveOffset = (octave + progressionItem->octave - 1) * 12;
1712 const int rootNote = chordRoot + octaveOffset;
1714 Chord chord = progressionItem->getChord (scale);
1716 auto steps = chord.getSteps (progressionItem->inversion);
1726 steps.
add (steps[1] + 12);
1730 for (
int step : steps)
1732 for (
const ChordNote& chordNote : notes[patternCur])
1734 const int note = rootNote + step;
1736 if (chordNote.start <
toPosition (patternLength))
1738 addNote (sequence, note, curBeat + toDuration (chordNote.start),
1740 int (velocity / 100.0f * chordNote.velocity),
1741 mc->edit.engine.getEngineBehaviour().getDefaultNoteColour(), um);
1747 curBeat = curBeat + patternLength;
1750 if (progressionCur >= progressionLength)
1754 if (patternCur >= notes.
size())
1765void PatternGenerator::generateMelodyPattern()
1767 auto mc = getMidiClip();
1776 auto progressionOffset = getFlattenedChordProgression (progressionItems);
1777 auto progressionLength = progressionItems.
size();
1779 if (progressionLength == 0)
1782 auto um = mc->getUndoManager();
1783 auto& sequence = mc->getSequence();
1784 auto& edit = mc->edit;
1785 auto& tempoSequence = edit.tempoSequence;
1787 auto beatsPerWhole = tempoSequence.getTimeSigAt (mc->getPosition().getStart()).denominator.get();
1788 auto noteLengthBeat = melodyNoteLength.get() * beatsPerWhole;
1792 for (
int i = 0; i <
std::ceil (getMaximumChordLength() / noteLengthBeat) * 4; i++)
1793 notes.
add (ChordNote (toPosition (noteLengthBeat * i),
1794 std::min (noteLengthBeat, getMaximumChordLength() - (noteLengthBeat * i)), 127.0f));
1796 int progressionCur = 0;
1798 auto lengthFactor = gate / 100.0f;
1804 auto curBeat = BeatPosition::fromBeats (-progressionOffset.inBeats());
1805 const auto clipLength =
toPosition (mc->isLooping() ? mc->getLoopLengthBeats()
1806 : mc->getLengthInBeats() + mc->getOffsetInBeats());
1809 while (curBeat < clipLength)
1814 auto scale = getScaleAtBeat (curBeat);
1815 auto steps = scale.getSteps (3);
1817 if (
auto progressionItem = progressionItems[progressionCur])
1819 const auto patternLength = progressionItem->lengthInBeats.get();
1821 if (progressionItem->isValid())
1823 auto chordRoot = progressionItem->getRootNote (getNoteAtBeat (curBeat), scale);
1824 auto octaveOffset = (octave + progressionItem->octave - 1) * 12;
1825 auto rootNote = chordRoot + octaveOffset;
1826 auto chord = progressionItem->getChord (scale);
1833 for (
auto s : chord.getSteps (0))
1835 chordSteps.
add (rootNote + s);
1836 chordSteps.
add (rootNote + s + 12);
1841 for (
int o = 0; o < 3; o++)
1843 for (
int step : scale.getSteps())
1845 auto note1 = getNoteAtBeat (curBeat) + octaveOffset + step + o * 12;
1847 if (notesAdded < 14 && note1 >= chordSteps.
getFirst())
1849 for (
auto& chordNote : notes)
1851 if (chordNote.start <
toPosition (patternLength))
1853 if (
auto newNote = addNote (sequence, note1, curBeat + toDuration (chordNote.start),
1855 patternLength -
toDuration (chordNote.start)),
1856 (
int) (velocity / 100.0f * chordNote.velocity),
1857 mc->edit.engine.getEngineBehaviour().getDefaultNoteColour(), um))
1859 newNote->setMute (
true, um);
1860 newNote->setColour (chordSteps.
contains (note1) ? 0 : 2, um);
1872 for (
int step : chord.getSteps (progressionItem->inversion))
1874 for (
auto& chordNote : notes)
1876 auto note1 = rootNote + step;
1878 if (chordNote.start <
toPosition (patternLength))
1879 if (
auto newNote = addNote (sequence, note1, curBeat + toDuration (chordNote.start),
1881 patternLength -
toDuration (chordNote.start)),
1882 (
int) (velocity / 100.0f * chordNote.velocity),
1883 mc->edit.engine.getEngineBehaviour().getDefaultNoteColour(), um))
1884 newNote->setMute (
true, um);
1887 auto note2 = rootNote + step + 12;
1889 if (chordNote.start <
toPosition (patternLength))
1890 if (
auto newNote = addNote (sequence, note2, curBeat + toDuration (chordNote.start),
1892 patternLength -
toDuration (chordNote.start)),
1893 (
int) (velocity / 100.0f * chordNote.velocity),
1894 mc->edit.engine.getEngineBehaviour().getDefaultNoteColour(), um))
1895 newNote->setMute (
true, um);
1902 curBeat = curBeat + patternLength;
1904 if (++progressionCur >= progressionLength)
1924void PatternGenerator::generateBassPattern()
1926 auto mc = getMidiClip();
1935 auto progressionOffset = getFlattenedChordProgression (progressionItems);
1936 auto progressionLength = progressionItems.
size();
1938 if (progressionLength == 0)
1944 auto pattern = getBassPattern();
1948 for (
int i = 0; i < pattern.getNumChildren(); i++)
1952 auto bar = pattern.getChild (i);
1953 auto barLen =
std::max (1,
int (bar.getProperty (IDs::length, 4)));
1954 const int end =
static_cast<int> (getMaximumChordLength().
inBeats() / barLen);
1956 for (
int j = 0; j < bar.getNumChildren(); j++)
1958 auto c = bar.getChild (j);
1960 for (
int k = 0; k < end; k++)
1962 notes.
getReference (i).add ({ BeatPosition::fromBeats (
static_cast<double> (c.getProperty (IDs::start)) + barLen * k),
1963 BeatDuration::fromBeats (
static_cast<double> (c.getProperty (IDs::length))),
1964 c.getProperty (IDs::velocity),
1965 c.getProperty (IDs::pitch),
1966 c.getProperty (IDs::octave) });
1974 notes.
getReference (0).add ({ {}, getMaximumChordLength(), 127.0f, 0, 0 });
1977 auto um = mc->getUndoManager();
1978 auto& sequence = mc->getSequence();
1980 int progressionCur = 0;
1983 const float lengthFactor = gate / 100.0f;
1989 auto curBeat = BeatPosition::fromBeats (-progressionOffset.inBeats());
1990 const auto clipLength =
toPosition (mc->isLooping() ? mc->getLoopLengthBeats()
1991 : mc->getLengthInBeats() + mc->getOffsetInBeats());
1994 while (curBeat < clipLength)
1999 auto scale = getScaleAtBeat (curBeat);
2002 if (
auto progressionItem = progressionItems[progressionCur])
2004 const auto patternLength = progressionItem->lengthInBeats.get();
2006 if (progressionItem->isValid())
2008 const int chordRoot = progressionItem->getRootNote (getNoteAtBeat (curBeat), scale);
2009 const int octaveOffset = (octave + progressionItem->octave - 1) * 12;
2010 const int rootNote = chordRoot + octaveOffset;
2012 for (
const BassNote& bassNote : notes[patternCur])
2014 int pos = bassNote.pitch;
2015 int off = bassNote.octave * 12;
2017 if (pos < scale.getSteps().size())
2019 const int rootNoteIndex = steps.
indexOf (chordRoot);
2020 const int note = rootNote + steps[rootNoteIndex + pos] - steps[rootNoteIndex] + off;
2022 if (bassNote.start <
toPosition (patternLength))
2024 addNote (sequence, note, curBeat + toDuration (bassNote.start),
2025 std::min (bassNote.length * lengthFactor,
2026 patternLength -
toDuration (bassNote.start)),
2027 (
int) (velocity / 100.0f * bassNote.velocity),
2028 mc->edit.engine.getEngineBehaviour().getDefaultNoteColour(), um);
2034 curBeat = curBeat + patternLength;
2037 if (progressionCur >= progressionItems.
size())
2041 if (patternCur >= notes.
size())
2052void PatternGenerator::playGuideChord (
int idx)
const
2054 if (
auto mc = getMidiClip())
2056 auto progressionItems = getChordProgression();
2058 if (idx < 0 || idx >= progressionItems.
size())
2064 BeatPosition curBeat;
2066 for (
int i = 0; i < idx; i++)
2067 curBeat = curBeat + progressionItems[i]->lengthInBeats;
2069 auto scale = getScaleAtBeat (curBeat);
2070 auto steps = scale.getSteps (3);
2072 if (
auto progressionItem = progressionItems[idx])
2074 const int chordRoot = progressionItem->getRootNote (getNoteAtBeat (curBeat), scale);
2075 const int octaveOffset = (octave + progressionItem->octave - 1) * 12;
2076 const int rootNote = chordRoot + octaveOffset;
2078 Chord chord = progressionItem->getChord (scale);
2080 if (
auto at = mc->getAudioTrack())
2084 for (
int step : chord.getSteps (progressionItem->inversion))
2086 auto note = rootNote + step;
2088 if (note >= 0 && note < 128)
2090 at->playGuideNote (note, mc->getMidiChannel(),
2091 (
int) (velocity / 100 * 127), stop);
2100void PatternGenerator::updateHash()
2102 if (
auto mc = getMidiClip())
2103 patternHash = hashNotes (mc->getSequence(), 2);
2108void PatternGenerator::clearHash()
2113HashCode PatternGenerator::hashNotes (MidiList& sequence,
int version)
2117 HashCode
hash = sequence.getNumNotes() + 1;
2119 for (
auto note : sequence.getNotes())
2121 hash ^=
static_cast<HashCode
> (note->getNoteNumber() * 31)
2122 ^
static_cast<HashCode
> (note->getStartBeat().inBeats() * 73)
2123 ^
static_cast<HashCode
> (note->getLengthBeats().inBeats() * 233)
2124 ^
static_cast<HashCode
> (note->getColour() * 467)
2125 ^
static_cast<HashCode
> (note->isMute() ? 877 : 947)
2126 ^
static_cast<HashCode
> (note->getVelocity() * 3083);
2129 hash =
static_cast<HashCode
> (core::hash (
static_cast<size_t> (hash), 7));
2135void PatternGenerator::setAutoUpdate (
bool on)
2148bool PatternGenerator::getAutoUpdate()
2150 if (
auto mc = getMidiClip())
2151 return autoUpdate && patternHash == hashNotes (mc->getSequence(), 2);
2156void PatternGenerator::refreshPatternIfNeeded()
2158 if (autoUpdateManager !=
nullptr)
2159 autoUpdateManager->triggerAsyncUpdate();
2169 double durations[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
2171 for (
auto n : notes)
2172 durations[n->getNoteNumber() % 12] += n->getLengthBeats().inBeats();
2174 double major[12] = { 6.35, 2.23, 3.48, 2.33, 4.38, 4.09, 2.52, 5.19, 2.39, 3.66, 2.29, 2.88 };
2175 double minor[12] = { 6.33, 2.68, 3.52, 5.38, 2.60, 3.53, 2.54, 4.75, 3.98, 2.69, 3.34, 3.17 };
2177 double xAveMajor = 0, xAveMinor = 0, yAve = 0;
2179 for (
int i = 0; i < 12; i++)
2181 xAveMajor += major[i];
2182 xAveMinor += minor[i];
2183 yAve += durations[i];
2190 for (
int key = 0; key < 12; key++)
2192 double s1Major = 0, s2Major = 0, s3Major = 0;
2193 double s1Minor = 0, s2Minor = 0, s3Minor = 0;
2195 for (
int i = 0; i < 12; i++)
2197 double xiMajor = major[(i - key + 12) % 12];
2198 double xiMinor = minor[(i - key + 12) % 12];
2200 s1Major += (xiMajor - xAveMajor) * (durations[i] - yAve);
2201 s2Major += (xiMajor - xAveMajor) * (xiMajor - xAveMajor);
2202 s3Major += (durations[i] - yAve) * (durations[i] - yAve);
2204 s1Minor += (xiMinor - xAveMinor) * (durations[i] - yAve);
2205 s2Minor += (xiMinor - xAveMinor) * (xiMinor - xAveMinor);
2206 s3Minor += (durations[i] - yAve) * (durations[i] - yAve);
2209 if (s2Major * s3Major > 0)
2211 double rMajor = s1Major /
std::sqrt (s2Major * s3Major);
2212 double rMinor = s1Minor /
std::sqrt (s2Minor * s3Minor);
2214 results.
add ({ rMajor, key, Scale::major });
2215 results.
add ({ rMinor, key, Scale::minor });
int size() const noexcept
void remove(int indexToRemove)
void insert(int indexToInsertAt, ParameterType newElement)
ElementType getFirst() const noexcept
int indexOf(ParameterType elementToLookFor) const
void add(const ElementType &newElement)
ElementType removeAndReturn(int indexToRemove)
bool contains(ParameterType elementToLookFor) const
ElementType & getReference(int index) noexcept
Type get() const noexcept
bool isValid() const noexcept
static MidiMessage allNotesOff(int channel) noexcept
static String getMidiNoteName(int noteNumber, bool useSharps, bool includeOctaveNumber, int octaveNumForMiddleC)
int size() const noexcept
static StringArray fromTokens(StringRef stringToTokenise, bool preserveQuotedStrings)
int size() const noexcept
void add(String stringToAdd)
void set(int index, String newString)
int length() const noexcept
int indexOf(StringRef textToLookFor) const noexcept
String retainCharacters(StringRef charactersToRetain) const
bool contains(StringRef text) const noexcept
static String charToString(juce_wchar character)
bool isNotEmpty() const noexcept
void removeChild(const ValueTree &child, UndoManager *undoManager)
bool isValid() const noexcept
ValueTree & setProperty(const Identifier &name, const var &newValue, UndoManager *undoManager)
void addChild(const ValueTree &child, int index, UndoManager *undoManager)
ValueTree getChildWithName(const Identifier &type) const
void removeListener(Listener *listener)
TimePosition getTimeOfContentBeat(BeatPosition) const
Returns time of a beat number.
juce::UndoManager * getUndoManager() const
Returns the UndoManager.
PitchSequence pitchSequence
The global PitchSequence of this Edit.
juce::UndoManager & getUndoManager() noexcept
Returns the juce::UndoManager used for this Edit.
void setChordProgressionFromChordNames(juce::StringArray progression)
Sets a chord progression using chord roman numerals.
BeatPosition getStartBeat() const
Returns the start beat in the Edit of this item.
BeatDuration getLengthInBeats() const
Returns the duration in beats the of this item.
#define TRANS(stringLiteral)
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
constexpr TimePosition toPosition(TimeDuration)
Converts a TimeDuration to a TimePosition.
constexpr TimeDuration toDuration(TimePosition)
Converts a TimePosition to a TimeDuration.
size_t hash(size_t seed, const T &v)
Hashes a type with a given seed and returns the new hash value.
T next_permutation(T... args)
Represents a duration in beats.
constexpr double inBeats() const
Returns the position as a number of beats.
Represents a position in beats.
#define CRASH_TRACER
This macro adds the current location to a stack which gets logged if a crash happens.