Check boundries when transposing clips

and move transpose function to NoteVector
This commit is contained in:
allejok96
2022-05-24 21:35:54 +02:00
parent 0861d30616
commit d56c0fe4bd
6 changed files with 92 additions and 12 deletions

View File

@@ -72,7 +72,6 @@ public:
// Split the list of notes on the given position
void splitNotes(NoteVector notes, TimePos pos);
void transpose(int semitones);
// clip-type stuff
inline MidiClipTypes type() const

View File

@@ -244,7 +244,14 @@ private:
};
typedef QVector<Note *> NoteVector;
class NoteVector: public QVector<Note*>
{
public:
bool getBounds(TimePos& start, TimePos& end, int& lower, int& upper) const;
void transpose(int semitones) const;
};
#endif

View File

@@ -58,6 +58,7 @@ void JournallingObject::addJournalCheckPoint()
if( isJournalling() )
{
Engine::projectJournal()->addJournalCheckPoint( this );
// TODO Engine::getSong()->setModified() ?
}
}

View File

@@ -232,3 +232,59 @@ bool Note::withinRange(int tickStart, int tickEnd) const
return pos().getTicks() >= tickStart && pos().getTicks() <= tickEnd
&& length().getTicks() != 0;
}
/*! \brief Get the outer bounds of the notes
*
* \param start - Will be set to the first time position
* \param end - Will be set to the last end position
* \param lower - Will be set to the lowest key
* \param upper - Will be set to the highest key
* \return false if there are no notes
*/
bool NoteVector::getBounds(TimePos& start, TimePos& end, int& lower, int& upper) const
{
if (empty()) { return false; }
start = first()->pos();
end = start;
lower = first()->key();
upper = lower;
for (const Note* note: *this)
{
// TODO should we assume that NoteVector is always sorted correctly,
// so first() always has the lowest time position?
start = std::min(start, note->pos());
end = std::max(end, note->endPos());
lower = std::min(lower, note->key());
upper = std::max(upper, note->key());
}
return true;
}
/*! \brief Transpose all notes in the vector by X semitones
*
* Notes will be hard-clipped to the MIDI note range. To prevent this use getBounds() prior to transposing.
*
* \param semitones Semitones to transpose
*/
void NoteVector::transpose(int semitones) const
{
if (empty() || !semitones) { return; }
for (Note* note: *this)
{
note->setKey(note->key() + semitones);
}
}

View File

@@ -144,7 +144,28 @@ void MidiClipView::changeName()
void MidiClipView::transposeSelection()
{
int semitones = QInputDialog::getInt(this, tr("Transpose"), tr("Semitones to transpose:"), 0, -NumKeys, NumKeys, 1);
const auto selection = getClickedClips();
// Calculate the key boundries for all clips
int highest = 0;
int lowest = NumKeys - 1;
for (ClipView* clipview: selection)
{
if (auto mcv = dynamic_cast<MidiClipView*>(clipview))
{
TimePos start, end;
int low, high;
if (mcv->getMidiClip()->notes().getBounds(start, end, low, high))
{
lowest = std::min(low, lowest);
highest = std::max(high, highest);
}
}
}
int minStep = 0 - lowest;
int maxStep = NumKeys - 1 - highest;
int semitones = QInputDialog::getInt(this, tr("Transpose"), tr("Semitones to transpose:"), 0, minStep, maxStep, 1);
if (semitones == 0) { return; }
// TODO make this not crash
@@ -161,9 +182,12 @@ void MidiClipView::transposeSelection()
track->addJournalCheckPoint();
m_changedTracks.insert(track);
}
mcv->getMidiClip()->transpose(semitones);
auto clip = mcv->getMidiClip();
clip->notes().transpose(semitones);
emit clip->dataChanged();
}
}
Engine::getSong()->setModified();
}

View File

@@ -346,15 +346,8 @@ void MidiClip::splitNotes(NoteVector notes, TimePos pos)
void MidiClip::transpose(int semitones)
{
if (notes().empty()) { return; }
for (Note* note: notes())
{
note->setKey(note->key() + semitones);
}
}