Check boundries when transposing clips
and move transpose function to NoteVector
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -58,6 +58,7 @@ void JournallingObject::addJournalCheckPoint()
|
||||
if( isJournalling() )
|
||||
{
|
||||
Engine::projectJournal()->addJournalCheckPoint( this );
|
||||
// TODO Engine::getSong()->setModified() ?
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user