More fixes and tests for chained mixes

parent a3bc1439
......@@ -255,7 +255,7 @@ bool ClipModel::requestResize(int size, bool right, Fun &undo, Fun &redo, bool l
return false;
};
if (operation()) {
Fun reverse = [hasMix]() { return true; };
Fun reverse = []() { return true; };
if (logUndo) {
// Now, we are in the state in which the timeline should be when we try to revert current action. So we can build the reverse action from here
if (m_currentTrackId != -1) {
......
......@@ -842,6 +842,10 @@ bool TimelineModel::mixClip(int idToMove)
nextClip = getTrackById_const(selectedTrack)->getClipByPosition(mixPosition + clipDuration + 1);
} else if (getTrackById_const(selectedTrack)->hasEndMix(idToMove)) {
previousClip = getTrackById_const(selectedTrack)->getClipByPosition(mixPosition - 1);
if (previousClip > -1 && getTrackById_const(selectedTrack)->hasEndMix(previousClip)) {
// Could happen if 2 clips before are mixed to full length
previousClip = -1;
}
} else {
previousClip = getTrackById_const(selectedTrack)->getClipByPosition(mixPosition - 1);
nextClip = getTrackById_const(selectedTrack)->getClipByPosition(mixPosition + clipDuration + 1);
......
......@@ -1443,7 +1443,8 @@ bool TrackModel::requestClipMix(std::pair<int, int> clipIds, int mixDuration, bo
int source_track;
int mixPosition;
int dest_track = 1;
qDebug()<<"=========MIXING CLIPS: "<<clipIds;
bool remixPlaylists = false;
bool clipHasEndMix = false;
if (auto ptr = m_parent.lock()) {
// The clip that will be moved to playlist 1
std::shared_ptr<ClipModel> secondClip(ptr->getClipPtr(clipIds.second));
......@@ -1452,14 +1453,26 @@ bool TrackModel::requestClipMix(std::pair<int, int> clipIds, int mixDuration, bo
source_track = secondClip->getSubPlaylistIndex();
std::shared_ptr<ClipModel> firstClip(ptr->getClipPtr(clipIds.first));
firstClipDuration = firstClip->getPlaytime();
// Ensure mix is not longer than clip
// Ensure mix is not longer than clip and doesn't overlap other mixes
firstClipPos = firstClip->getPosition();
mixPosition = qMax(firstClipPos, secondClipPos - mixDuration / 2);
int maxPos = qMin(secondClipPos + secondClipDuration, secondClipPos + mixDuration - (mixDuration / 2));
mixDuration = qMin(mixDuration, maxPos - mixPosition);
if (firstClip->getSubPlaylistIndex() == 1) {
dest_track = 0;
}
mixPosition = qMax(firstClipPos, secondClipPos - mixDuration / 2);
int maxPos = qMin(secondClipPos + secondClipDuration, secondClipPos + mixDuration - (mixDuration / 2));
if (hasStartMix(clipIds.first)) {
std::pair<MixInfo, MixInfo> mixData = getMixInfo(clipIds.first);
mixPosition = qMax(mixData.first.firstClipInOut.second, mixPosition);
}
if (hasEndMix(clipIds.second)) {
std::pair<MixInfo, MixInfo> mixData = getMixInfo(clipIds.second);
clipHasEndMix = true;
maxPos = qMin(mixData.second.secondClipInOut.first, maxPos);
if (ptr->m_allClips[mixData.second.secondClipId]->getSubPlaylistIndex() == dest_track) {
remixPlaylists = true;
}
}
mixDuration = qMin(mixDuration, maxPos - mixPosition);
} else {
// Error, timeline unavailable
return false;
......@@ -1468,13 +1481,14 @@ bool TrackModel::requestClipMix(std::pair<int, int> clipIds, int mixDuration, bo
// Rearrange subsequent mixes
Fun rearrange_playlists = []() { return true; };
Fun rearrange_playlists_undo = []() { return true; };
if (source_track != dest_track && hasEndMix(clipIds.second)) {
if (remixPlaylists && source_track != dest_track) {
// A list of clip ids x playlists
QMap<int, int> rearrangedPlaylists;
int ix = 0;
int moveId = m_mixList.value(clipIds.second, -1);
while (moveId > -1) {
rearrangedPlaylists.insert(moveId, ix % 2 ? 1 - dest_track : dest_track);
int current = m_allClips[moveId]->getSubPlaylistIndex();
rearrangedPlaylists.insert(moveId, current);
if (hasEndMix(moveId)) {
moveId = m_mixList.value(moveId, -1);
} else {
......@@ -1482,31 +1496,106 @@ bool TrackModel::requestClipMix(std::pair<int, int> clipIds, int mixDuration, bo
}
ix++;
}
rearrange_playlists = [this, rearrangedPlaylists]() {
bool result = true;
// First, remove all clips on playlist 0
QMapIterator<int, int> i(rearrangedPlaylists);
i.toBack();
while (i.hasPrevious()) {
i.previous();
result = switchPlaylist(i.key(), m_allClips[i.key()]->getPosition(), m_allClips[i.key()]->getSubPlaylistIndex(), i.value());
if (!result) {
break;
while (i.hasNext()) {
i.next();
if (i.value() == 0) {
int target_clip = m_playlists[0].get_clip_index_at(m_allClips[i.key()]->getPosition());
std::unique_ptr<Mlt::Producer> prod(m_playlists[0].replace_with_blank(target_clip));
}
m_playlists[0].consolidate_blanks();
}
return true;
// Then move all clips from playlist 1 to playlist 0
i.toFront();
if (auto ptr = m_parent.lock()) {
while (i.hasNext()) {
i.next();
if (i.value() == 1) {
// Remove
int pos = m_allClips[i.key()]->getPosition();
int target_clip = m_playlists[1].get_clip_index_at(pos);
std::unique_ptr<Mlt::Producer> prod(m_playlists[1].replace_with_blank(target_clip));
// Replug
std::shared_ptr<ClipModel> clip = ptr->getClipPtr(i.key());
int index = m_playlists[0].insert_at(pos, *clip, 1);
clip->setSubPlaylistIndex(0);
m_playlists[0].consolidate_blanks();
}
m_playlists[1].consolidate_blanks();
}
// Finally replug playlist 0 clips in playlist 1 and fix transition direction
i.toFront();
while (i.hasNext()) {
i.next();
if (i.value() == 0) {
int pos = m_allClips[i.key()]->getPosition();
std::shared_ptr<ClipModel> clip = ptr->getClipPtr(i.key());
int index = m_playlists[1].insert_at(pos, *clip, 1);
clip->setSubPlaylistIndex(1);
m_playlists[1].consolidate_blanks();
}
if (m_sameCompositions.count(i.key()) > 0) {
// There is a mix at clip start, adjust direction
Mlt::Transition &transition = *m_sameCompositions[i.key()].get();
transition.set("reverse", i.value());
}
}
return true;
} else return false;
};
rearrange_playlists_undo = [this, rearrangedPlaylists]() {
bool result = true;
// First, remove all clips on playlist 1
QMapIterator<int, int> i(rearrangedPlaylists);
i.toBack();
while (i.hasPrevious()) {
i.previous();
result = switchPlaylist(i.key(), m_allClips[i.key()]->getPosition(), m_allClips[i.key()]->getSubPlaylistIndex(), 1 - i.value());
if (!result) {
break;
while (i.hasNext()) {
i.next();
if (i.value() == 0) {
int target_clip = m_playlists[1].get_clip_index_at(m_allClips[i.key()]->getPosition());
std::unique_ptr<Mlt::Producer> prod(m_playlists[1].replace_with_blank(target_clip));
}
m_playlists[1].consolidate_blanks();
}
return true;
// Then move all clips from playlist 0 to playlist 1
i.toFront();
if (auto ptr = m_parent.lock()) {
while (i.hasNext()) {
i.next();
if (i.value() == 1) {
// Remove
int pos = m_allClips[i.key()]->getPosition();
int target_clip = m_playlists[0].get_clip_index_at(pos);
std::unique_ptr<Mlt::Producer> prod(m_playlists[0].replace_with_blank(target_clip));
// Replug
std::shared_ptr<ClipModel> clip = ptr->getClipPtr(i.key());
int index = m_playlists[1].insert_at(pos, *clip, 1);
clip->setSubPlaylistIndex(1);
m_playlists[1].consolidate_blanks();
}
m_playlists[0].consolidate_blanks();
}
// Finally replug playlist 1 clips in playlist 0 and fix transition direction
i.toFront();
while (i.hasNext()) {
i.next();
if (i.value() == 0) {
int pos = m_allClips[i.key()]->getPosition();
std::shared_ptr<ClipModel> clip = ptr->getClipPtr(i.key());
int index = m_playlists[0].insert_at(pos, *clip, 1);
clip->setSubPlaylistIndex(0);
m_playlists[0].consolidate_blanks();
}
if (m_sameCompositions.count(i.key()) > 0) {
// There is a mix at clip start, adjust direction
Mlt::Transition &transition = *m_sameCompositions[i.key()].get();
transition.set("reverse", 1 - i.value());
}
}
return true;
} else return false;
};
}
// Create mix compositing
......@@ -1557,7 +1646,7 @@ bool TrackModel::requestClipMix(std::pair<int, int> clipIds, int mixDuration, bo
auto operation = requestClipDeletion_lambda(clipIds.second, updateView, finalMove, groupMove, finalMove);
bool res = operation();
if (res) {
Fun replay = [this, clipIds, dest_track, firstClipPos, secondClipDuration, mixPosition, mixDuration, build_mix, secondClipPos, updateView, finalMove, groupMove, rearrange_playlists]() {
Fun replay = [this, clipIds, dest_track, firstClipPos, secondClipDuration, mixPosition, mixDuration, build_mix, secondClipPos, clipHasEndMix, updateView, finalMove, groupMove, rearrange_playlists]() {
if (auto ptr = m_parent.lock()) {
ptr->getClipPtr(clipIds.second)->setSubPlaylistIndex(dest_track);
}
......@@ -1569,8 +1658,8 @@ bool TrackModel::requestClipMix(std::pair<int, int> clipIds, int mixDuration, bo
std::function<bool(void)> local_undo = []() { return true; };
std::function<bool(void)> local_redo = []() { return true; };
if (auto ptr = m_parent.lock()) {
result = ptr->getClipPtr(clipIds.second)->requestResize(secondClipPos + secondClipDuration - mixPosition, false, local_undo, local_redo, false);
result = ptr->getClipPtr(clipIds.first)->requestResize(mixPosition + mixDuration - firstClipPos, true, local_undo, local_redo, false);
result = ptr->getClipPtr(clipIds.second)->requestResize(secondClipPos + secondClipDuration - mixPosition, false, local_undo, local_redo, clipHasEndMix);
result = result && ptr->getClipPtr(clipIds.first)->requestResize(mixPosition + mixDuration - firstClipPos, true, local_undo, local_redo, false);
QModelIndex ix = ptr->makeClipIndexFromID(clipIds.second);
emit ptr->dataChanged(ix, ix, {TimelineModel::StartRole,TimelineModel::MixRole});
}
......@@ -1590,9 +1679,9 @@ bool TrackModel::requestClipMix(std::pair<int, int> clipIds, int mixDuration, bo
if (auto ptr = m_parent.lock()) {
ptr->getClipPtr(clipIds.second)->setSubPlaylistIndex(source_track);
}
rearrange_playlists_undo();
auto op = requestClipInsertion_lambda(clipIds.second, secondClipPos, updateView, finalMove, groupMove);
op();
rearrange_playlists_undo();
return true;
};
res = res && replay();
......
......@@ -355,6 +355,84 @@ TEST_CASE("Simple Mix", "[SameTrackMix]")
state0();
}
SECTION("Test chained mixes on color clips")
{
// Add 2 more color clips
int cid5;
int cid6;
int cid7;
state0();
REQUIRE(timeline->requestClipInsertion(binId2, tid2, 540, cid5));
REQUIRE(timeline->requestItemResize(cid5, 20, true, true));
REQUIRE(timeline->requestClipInsertion(binId2, tid2, 560, cid6));
REQUIRE(timeline->requestItemResize(cid6, 20, true, true));
REQUIRE(timeline->requestClipInsertion(binId2, tid2, 580, cid7));
REQUIRE(timeline->requestItemResize(cid7, 20, true, true));
// Cid3 pos=500, duration=20
// Cid4 pos=520, duration=20
// Cid5 pos=540, duration=20
// Cid6 pos=560, duration=20
// Mix 3 and 4
REQUIRE(timeline->mixClip(cid4));
REQUIRE(timeline->m_allClips[cid3]->getSubPlaylistIndex() == 0);
REQUIRE(timeline->m_allClips[cid4]->getSubPlaylistIndex() == 1);
REQUIRE(timeline->m_allClips[cid5]->getSubPlaylistIndex() == 0);
REQUIRE(timeline->m_allClips[cid6]->getSubPlaylistIndex() == 0);
REQUIRE(timeline->m_allClips[cid7]->getSubPlaylistIndex() == 0);
REQUIRE(timeline->getTrackById_const(tid2)->mixCount() == 1);
// Mix 6 and 7
REQUIRE(timeline->mixClip(cid7));
REQUIRE(timeline->m_allClips[cid3]->getSubPlaylistIndex() == 0);
REQUIRE(timeline->m_allClips[cid4]->getSubPlaylistIndex() == 1);
REQUIRE(timeline->m_allClips[cid5]->getSubPlaylistIndex() == 0);
REQUIRE(timeline->m_allClips[cid6]->getSubPlaylistIndex() == 0);
REQUIRE(timeline->m_allClips[cid7]->getSubPlaylistIndex() == 1);
REQUIRE(timeline->getTrackById_const(tid2)->mixCount() == 2);
// Mix 5 and 6
REQUIRE(timeline->mixClip(cid6));
REQUIRE(timeline->m_allClips[cid3]->getSubPlaylistIndex() == 0);
REQUIRE(timeline->m_allClips[cid4]->getSubPlaylistIndex() == 1);
REQUIRE(timeline->m_allClips[cid5]->getSubPlaylistIndex() == 0);
REQUIRE(timeline->m_allClips[cid6]->getSubPlaylistIndex() == 1);
REQUIRE(timeline->m_allClips[cid7]->getSubPlaylistIndex() == 0);
REQUIRE(timeline->getTrackById_const(tid2)->mixCount() == 3);
// Undo mix 5 and 6
undoStack->undo();
REQUIRE(timeline->m_allClips[cid3]->getSubPlaylistIndex() == 0);
REQUIRE(timeline->m_allClips[cid4]->getSubPlaylistIndex() == 1);
REQUIRE(timeline->m_allClips[cid5]->getSubPlaylistIndex() == 0);
REQUIRE(timeline->m_allClips[cid6]->getSubPlaylistIndex() == 0);
REQUIRE(timeline->m_allClips[cid7]->getSubPlaylistIndex() == 1);
REQUIRE(timeline->getTrackById_const(tid2)->mixCount() == 2);
// Undo mix 6 and 7
undoStack->undo();
REQUIRE(timeline->m_allClips[cid3]->getSubPlaylistIndex() == 0);
REQUIRE(timeline->m_allClips[cid4]->getSubPlaylistIndex() == 1);
REQUIRE(timeline->m_allClips[cid5]->getSubPlaylistIndex() == 0);
REQUIRE(timeline->m_allClips[cid6]->getSubPlaylistIndex() == 0);
REQUIRE(timeline->m_allClips[cid7]->getSubPlaylistIndex() == 0);
REQUIRE(timeline->getTrackById_const(tid2)->mixCount() == 1);
// Undo mix 3 and 4
undoStack->undo();
// Undo insert/resize ops
undoStack->undo();
undoStack->undo();
undoStack->undo();
undoStack->undo();
undoStack->undo();
undoStack->undo();
state0();
}
binModel->clean();
pCore->m_projectManager = nullptr;
Logger::print_trace();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment