Commit 03cf9745 authored by Jean-Baptiste Mardelle's avatar Jean-Baptiste Mardelle
Browse files

Same track mix update: resize both clips and create a mix in between

parent 761393e4
......@@ -668,13 +668,13 @@ bool TimelineModel::requestClipMove(int clipId, int trackId, int position, bool
}
bool TimelineModel::requestClipMixMove(int clipId, int trackId, int position, bool updateView, bool invalidateTimeline, bool finalMove, Fun &undo, Fun &redo, bool groupMove)
bool TimelineModel::requestClipMix(std::pair<int, int> clipIds, int trackId, int position, bool updateView, bool invalidateTimeline, bool finalMove, Fun &undo, Fun &redo, bool groupMove)
{
// qDebug() << "// FINAL MOVE: " << invalidateTimeline << ", UPDATE VIEW: " << updateView<<", FINAL: "<<finalMove;
if (trackId == -1) {
return false;
}
Q_ASSERT(isClip(clipId));
Q_ASSERT(isClip(clipIds.first));
std::function<bool(void)> local_undo = []() { return true; };
std::function<bool(void)> local_redo = []() { return true; };
bool ok = true;
......@@ -683,20 +683,22 @@ bool TimelineModel::requestClipMixMove(int clipId, int trackId, int position, bo
// Move on same track, simply inform the view
updateView = false;
notifyViewOnly = true;
update_model = [clipId, this, trackId, invalidateTimeline]() {
int mixDuration = 8;
update_model = [clipIds, this, trackId, position, invalidateTimeline, mixDuration]() {
qDebug()<<"==== PROCESSING UPDATE MODEL";
QModelIndex modelIndex = makeClipIndexFromID(clipId);
QModelIndex modelIndex = makeClipIndexFromID(clipIds.second);
notifyChange(modelIndex, modelIndex, StartRole);
QModelIndex modelIndex2 = makeClipIndexFromID(clipIds.first);
notifyChange(modelIndex2, modelIndex2, DurationRole);
if (invalidateTimeline && !getTrackById_const(trackId)->isAudioTrack()) {
int in = getClipPosition(clipId);
emit invalidateZone(in, in + getClipPlaytime(clipId));
emit invalidateZone(position - mixDuration, position + mixDuration);
}
return true;
};
if (notifyViewOnly) {
PUSH_LAMBDA(update_model, local_undo);
}
ok = getTrackById(trackId)->requestClipMix(clipId, position, updateView, finalMove, local_undo, local_redo, groupMove);
ok = getTrackById(trackId)->requestClipMix(clipIds, mixDuration, updateView, finalMove, local_undo, local_redo, groupMove);
if (!ok) {
qDebug() << "-------------\nMIX FAILED, REVERTING\n\n-------------------";
bool undone = local_undo();
......@@ -707,7 +709,7 @@ bool TimelineModel::requestClipMixMove(int clipId, int trackId, int position, bo
if (notifyViewOnly) {
PUSH_LAMBDA(update_model, local_redo);
}
qDebug()<<"======== FINISHED MIX CREATION FOR CLIP: "<<clipId;
qDebug()<<"======== FINISHED MIX CREATION FOR CLIP: "<<clipIds;
UPDATE_UNDO_REDO(local_redo, local_undo, undo, redo);
return ok;
......
......@@ -356,7 +356,7 @@ public:
*/
Q_INVOKABLE bool requestClipMove(int clipId, int trackId, int position, bool moveMirrorTracks = true, bool updateView = true, bool logUndo = true, bool invalidateTimeline = false);
bool requestClipMixMove(int clipId, int trackId, int position, bool updateView, bool invalidateTimeline, bool finalMove, Fun &undo, Fun &redo, bool groupMove);
bool requestClipMix(std::pair<int, int> clipIds, int trackId, int position, bool updateView, bool invalidateTimeline, bool finalMove, Fun &undo, Fun &redo, bool groupMove);
/* @brief Move a composition to a specific position This action is undoable
Returns true on success. If it fails, nothing is modified. If the clip is
......
......@@ -519,7 +519,7 @@ int TrackModel::getBlankSizeNearComposition(int compoId, bool after)
return length;
}
Fun TrackModel::requestClipResize_lambda(int clipId, int in, int out, bool right)
Fun TrackModel::requestClipResize_lambda(int clipId, int in, int out, bool right, bool allowMix)
{
QWriteLocker locker(&m_lock);
int clip_position = m_allClips[clipId]->getPosition();
......@@ -598,7 +598,7 @@ Fun TrackModel::requestClipResize_lambda(int clipId, int in, int out, bool right
int blank = -1;
int other_blank_end = getBlankEnd(clip_position, (target_track + 1) % 2);
if (right) {
if (target_clip == m_playlists[target_track].count() - 1 && other_blank_end >= out) {
if (target_clip == m_playlists[target_track].count() - 1 && (allowMix || other_blank_end >= out)) {
// clip is last, it can always be extended
return [this, target_clip, target_track, in, out, update_snaps, clipId]() {
if (isLocked()) return false;
......@@ -634,7 +634,7 @@ Fun TrackModel::requestClipResize_lambda(int clipId, int in, int out, bool right
}
if (m_playlists[target_track].is_blank(blank)) {
int blank_length = m_playlists[target_track].clip_length(blank);
if (blank_length + delta >= 0 && other_blank_end >= out) {
if (blank_length + delta >= 0 && (allowMix || other_blank_end >= out)) {
return [blank_length, blank, right, clipId, delta, update_snaps, this, in, out, target_clip, target_track]() {
if (isLocked()) return false;
int target_clip_mutable = target_clip;
......@@ -1361,22 +1361,30 @@ bool TrackModel::isAvailable(int position, int duration)
return m_playlists[0].is_blank(start_clip);
}
bool TrackModel::requestClipMix(int clipId, int position, bool updateView, bool finalMove, Fun &undo, Fun &redo, bool groupMove)
bool TrackModel::requestClipMix(std::pair<int, int> clipIds, int mixDuration, bool updateView, bool finalMove, Fun &undo, Fun &redo, bool groupMove)
{
QWriteLocker locker(&m_lock);
// By default, insertion occurs in topmost track
// Find out the clip id at position
int clipInitialPos;
int secondClipPos;
int secondClipDuration;
int firstClipPos;
int firstClipDuration;
int source_track;
MixInfo mixInfo;
qDebug()<<"=========MIXING CLIPS: "<<clipIds;
if (auto ptr = m_parent.lock()) {
// The clip that will be moved to playlist 1
std::shared_ptr<ClipModel> movedClip(ptr->getClipPtr(clipId));
source_track = movedClip->getSubPlaylistIndex();
clipInitialPos = movedClip->getPosition();
movedClip->setMixDuration(mixInfo.mixDuration);
mixInfo.mixDuration = clipInitialPos - position;
mixInfo.mixPosition = position;
std::shared_ptr<ClipModel> secondClip(ptr->getClipPtr(clipIds.second));
secondClipDuration = secondClip->getPlaytime() - 1;
secondClipPos = secondClip->getPosition();
source_track = secondClip->getSubPlaylistIndex();
std::shared_ptr<ClipModel> firstClip(ptr->getClipPtr(clipIds.first));
firstClipDuration = firstClip->getPlaytime() - 1;
firstClipPos = firstClip->getPosition();
mixInfo.mixPosition = secondClipPos - mixDuration;
mixInfo.mixDuration = mixDuration * 2;
secondClip->setMixDuration(mixInfo.mixDuration);
} else {
// Error, timeline unavailable
return false;
......@@ -1388,57 +1396,63 @@ bool TrackModel::requestClipMix(int clipId, int position, bool updateView, bool
}
// Create mix compositing
Fun build_mix = [clipId, mixInfo, this]() {
Fun build_mix = [clipIds, mixInfo, this]() {
if (auto ptr = m_parent.lock()) {
std::shared_ptr<ClipModel> movedClip(ptr->getClipPtr(clipId));
std::shared_ptr<ClipModel> movedClip(ptr->getClipPtr(clipIds.second));
movedClip->setMixDuration(mixInfo.mixDuration);
QModelIndex ix = ptr->makeClipIndexFromID(clipId);
QModelIndex ix = ptr->makeClipIndexFromID(clipIds.second);
emit ptr->dataChanged(ix, ix, {TimelineModel::StartRole,TimelineModel::MixRole});
// Insert mix transition
if (isAudioTrack()) {
std::shared_ptr<Mlt::Transition> t(new Mlt::Transition(*ptr->getProfile(), "mix"));
t->set_in_and_out(mixInfo.mixPosition, mixInfo.mixPosition + mixInfo.mixDuration);
m_track->plant_transition(*t.get(), 0, 1);
m_sameCompositions[clipId] = t;
m_sameCompositions[clipIds.second] = t;
} else {
std::shared_ptr<Mlt::Transition> t(new Mlt::Transition(*ptr->getProfile(), "luma"));
t->set_in_and_out(mixInfo.mixPosition, mixInfo.mixPosition + mixInfo.mixDuration);
qDebug()<<"==== INSERTING MIX: : "<<mixInfo.mixPosition<<" - "<<(mixInfo.mixPosition + mixInfo.mixDuration);
m_track->plant_transition(*t.get(), 0, 1);
m_sameCompositions[clipId] = t;
m_sameCompositions[clipIds.second] = t;
}
}
return true;
};
Fun destroy_mix = [clipId, mixInfo, this]() {
Fun destroy_mix = [clipIds, mixInfo, this]() {
if (auto ptr = m_parent.lock()) {
Mlt::Transition &transition = *m_sameCompositions[clipId].get();
std::shared_ptr<ClipModel> movedClip(ptr->getClipPtr(clipId));
Mlt::Transition &transition = *m_sameCompositions[clipIds.second].get();
std::shared_ptr<ClipModel> movedClip(ptr->getClipPtr(clipIds.second));
movedClip->setMixDuration(0);
QModelIndex ix = ptr->makeClipIndexFromID(clipId);
QModelIndex ix = ptr->makeClipIndexFromID(clipIds.second);
emit ptr->dataChanged(ix, ix, {TimelineModel::StartRole,TimelineModel::MixRole});
QScopedPointer<Mlt::Field> field(m_track->field());
field->lock();
field->disconnect_service(transition);
field->unlock();
m_sameCompositions.erase(clipId);
m_sameCompositions.erase(clipIds.second);
}
return true;
};
// lock MLT playlist so that we don't end up with invalid frames in monitor
auto operation = requestClipDeletion_lambda(clipId, updateView, finalMove, groupMove, finalMove);
auto operation = requestClipDeletion_lambda(clipIds.second, updateView, finalMove, groupMove, finalMove);
bool res = operation();
if (res) {
qDebug()<<"=== CLIP DELETED; OK";
auto reverse = requestClipInsertion_lambda(clipId, clipInitialPos, updateView, finalMove, groupMove);
auto reverse = requestClipInsertion_lambda(clipIds.second, secondClipPos, updateView, finalMove, groupMove);
if (auto ptr = m_parent.lock()) {
ptr->getClipPtr(clipId)->setSubPlaylistIndex(dest_track);
ptr->getClipPtr(clipIds.second)->setSubPlaylistIndex(dest_track);
}
auto operation2 = requestClipInsertion_lambda(clipId, position, updateView, finalMove, groupMove);
auto operation2 = requestClipInsertion_lambda(clipIds.second, secondClipPos, updateView, finalMove, groupMove);
res = res && operation2();
if (res) {
auto reverse2 = requestClipDeletion_lambda(clipId, updateView, finalMove, groupMove, finalMove);
auto operation3 = requestClipResize_lambda(clipIds.second, secondClipPos - mixDuration, secondClipPos + secondClipDuration, false, true);
qDebug()<<"==== PROCESSING 2nd CLIP RESIZE: "<<clipIds.second<<", FROM: "<<(secondClipPos - mixDuration)<<"-"<<(secondClipPos + secondClipDuration);
res = operation3();
auto operation4 = requestClipResize_lambda(clipIds.first, firstClipPos, firstClipPos + firstClipDuration + mixDuration, true, true);
res = res && operation4();
auto reverse2 = requestClipDeletion_lambda(clipIds.second, updateView, finalMove, groupMove, finalMove);
// Create mix composition
build_mix();
qDebug()<<"=============\nSECOND INSERT SUCCESS\n\n=================";
......
......@@ -123,7 +123,7 @@ public:
QVariant getProperty(const QString &name) const;
void setProperty(const QString &name, const QString &value);
/** @brief Create a composition between 2 same track clips */
bool requestClipMix(int clipId, int position, bool updateView, bool finalMove, Fun &undo, Fun &redo, bool groupMove);
bool requestClipMix(std::pair<int, int> clipIds, int mixDuration, bool updateView, bool finalMove, Fun &undo, Fun &redo, bool groupMove);
/** @brief Get in/out position for mix composition */
std::pair<int, int> getMixInfo(int position) const;
/** @brief Delete a mix composition */
......@@ -149,7 +149,7 @@ protected:
@param out is the new ending on the clip
@param right is true if we change the right side of the clip, false otherwise
*/
Fun requestClipResize_lambda(int clipId, int in, int out, bool right);
Fun requestClipResize_lambda(int clipId, int in, int out, bool right, bool allowMix = false);
/* @brief Performs an insertion of the given clip.
Returns true if the operation succeeded, and otherwise, the track is not modified.
......
......@@ -3672,13 +3672,43 @@ void TimelineController::mixClip()
if (sel.empty()) {
return;
}
int idToMove = *sel.begin();
// Mix duration currently hardcoded to 75 frames
int mixPosition = m_model->getItemPosition(idToMove) - 75;
int selectedTrack = -1;
int idToMove = -1;
for (int s : sel) {
if (!m_model->isClip(s)) {
continue;
}
int tid = m_model->getItemTrackId(s);
if (selectedTrack == -1) {
selectedTrack = tid;
idToMove = s;
break;
}
}
if (idToMove == -1) {
pCore->displayMessage(i18n("Select a clip to apply the mix"), InformationMessage, 500);
return;
}
int cursor = pCore->getTimelinePosition();
int cid2 = -1;
int mixPosition = m_model->getItemPosition(idToMove);
int clipDuration = m_model->getItemPlaytime(idToMove);
std::pair<int, int> clipsToMix;
if (cursor < mixPosition + clipDuration) {
// Mix at start of selected clip
cid2 = m_model->getTrackById_const(selectedTrack)->getClipByPosition(mixPosition - 1);
clipsToMix.first = cid2;
clipsToMix.second = idToMove;
} else {
// Mix at end of selected clip
mixPosition += clipDuration + 1;
cid2 = m_model->getTrackById_const(selectedTrack)->getClipByPosition(mixPosition);
clipsToMix.first = idToMove;
clipsToMix.second = cid2;
}
std::function<bool(void)> undo = []() { return true; };
std::function<bool(void)> redo = []() { return true; };
int selectedTrack = m_model->getItemTrackId(idToMove);
bool result = m_model->requestClipMixMove(idToMove, selectedTrack, mixPosition, true, true, true, undo, redo, false);
bool result = m_model->requestClipMix(clipsToMix, selectedTrack, mixPosition, true, true, true, undo, redo, false);
pCore->pushUndo(undo, redo, i18n("Create mix"));
}
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