Note* Glissando::guessFinalNote(Chord* chord) { int chordTrack = chord->track(); Segment* segm = chord->segment(); Part* part = chord->staff()->part(); if (segm != nullptr) segm = segm->next1(); while (segm) { // if next segment is a ChordRest segment if (segm->segmentType() == Segment::Type::ChordRest) { // look for a Chord in the same track and returns its top note, if found if (segm->element(chordTrack) && segm->element(chordTrack)->type() == Element::Type::CHORD) return static_cast<Chord*>(segm->element(chordTrack))->upNote(); // if no chord, look for other chords in the same instrument for (Element* currChord : segm->elist()) if (currChord != nullptr && currChord->type() == Element::Type::CHORD && static_cast<Chord*>(currChord)->staff()->part() == part) return static_cast<Chord*>(currChord)->upNote(); } segm = segm->next1(); } qDebug("no second note for glissando found"); return nullptr; }
Note* Glissando::guessFinalNote(Chord* chord) { switch (chord->noteType()) { // case NoteType::INVALID: // return nullptr; // for grace notes before, return top note of parent chord // TODO : if the grace-before is not the LAST ONE, this still returns the main note // which is probably not correct; however a glissando between two grace notes // probably makes little sense. case NoteType::ACCIACCATURA: case NoteType::APPOGGIATURA: case NoteType::GRACE4: case NoteType::GRACE16: case NoteType::GRACE32: if (chord->parent() && chord->parent()->type() == Element::Type::CHORD) return static_cast<Chord*>(chord->parent())->upNote(); else // no parent or parent is not a chord? return nullptr; // for grace notes after, next chord is next chord of parent chord // TODO : same note as case above! case NoteType::GRACE8_AFTER: case NoteType::GRACE16_AFTER: case NoteType::GRACE32_AFTER: // move unto parent chord and proceed to standard case if (chord->parent() && chord->parent()->type() == Element::Type::CHORD) chord = static_cast<Chord*>(chord->parent()); else return nullptr; break; case NoteType::NORMAL: { // if chord has grace notes after, the first one is the next note QVector<Chord*>graces = chord->graceNotesAfter(); if (graces.size() > 0) return graces.first()->upNote(); } break; default: break; } // standard case (NORMAL or grace after chord) // if parent not a segment, can't locate a target note if (chord->parent()->type() != Element::Type::SEGMENT) return nullptr; // look for first ChordRest segment after initial note is elapsed Segment* segm = chord->score()->tick2rightSegment(chord->tick() + chord->actualTicks()); int chordTrack = chord->track(); Part* part = chord->part(); while (segm) { // if next segment is a ChordRest segment if (segm->segmentType() == Segment::Type::ChordRest) { Chord* target = nullptr; // look for a Chord in the same track if (segm->element(chordTrack) && segm->element(chordTrack)->type() == Element::Type::CHORD) target = static_cast<Chord*>(segm->element(chordTrack)); else // if no same track, look for other chords in the same instrument for (Element* currChord : segm->elist()) if (currChord != nullptr && currChord->type() == Element::Type::CHORD && static_cast<Chord*>(currChord)->part() == part) { target = static_cast<Chord*>(currChord); break; } // if we found a target next chord if (target) { // if chord has grace notes before, the first one is the next note QVector<Chord*>graces = target->graceNotesBefore(); if (graces.size() > 0) return graces.first()->upNote(); return target->upNote(); // if no grace before, return top note } } segm = segm->next1(); } qDebug("no second note for glissando found"); return nullptr; }
void StringData::fretChords(Chord * chord) const { int nFret, nNewFret, nTempFret; int nString, nNewString, nTempString; if(bFretting) return; bFretting = true; // we need to keep track of each string we allocate ourselves within this algorithm bool bUsed[strings()]; // initially all strings are available for(nString=0; nString<strings(); nString++) bUsed[nString] = false; // we also need the notes sorted in order of string (from highest to lowest) and then pitch QMap<int, Note *> sortedNotes; int count = 0; // if chord parent is not a segment, the chord is special (usually a grace chord): // fret it by itself, ignoring the segment if (chord->parent()->type() != Element::Type::SEGMENT) sortChordNotes(sortedNotes, chord, &count); else { // scan each chord of seg from same staff as 'chord', inserting each of its notes in sortedNotes Segment* seg = chord->segment(); int trk; int trkFrom = (chord->track() / VOICES) * VOICES; int trkTo = trkFrom + VOICES; for(trk = trkFrom; trk < trkTo; ++trk) { Element* ch = seg->elist().at(trk); if (ch && ch->type() == Element::Type::CHORD) sortChordNotes(sortedNotes, static_cast<Chord*>(ch), &count); } } // scan chord notes from highest, matching with strings from the highest foreach(Note * note, sortedNotes) { nString = nNewString = note->string(); nFret = nNewFret = note->fret(); note->setFretConflict(false); // assume no conflicts on this note // if no fretting yet or current fretting is no longer valid if (nString == STRING_NONE || nFret == FRET_NONE || getPitch(nString, nFret) != note->pitch()) { // get a new fretting if(!convertPitch(note->pitch(), &nNewString, &nNewFret) ) { // no way to fit this note in this tab: // mark as fretting conflict note->setFretConflict(true); // store fretting change without affecting chord context if (nFret != nNewFret) note->score()->undoChangeProperty(note, P_ID::FRET, nNewFret); if (nString != nNewString) note->score()->undoChangeProperty(note, P_ID::STRING, nNewString); continue; } // check this note is not using the same string of another note of this chord foreach(Note * note2, sortedNotes) { // if same string... if(note2 != note && note2->string() == nNewString) { // ...attempt to fret this note on its old string if( (nTempFret=fret(note->pitch(), nString)) != FRET_NONE) { nNewFret = nTempFret; nNewString = nString; } break; } } } // check we are not reusing a string we already used if(bUsed[nNewString]) { // ...try with each other string, from the highest for(nTempString=0; nTempString < strings(); nTempString++) { if(bUsed[nTempString]) continue; if( (nTempFret=fret(note->pitch(), nTempString)) != FRET_NONE) { // suitable string found nNewFret = nTempFret; nNewString = nTempString; break; } } // if we run out of strings if(nTempString >= strings()) { // no way to fit this chord in this tab: // mark this note as fretting conflict note->setFretConflict(true); // continue; } } // if fretting did change, store as a fret change if (nFret != nNewFret) note->score()->undoChangeProperty(note, P_ID::FRET, nNewFret); if (nString != nNewString) note->score()->undoChangeProperty(note, P_ID::STRING, nNewString); bUsed[nNewString] = true; // string is used }
Note* Glissando::guessInitialNote(Chord* chord) { switch (chord->noteType()) { // case NoteType::INVALID: // return nullptr; // for grace notes before, previous chord is previous chord of parent chord case NoteType::ACCIACCATURA: case NoteType::APPOGGIATURA: case NoteType::GRACE4: case NoteType::GRACE16: case NoteType::GRACE32: // move unto parent chord and proceed to standard case if (chord->parent() && chord->parent()->type() == Element::Type::CHORD) chord = static_cast<Chord*>(chord->parent()); else return nullptr; break; // for grace notes after, return top note of parent chord case NoteType::GRACE8_AFTER: case NoteType::GRACE16_AFTER: case NoteType::GRACE32_AFTER: if (chord->parent() && chord->parent()->type() == Element::Type::CHORD) return static_cast<Chord*>(chord->parent())->upNote(); else // no parent or parent is not a chord? return nullptr; case NoteType::NORMAL: { // if chord has grace notes before, the last one is the previous note QVector<Chord*>graces = chord->graceNotesBefore(); if (graces.size() > 0) return graces.last()->upNote(); } break; // else process to standard case default: break; } // standard case (NORMAL or grace before chord) // if parent not a segment, can't locate a target note if (chord->parent()->type() != Element::Type::SEGMENT) return nullptr; int chordTrack = chord->track(); Segment* segm = chord->segment(); Part* part = chord->part(); if (segm != nullptr) segm = segm->prev1(); while (segm) { // if previous segment is a ChordRest segment if (segm->segmentType() == Segment::Type::ChordRest) { Chord* target = nullptr; // look for a Chord in the same track if (segm->element(chordTrack) && segm->element(chordTrack)->type() == Element::Type::CHORD) target = static_cast<Chord*>(segm->element(chordTrack)); else // if no same track, look for other chords in the same instrument for (Element* currChord : segm->elist()) if (currChord != nullptr && currChord->type() == Element::Type::CHORD && static_cast<Chord*>(currChord)->part() == part) { target = static_cast<Chord*>(currChord); break; } // if we found a target previous chord if (target) { // if chord has grace notes after, the last one is the previous note QVector<Chord*>graces = target->graceNotesAfter(); if (graces.size() > 0) return graces.last()->upNote(); return target->upNote(); // if no grace after, return top note } } segm = segm->prev1(); } qDebug("no first note for glissando found"); return nullptr; }
void StringData::fretChords(Chord * chord) const { int nFret, minFret, maxFret, nNewFret, nTempFret; int nString, nNewString, nTempString; if(bFretting) return; bFretting = true; // we need to keep track of string allocation int bUsed[strings()]; // initially all strings are available for(nString=0; nString<strings(); nString++) bUsed[nString] = 0; // we also need the notes sorted in order of string (from highest to lowest) and then pitch QMap<int, Note *> sortedNotes; int count = 0; // store staff pitch offset at this tick, to speed up actual note pitch calculations // (ottavas not implemented yet) int transp = chord->staff() ? chord->part()->instrument()->transpose().chromatic : 0; // TODO: tick? int pitchOffset = /*chord->staff()->pitchOffset(chord->segment()->tick())*/ - transp; // if chord parent is not a segment, the chord is special (usually a grace chord): // fret it by itself, ignoring the segment if (chord->parent()->type() != ElementType::SEGMENT) sortChordNotes(sortedNotes, chord, pitchOffset, &count); else { // scan each chord of seg from same staff as 'chord', inserting each of its notes in sortedNotes Segment* seg = chord->segment(); int trk; int trkFrom = (chord->track() / VOICES) * VOICES; int trkTo = trkFrom + VOICES; for(trk = trkFrom; trk < trkTo; ++trk) { Element* ch = seg->elist().at(trk); if (ch && ch->type() == ElementType::CHORD) sortChordNotes(sortedNotes, toChord(ch), pitchOffset, &count); } } // determine used range of frets minFret = INT32_MAX; maxFret = INT32_MIN; foreach(Note* note, sortedNotes) { if (note->string() != STRING_NONE) bUsed[note->string()]++; if (note->fret() != FRET_NONE && note->fret() < minFret) minFret = note->fret(); if (note->fret() != FRET_NONE && note->fret() > maxFret) maxFret = note->fret(); } // scan chord notes from highest, matching with strings from the highest foreach(Note * note, sortedNotes) { nString = nNewString = note->string(); nFret = nNewFret = note->fret(); note->setFretConflict(false); // assume no conflicts on this note // if no fretting (any invalid fretting has been erased by sortChordNotes() ) if (nString == STRING_NONE /*|| nFret == FRET_NONE || getPitch(nString, nFret) != note->pitch()*/) { // get a new fretting if (!convertPitch(note->pitch(), pitchOffset, &nNewString, &nNewFret) ) { // no way to fit this note in this tab: // mark as fretting conflict note->setFretConflict(true); // store fretting change without affecting chord context if (nFret != nNewFret) note->undoChangeProperty(Pid::FRET, nNewFret); if (nString != nNewString) note->undoChangeProperty(Pid::STRING, nNewString); continue; } // note can be fretted: use string else { bUsed[nNewString]++; } } // if the note string (either original or newly assigned) is also used by another note if (bUsed[nNewString] > 1) { // attempt to find a suitable string, from topmost for (nTempString=0; nTempString < strings(); nTempString++) { if (bUsed[nTempString] < 1 && (nTempFret=fret(note->pitch(), nTempString, pitchOffset)) != FRET_NONE) { bUsed[nNewString]--; // free previous string bUsed[nTempString]++; // and occupy new string nNewFret = nTempFret; nNewString = nTempString; break; } } } // TODO : try to optimize used fret range, avoiding eccessively open positions // if fretting did change, store as a fret change if (nFret != nNewFret) note->undoChangeProperty(Pid::FRET, nNewFret); if (nString != nNewString) note->undoChangeProperty(Pid::STRING, nNewString); }