Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Kdenlive
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
259
Issues
259
List
Boards
Labels
Service Desk
Milestones
Merge Requests
14
Merge Requests
14
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Incidents
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Multimedia
Kdenlive
Commits
03cf9745
Commit
03cf9745
authored
Aug 28, 2020
by
Jean-Baptiste Mardelle
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Same track mix update: resize both clips and create a mix in between
parent
761393e4
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
88 additions
and
42 deletions
+88
-42
src/timeline2/model/timelinemodel.cpp
src/timeline2/model/timelinemodel.cpp
+10
-8
src/timeline2/model/timelinemodel.hpp
src/timeline2/model/timelinemodel.hpp
+1
-1
src/timeline2/model/trackmodel.cpp
src/timeline2/model/trackmodel.cpp
+40
-26
src/timeline2/model/trackmodel.hpp
src/timeline2/model/trackmodel.hpp
+2
-2
src/timeline2/view/timelinecontroller.cpp
src/timeline2/view/timelinecontroller.cpp
+35
-5
No files found.
src/timeline2/model/timelinemodel.cpp
View file @
03cf9745
...
...
@@ -668,13 +668,13 @@ bool TimelineModel::requestClipMove(int clipId, int trackId, int position, bool
}
bool
TimelineModel
::
requestClipMix
Move
(
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
(
clipId
s
.
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
(
clipId
s
.
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
,
posi
tion
,
updateView
,
finalMove
,
local_undo
,
local_redo
,
groupMove
);
ok
=
getTrackById
(
trackId
)
->
requestClipMix
(
clipId
s
,
mixDura
tion
,
updateView
,
finalMove
,
local_undo
,
local_redo
,
groupMove
);
if
(
!
ok
)
{
qDebug
()
<<
"-------------
\n
MIX 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: "
<<
clipId
s
;
UPDATE_UNDO_REDO
(
local_redo
,
local_undo
,
undo
,
redo
);
return
ok
;
...
...
src/timeline2/model/timelinemodel.hpp
View file @
03cf9745
...
...
@@ -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
requestClipMix
Move
(
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
...
...
src/timeline2/model/trackmodel.cpp
View file @
03cf9745
...
...
@@ -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
posi
tion
,
bool
updateView
,
bool
finalMove
,
Fun
&
undo
,
Fun
&
redo
,
bool
groupMove
)
bool
TrackModel
::
requestClipMix
(
std
::
pair
<
int
,
int
>
clipIds
,
int
mixDura
tion
,
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
=
[
clipId
s
,
mixInfo
,
this
]()
{
if
(
auto
ptr
=
m_parent
.
lock
())
{
std
::
shared_ptr
<
ClipModel
>
movedClip
(
ptr
->
getClipPtr
(
clipId
));
std
::
shared_ptr
<
ClipModel
>
movedClip
(
ptr
->
getClipPtr
(
clipId
s
.
second
));
movedClip
->
setMixDuration
(
mixInfo
.
mixDuration
);
QModelIndex
ix
=
ptr
->
makeClipIndexFromID
(
clipId
);
QModelIndex
ix
=
ptr
->
makeClipIndexFromID
(
clipId
s
.
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
[
clipId
s
.
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
[
clipId
s
.
second
]
=
t
;
}
}
return
true
;
};
Fun
destroy_mix
=
[
clipId
,
mixInfo
,
this
]()
{
Fun
destroy_mix
=
[
clipId
s
,
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
[
clipId
s
.
second
].
get
();
std
::
shared_ptr
<
ClipModel
>
movedClip
(
ptr
->
getClipPtr
(
clipId
s
.
second
));
movedClip
->
setMixDuration
(
0
);
QModelIndex
ix
=
ptr
->
makeClipIndexFromID
(
clipId
);
QModelIndex
ix
=
ptr
->
makeClipIndexFromID
(
clipId
s
.
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
(
clipId
s
.
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
(
clipId
s
.
second
,
updateView
,
finalMove
,
groupMove
,
finalMove
);
bool
res
=
operation
();
if
(
res
)
{
qDebug
()
<<
"=== CLIP DELETED; OK"
;
auto
reverse
=
requestClipInsertion_lambda
(
clipId
,
clipInitial
Pos
,
updateView
,
finalMove
,
groupMove
);
auto
reverse
=
requestClipInsertion_lambda
(
clipId
s
.
second
,
secondClip
Pos
,
updateView
,
finalMove
,
groupMove
);
if
(
auto
ptr
=
m_parent
.
lock
())
{
ptr
->
getClipPtr
(
clipId
)
->
setSubPlaylistIndex
(
dest_track
);
ptr
->
getClipPtr
(
clipId
s
.
second
)
->
setSubPlaylistIndex
(
dest_track
);
}
auto
operation2
=
requestClipInsertion_lambda
(
clipId
,
position
,
updateView
,
finalMove
,
groupMove
);
auto
operation2
=
requestClipInsertion_lambda
(
clipId
s
.
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
()
<<
"=============
\n
SECOND INSERT SUCCESS
\n\n
================="
;
...
...
src/timeline2/model/trackmodel.hpp
View file @
03cf9745
...
...
@@ -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
posi
tion
,
bool
updateView
,
bool
finalMove
,
Fun
&
undo
,
Fun
&
redo
,
bool
groupMove
);
bool
requestClipMix
(
std
::
pair
<
int
,
int
>
clipIds
,
int
mixDura
tion
,
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.
...
...
src/timeline2/view/timelinecontroller.cpp
View file @
03cf9745
...
...
@@ -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"
));
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment