11namespace tracktion {
inline namespace engine
14const char* GrooveTemplate::grooveXmlTag =
"GROOVETEMPLATE";
16GrooveTemplate::GrooveTemplate()
19 name (
TRANS(
"Unnamed")),
27 if (node !=
nullptr && node->
hasTagName (grooveXmlTag))
34 for (
auto n : node->getChildWithTagNameIterator (
"SHIFT"))
35 latenesses.add ((
float) n->getDoubleAttribute (
"delta", 0.0));
39GrooveTemplate::GrooveTemplate (
const GrooveTemplate& other)
41 latenesses = other.latenesses;
42 numNotes = other.numNotes;
43 notesPerBeat = other.notesPerBeat;
45 parameterized = other.parameterized;
48GrooveTemplate::~GrooveTemplate()
52bool GrooveTemplate::operator== (
const GrooveTemplate& o)
const
54 return latenesses == o.latenesses
55 && numNotes == o.numNotes
56 && notesPerBeat == o.notesPerBeat
57 && parameterized == o.parameterized;
60bool GrooveTemplate::isParameterized()
const
65const GrooveTemplate& GrooveTemplate::operator= (
const GrooveTemplate& other)
67 latenesses = other.latenesses;
68 numNotes = other.numNotes;
69 notesPerBeat = other.notesPerBeat;
71 parameterized = other.parameterized;
89 int lastNonZeroNote = numNotes;
90 while (--lastNonZeroNote >= 0)
91 if (latenesses[lastNonZeroNote] != 0.0f)
94 for (
int i = 0; i <= lastNonZeroNote; ++i)
97 n->setAttribute (
"delta", 0.001 *
juce::roundToInt (1000.0 * latenesses[i]));
104void GrooveTemplate::setNumberOfNotes (
int notes)
109void GrooveTemplate::setNotesPerBeat (
int notes)
114float GrooveTemplate::getLatenessProportion (
int noteNumber,
float strength)
const
117 return latenesses[noteNumber] * strength;
119 return latenesses[noteNumber];
122void GrooveTemplate::setLatenessProportion (
int noteNumber,
float p,
float strength)
124 while (latenesses.size() < noteNumber)
125 latenesses.add (0.0f);
128 latenesses.set (noteNumber,
juce::jlimit (-1.0f, 1.0f, p / strength));
130 latenesses.set (noteNumber,
juce::jlimit (-1.0f, 1.0f, p));
133void GrooveTemplate::clearLatenesses()
135 latenesses.clearQuick();
140 auto activeStrength = parameterized ? strength : 1.0f;
143 const double offset = notesPerBeat * (beatsTime.
inBeats() - (beatNum / notesPerBeat));
146 const double lateness = latenesses[latenessIndex] * activeStrength;
147 const double t1 = (beatNum + 0.5f * lateness);
148 const double t2minust1 = 1.0 + 0.5f * ((latenesses[(latenessIndex + 1) % numNotes] * activeStrength) - lateness);
150 return BeatPosition::fromBeats ((t1 + offset * t2minust1) / notesPerBeat);
156 beats = beatsTimeToGroovyTime (beats, strength);
160bool GrooveTemplate::isEmpty()
const
162 for (
int i = latenesses.size(); --i >= 0;)
163 if (latenesses.getUnchecked (i) != 0.0f)
170const char* basic8SwingXML =
"<GROOVETEMPLATE name=\"Basic 8th Swing\" numberOfNotes=\"2\" notesPerBeat=\"2\" parameterized=\"1\"><SHIFT delta=\"0.0\"/><SHIFT delta=\"0.66\"/></GROOVETEMPLATE>";
171const char* basic16SwingXML =
"<GROOVETEMPLATE name=\"Basic 16th Swing\" numberOfNotes=\"2\" notesPerBeat=\"4\" parameterized=\"1\"><SHIFT delta=\"0.0\"/><SHIFT delta=\"0.66\"/></GROOVETEMPLATE>";
174GrooveTemplateManager::GrooveTemplateManager (
Engine& e)
179 if (knownGrooves.isEmpty())
182 if (
auto defSettings =
juce::parseXML (TracktionBinaryData::groove_templates_xml))
183 if (defSettings->getTagName() ==
"GROOVETEMPLATES")
184 reload (defSettings.get());
187 bool anyParameterized =
false;
188 for (
auto g : knownGrooves)
189 if (g->isParameterized())
190 anyParameterized = true;
193 if (! anyParameterized)
195 if (
auto defSettings =
juce::parseXML (TracktionBinaryData::groove_templates_2_xml))
196 if (defSettings->hasTagName (
"GROOVETEMPLATES"))
197 for (
auto n : defSettings->getChildWithTagNameIterator (GrooveTemplate::grooveXmlTag))
198 knownGrooves.add (new GrooveTemplate (n));
202 auto hasGroove = [
this] (
const GrooveTemplate& gt)
204 for (
auto g : knownGrooves)
205 if (g->getName() == gt.getName())
211 auto addGrooveIfNotExists = [
this, &hasGroove] (
juce::String xmlString)
216 if (! hasGroove (*gt))
217 knownGrooves.add (gt.release());
220 addGrooveIfNotExists (basic8SwingXML);
221 addGrooveIfNotExists (basic16SwingXML);
225void GrooveTemplateManager::useParameterizedGrooves (
bool use)
227 activeGrooves.clear();
229 useParameterized = use;
230 if (useParameterized)
232 for (
auto g : knownGrooves)
233 activeGrooves.add (g);
237 for (
auto g : knownGrooves)
238 if (! g->isParameterized())
239 activeGrooves.add (g);
245 if (
auto xml = engine.
getPropertyStorage().getXmlProperty (SettingID::grooveTemplates))
251 knownGrooves.clearQuick (
true);
253 if (grooves !=
nullptr && grooves->
hasTagName (
"GROOVETEMPLATES"))
254 for (
auto n : grooves->getChildWithTagNameIterator (
GrooveTemplate::grooveXmlTag))
257 useParameterizedGrooves (useParameterized);
260void GrooveTemplateManager::save()
264 for (
auto gt : knownGrooves)
266 n.addChildElement (gt->createXml());
272int GrooveTemplateManager::getNumTemplates()
const
274 return activeGrooves.size();
277juce::String GrooveTemplateManager::getTemplateName (
int index)
const
279 if (
auto gt = activeGrooves[index])
280 return gt->getName();
285const GrooveTemplate* GrooveTemplateManager::getTemplate (
int index)
287 return activeGrooves[index];
290const GrooveTemplate* GrooveTemplateManager::getTemplateByName (
const juce::String& name)
292 for (
auto gt : activeGrooves)
303 for (
auto gt : activeGrooves)
309void GrooveTemplateManager::updateTemplate (
int index,
const GrooveTemplate& gt)
311 index = knownGrooves.indexOf (activeGrooves[index]);
314 auto n = gt.getName().trim();
317 n =
TRANS(
"Unnamed");
319 n = n.substring (0, 32);
321 auto originalName = n;
323 if (originalName.trim().endsWithChar (
')'))
325 const int openBracks = originalName.lastIndexOfChar (
'(');
326 const int closeBracks = originalName.lastIndexOfChar (
')');
328 if (openBracks > 0 && closeBracks > openBracks
329 && originalName.substring (openBracks + 1, closeBracks).containsOnly (
"0123456789"))
331 originalName = originalName.substring (0, openBracks).trim();
335 if (knownGrooves[index] !=
nullptr)
336 knownGrooves.remove (index);
338 useParameterizedGrooves (useParameterized);
341 while (getTemplateByName (n) !=
nullptr)
344 auto newGroove = knownGrooves.insert (index,
new GrooveTemplate (gt));
345 newGroove->setName (n);
346 newGroove->setParameterized (useParameterized);
349 useParameterizedGrooves (useParameterized);
353void GrooveTemplateManager::deleteTemplate (
int index)
355 knownGrooves.removeObject (activeGrooves[index]);
357 useParameterizedGrooves (useParameterized);
void addChildElement(XmlElement *newChildElement) noexcept
bool getBoolAttribute(StringRef attributeName, bool defaultReturnValue=false) const
bool hasTagName(StringRef possibleTagName) const noexcept
int getIntAttribute(StringRef attributeName, int defaultReturnValue=0) const
const String & getStringAttribute(StringRef attributeName) const noexcept
void setAttribute(const Identifier &attributeName, const String &newValue)
The Tracktion Edit class!
TempoSequence tempoSequence
The global TempoSequence of this Edit.
The Engine is the central class for all tracktion sessions.
PropertyStorage & getPropertyStorage() const
Returns the PropertyStorage user settings customisable XML file.
void reload()
called when usersettings change, because that's where the grooves are kept.
TimePosition toTime(BeatPosition) const
Converts a number of beats a time.
BeatPosition toBeats(TimePosition) const
Converts a time to a number of beats.
static std::vector< std::unique_ptr< ScopedContextAllocator > > restartAllTransports(Engine &, bool clearDevices)
Restarts all TransportControl[s] in the Edit.
#define TRANS(stringLiteral)
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
std::unique_ptr< XmlElement > parseXML(const String &textToParse)
int roundToInt(const FloatType value) noexcept
juce::String getName(LaunchQType t)
Retuns the name of a LaunchQType for display purposes.
Represents a position in beats.
constexpr double inBeats() const
Returns the position as a number of beats.
Represents a position in real-life time.