diff --git a/changes b/changes index 5dfe97cce6761fae302da28a7198b10f6f9d8377..4f23e143d72c9d0dc4b9e6ebd6db7f03320a2e7b 100644 --- a/changes +++ b/changes @@ -1,5 +1,7 @@ 1.??? + - new method to make random melodies more... melodic - melody can be randomized from selected notes, i.e. pentatonic scale + - so added level with pentatonic scales - added notifications about too hi/low input volume - improved look of sound views, better fit to high DPI screens ANDROID diff --git a/src/libs/main/exam/trandmelody.cpp b/src/libs/main/exam/trandmelody.cpp index 4fa9494cb74fbb145318f9bc1c1668ea753f8bd1..faf8cee46726a0d60ca6b37e4e40d0f05b41aa75 100644 --- a/src/libs/main/exam/trandmelody.cpp +++ b/src/libs/main/exam/trandmelody.cpp @@ -93,3 +93,69 @@ void getRandomMelody(QList<TQAgroup>& qList, Tmelody* mel, int len, bool inKey, } } + +/** + * Generates random melody with following way: + * If @p inKey is required creates list of notes in given key only + * Random algorithm works with phrases + * 1. Randomizes length of phrase (half of total length max) + * 2. Randomizes first note in the phrase (from whole list - scale) + * 3. Randomizes direction (ascending/descending) + * 4. Takes notes from the list starting from random first one in random direction. + * 5. etc, etc, until melody @p len is fulfilled + * 6. Looks for tonic note at the end if required (@p onTonic) + */ +void getRandomMelodyNG(QList<TQAgroup>& qList, Tmelody* mel, int len, bool inKey, bool onTonic) { + QList<TQAgroup>* qListPtr = &qList; + QList<TQAgroup> inKeyList; + if (inKey) { // create list with notes in required key signature only + for (TQAgroup qa : qList) { + TQAgroup g; + g.note = mel->key().inKey(qa.note); + if (g.note.isValid()) { + g.pos = qa.pos; + inKeyList << g; + } + } + if (inKeyList.isEmpty()) + qDebug() << "[getRandomMelodyNG] Question list has no any note in key" << mel->key().getName(); + else + qListPtr = &inKeyList; + } + + qsrand(QDateTime::currentDateTime().toTime_t()); + while (mel->length() < len) { + int phLen = len < 4 ? len : qBound(2, 2 + qrand() % (len / 2 - 1), len); + int dir = qrand() % 2 == 1 ? 1 : -1; // direction of melody (ascending or descending) + int noteId = qrand() % qListPtr->size(); + int notesCnt = 0; + while (notesCnt < phLen && noteId < qListPtr->size() && mel->length() < len) { + auto curQA = &qListPtr->operator[](noteId); + mel->addNote(Tchunk(curQA->note, Trhythm(Trhythm::e_none), curQA->pos)); + notesCnt++; + noteId += dir; + if (noteId < 0 || noteId == qListPtr->size()) + break; + } + } + if (onTonic) { + auto tonic = mel->key().tonicNote(); + QList<int> tonicList; + for (int n = 0; n < qListPtr->size(); ++n) { + auto qa = &qListPtr->operator[](n); + if (qa->note.note == tonic.note && qa->note.alter == tonic.alter) + tonicList << n; + } + if (tonicList.isEmpty()) + qDebug() << "Tonic note of" << mel->key().getName() << "was not found"; + else { + int tonicRandNr = tonicList[qrand() % tonicList.size()]; + mel->note(mel->length() - 1)->g() = qListPtr->operator[](tonicRandNr).pos; + mel->note(mel->length() - 1)->p() = qListPtr->operator[](tonicRandNr).note; + } + } +} + + + + diff --git a/src/libs/main/exam/trandmelody.h b/src/libs/main/exam/trandmelody.h index 6e97d64ff38575d9713064a6ec06c3cf633c82a0..98ab1fddb50095ed63b62a995b39d2a687daaf6f 100644 --- a/src/libs/main/exam/trandmelody.h +++ b/src/libs/main/exam/trandmelody.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2014 by Tomasz Bojczuk * + * Copyright (C) 2014-2016 by Tomasz Bojczuk * * seelook@gmail.com * * * * This program is free software; you can redistribute it and/or modify * @@ -24,15 +24,18 @@ class TkeySignature; class Tmelody; -/** - * Generates randomized melody into given reference of @p Tmelody. - * Length is determined by @p len. - * Notes are taken form given question list - * and key signature is respected if @inKey is set to @p true - * Melody is finished on tonic note of the given key signature - * when @p onTonic is set to @p true - */ + /** + * Generates randomized melody into given reference of @p Tmelody. + * Length is determined by @p len. + * Notes are taken form given question list + * and key signature is respected if @inKey is set to @p true + * Melody is finished on tonic note of the given key signature + * when @p onTonic is set to @p true + */ void getRandomMelody(QList<TQAgroup>& qList, Tmelody* mel, int len, bool inKey, bool onTonic); -#endif // TRANDMELODY_H \ No newline at end of file +void getRandomMelodyNG(QList<TQAgroup>& qList, Tmelody* mel, int len, bool inKey, bool onTonic); + + +#endif // TRANDMELODY_H diff --git a/src/libs/main/gui/tmelman.cpp b/src/libs/main/gui/tmelman.cpp index 8ef74e7f6a15ca8977d22b09daa0c69b0490a056..a08601386f4688cf9d1e786f3de12cd8b6a9a60d 100644 --- a/src/libs/main/gui/tmelman.cpp +++ b/src/libs/main/gui/tmelman.cpp @@ -16,6 +16,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ***************************************************************************/ + #include "tmelman.h" #include "tmenu.h" #include "score/tmainscore.h" @@ -146,7 +147,7 @@ void TmelMan::randomizeMelodySlot() { } Tmelody *mel = new Tmelody(QString(), m_score->keySignature()); mel->setClef(m_score->clef().type()); - getRandomMelody(ql, mel, 14, true, true); + getRandomMelodyNG(ql, mel, 14, true, true); m_score->setMelody(mel); delete mel; } diff --git a/src/plugins/exam/texamexecutor.cpp b/src/plugins/exam/texamexecutor.cpp index 405516bc2885c4f9167d882f4106c0c15efb7e6c..886ca751e3d44dcfe332e763c7980f513c14a66a 100755 --- a/src/plugins/exam/texamexecutor.cpp +++ b/src/plugins/exam/texamexecutor.cpp @@ -87,17 +87,17 @@ QString getExamFileName(Texam* e) { TexamExecutor::TexamExecutor(QObject* parent) : QObject(parent), - m_exam(0), mW(MAINVIEW->mainWindow()), + m_supp(nullptr), + m_exam(nullptr), + m_snifferLocked(false), m_lockRightButt(false), m_goingClosed(false), - m_snifferLocked(false), - m_canvas(0), - m_supp(0), - m_penalty(0), - m_exercise(0), + m_canvas(nullptr), + m_penalty(nullptr), + m_exercise(nullptr), m_blindCounter(0), - m_rand(0) + m_rand(nullptr) { } @@ -188,6 +188,7 @@ void TexamExecutor::init(QString examFile, Tlevel *lev) { else emit examMessage(Torders::e_examSingle); m_supp = new TexecutorSupply(&m_level, this); + // TODO: when level has its own list of notes for melodies - it is wasting energy! m_supp->createQuestionsList(m_questList); if (m_exam->melodies()) m_melody = new TexamMelody(this); @@ -317,7 +318,7 @@ void TexamExecutor::askQuestion(bool isAttempt) { } if (m_penalty->isNot() && curQ->questionAsFret() && curQ->answerAsFret()) - curQ->qa = m_questList[m_supp->getQAnrForGuitarOnly()]; + curQ->qa = m_questList[m_supp->getQAnrForGuitarOnly()]; if (m_penalty->isNot() && (curQ->questionAsNote() || curQ->answerAsNote())) { if (m_level.useKeySign) @@ -335,9 +336,9 @@ void TexamExecutor::askQuestion(bool isAttempt) { QList<TQAgroup> qaList; m_supp->listForRandomNotes(curQ->key, qaList); // ignore in key (4th param) of level, notes from list are already in key (if required) - getRandomMelody(qaList, curQ->melody(), melodyLength, false, false); + getRandomMelodyNG(qaList, curQ->melody(), melodyLength, false, false); } else - getRandomMelody(m_questList, curQ->melody(), melodyLength, m_level.onlyCurrKey, m_level.endsOnTonic); + getRandomMelodyNG(m_questList, curQ->melody(), melodyLength, m_level.onlyCurrKey, m_level.endsOnTonic); } m_melody->newMelody(curQ->answerAsSound() ? curQ->melody()->length() : 0); // prepare list to store notes played by user or clear it m_exam->newAttempt(); diff --git a/src/plugins/exam/texecutorsupply.cpp b/src/plugins/exam/texecutorsupply.cpp index 9871ea02646fdd35692dba4afaf8bca7d9e97f68..c3653daaee16e609c87859b52501939827b257a1 100644 --- a/src/plugins/exam/texecutorsupply.cpp +++ b/src/plugins/exam/texecutorsupply.cpp @@ -608,8 +608,8 @@ void TexecutorSupply::resetKeyRandom() { bool TexecutorSupply::isNoteInKey(Tnote& n) { if (m_level->isSingleKey) { - if(m_level->loKey.inKey(n).note != 0) - return true; + if (m_level->loKey.inKey(n).isValid()) + return true; } else { for (int k = m_level->loKey.value(); k <= m_level->hiKey.value(); k++) { if (TkeySignature::inKey(TkeySignature(k), n).note != 0)