Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Multimedia
Kdenlive
Commits
402c251e
Commit
402c251e
authored
Feb 12, 2020
by
Jean-Baptiste Mardelle
Browse files
Add pitch compensation to speed effect (requires MLT git)
parent
55d0afae
Pipeline
#14903
passed with stage
in 17 minutes and 20 seconds
Changes
14
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
src/jobs/meltjob.cpp
View file @
402c251e
...
...
@@ -160,12 +160,12 @@ bool MeltJob::startJob()
return
false
;
}
if
(
m_out
==
-
1
)
{
m_out
=
m_producer
->
get_
playtime
()
-
1
;
m_out
=
m_producer
->
get_
length
()
-
1
;
}
if
(
m_in
==
-
1
)
{
m_in
=
0
;
}
if
(
m_out
!=
m_producer
->
get_
playtime
()
-
1
||
m_in
!=
0
)
{
if
(
m_out
!=
m_producer
->
get_
length
()
-
1
||
m_in
!=
0
)
{
std
::
swap
(
m_wholeProducer
,
m_producer
);
m_producer
.
reset
(
m_wholeProducer
->
cut
(
m_in
,
m_out
));
}
...
...
@@ -241,9 +241,9 @@ bool MeltJob::startJob()
}
Mlt
::
Tractor
tractor
(
*
m_profile
.
get
());
Mlt
::
Playlist
playlist
;
playlist
.
append
(
*
m_producer
.
get
());
Mlt
::
Playlist
playlist
(
*
m_profile
.
get
());
tractor
.
set_track
(
playlist
,
0
);
playlist
.
append
(
*
m_producer
.
get
());
m_consumer
->
connect
(
tractor
);
m_producer
->
set_speed
(
0
);
m_producer
->
seek
(
0
);
...
...
src/jobs/speedjob.cpp
View file @
402c251e
...
...
@@ -43,9 +43,10 @@
#include
<KLineEdit>
#include
<mlt++/Mlt.h>
SpeedJob
::
SpeedJob
(
const
QString
&
binId
,
double
speed
,
QString
destUrl
)
SpeedJob
::
SpeedJob
(
const
QString
&
binId
,
double
speed
,
bool
warp_pitch
,
QString
destUrl
)
:
MeltJob
(
binId
,
SPEEDJOB
,
false
,
-
1
,
-
1
)
,
m_speed
(
speed
)
,
m_warp_pitch
(
warp_pitch
)
,
m_destUrl
(
std
::
move
(
destUrl
))
{
m_requiresFilter
=
false
;
...
...
@@ -69,8 +70,15 @@ void SpeedJob::configureProducer()
if
(
!
qFuzzyCompare
(
m_speed
,
1.0
))
{
QString
resource
=
m_producer
->
get
(
"resource"
);
m_producer
=
std
::
make_unique
<
Mlt
::
Producer
>
(
*
m_profile
.
get
(),
"timewarp"
,
QStringLiteral
(
"%1:%2"
).
arg
(
QString
::
fromStdString
(
std
::
to_string
(
m_speed
))).
arg
(
resource
).
toUtf8
().
constData
());
m_in
/=
m_speed
;
m_out
/=
m_speed
;
if
(
m_in
>
0
)
{
m_in
/=
m_speed
;
}
if
(
m_out
>
0
)
{
m_out
/=
m_speed
;
}
if
(
m_warp_pitch
)
{
m_producer
->
set
(
"warp_pitch"
,
1
);
}
}
}
...
...
@@ -112,10 +120,13 @@ int SpeedJob::prepareJob(const std::shared_ptr<JobManager> &ptr, const std::vect
speedInput
.
setSuffix
(
QLatin1String
(
"%"
));
speedInput
.
setFocus
();
speedInput
.
selectAll
();
QCheckBox
cb
(
i18n
(
"Pitch compensation"
),
&
d
);
cb
.
setChecked
(
true
);
l
->
addWidget
(
&
labUrl
);
l
->
addWidget
(
&
fileUrl
);
l
->
addWidget
(
&
lab
);
l
->
addWidget
(
&
speedInput
);
l
->
addWidget
(
&
cb
);
l
->
addWidget
(
&
buttonBox
);
d
.
connect
(
&
buttonBox
,
&
QDialogButtonBox
::
rejected
,
&
d
,
&
QDialog
::
reject
);
d
.
connect
(
&
buttonBox
,
&
QDialogButtonBox
::
accepted
,
&
d
,
&
QDialog
::
accept
);
...
...
@@ -123,6 +134,7 @@ int SpeedJob::prepareJob(const std::shared_ptr<JobManager> &ptr, const std::vect
return
-
1
;
}
double
speed
=
speedInput
.
value
();
bool
warp_pitch
=
cb
.
isChecked
();
std
::
unordered_map
<
QString
,
QString
>
destinations
;
// keys are binIds, values are path to target files
for
(
const
auto
&
binId
:
binIds
)
{
QString
mltfile
;
...
...
@@ -153,7 +165,7 @@ int SpeedJob::prepareJob(const std::shared_ptr<JobManager> &ptr, const std::vect
// Now we have to create the jobs objects. This is trickier than usual, since the parameters are different for each job (each clip has its own
// destination). We have to construct a lambda that does that.
auto
createFn
=
[
dest
=
std
::
move
(
destinations
),
fSpeed
=
speed
/
100.0
](
const
QString
&
id
)
{
return
std
::
make_shared
<
SpeedJob
>
(
id
,
fSpeed
,
dest
.
at
(
id
));
};
auto
createFn
=
[
dest
=
std
::
move
(
destinations
),
fSpeed
=
speed
/
100.0
,
warp_pitch
](
const
QString
&
id
)
{
return
std
::
make_shared
<
SpeedJob
>
(
id
,
fSpeed
,
warp_pitch
,
dest
.
at
(
id
));
};
// We are now all set to create the job. Note that we pass all the parameters directly through the lambda, hence there are no extra parameters to the
// function
...
...
src/jobs/speedjob.hpp
View file @
402c251e
...
...
@@ -42,7 +42,7 @@ public:
/** @brief Creates a timewarp producer
@param speed The speed value
*/
SpeedJob
(
const
QString
&
binId
,
double
speed
,
QString
destUrl
);
SpeedJob
(
const
QString
&
binId
,
double
speed
,
bool
warp_pitch
,
QString
destUrl
);
// This is a special function that prepares the stabilize job for a given list of clips.
// Namely, it displays the required UI to configure the job and call startJob with the right set of parameters
...
...
@@ -63,5 +63,6 @@ protected:
void
configureFilter
()
override
;
double
m_speed
;
bool
m_warp_pitch
;
QString
m_destUrl
;
};
src/timeline2/model/clipmodel.cpp
View file @
402c251e
...
...
@@ -69,7 +69,7 @@ ClipModel::ClipModel(const std::shared_ptr<TimelineModel> &parent, std::shared_p
});
}
int
ClipModel
::
construct
(
const
std
::
shared_ptr
<
TimelineModel
>
&
parent
,
const
QString
&
binClipId
,
int
id
,
PlaylistState
::
ClipState
state
,
double
speed
)
int
ClipModel
::
construct
(
const
std
::
shared_ptr
<
TimelineModel
>
&
parent
,
const
QString
&
binClipId
,
int
id
,
PlaylistState
::
ClipState
state
,
double
speed
,
bool
warp_pitch
)
{
id
=
(
id
==
-
1
?
TimelineModel
::
getNextId
()
:
id
);
std
::
shared_ptr
<
ProjectClip
>
binClip
=
pCore
->
projectItemModel
()
->
getClipByBinID
(
binClipId
);
...
...
@@ -81,6 +81,9 @@ int ClipModel::construct(const std::shared_ptr<TimelineModel> &parent, const QSt
state
=
stateFromBool
(
videoAudio
);
std
::
shared_ptr
<
Mlt
::
Producer
>
cutProducer
=
binClip
->
getTimelineProducer
(
-
1
,
id
,
state
,
speed
);
std
::
shared_ptr
<
ClipModel
>
clip
(
new
ClipModel
(
parent
,
cutProducer
,
binClipId
,
id
,
state
,
speed
));
if
(
!
qFuzzyCompare
(
speed
,
1.
))
{
cutProducer
->
parent
().
set
(
"warp_pitch"
,
warp_pitch
?
1
:
0
);
}
TRACE_CONSTR
(
clip
.
get
(),
parent
,
binClipId
,
id
,
state
,
speed
);
clip
->
setClipState_lambda
(
state
)();
parent
->
registerClip
(
clip
);
...
...
@@ -106,11 +109,16 @@ int ClipModel::construct(const std::shared_ptr<TimelineModel> &parent, const QSt
state
=
stateFromBool
(
videoAudio
);
double
speed
=
1.0
;
bool
warp_pitch
=
false
;
if
(
QString
::
fromUtf8
(
producer
->
parent
().
get
(
"mlt_service"
))
==
QLatin1String
(
"timewarp"
))
{
speed
=
producer
->
parent
().
get_double
(
"warp_speed"
);
warp_pitch
=
producer
->
parent
().
get_int
(
"warp_pitch"
);
}
auto
result
=
binClip
->
giveMasterAndGetTimelineProducer
(
id
,
producer
,
state
);
std
::
shared_ptr
<
ClipModel
>
clip
(
new
ClipModel
(
parent
,
result
.
first
,
binClipId
,
id
,
state
,
speed
));
if
(
warp_pitch
)
{
result
.
first
->
parent
().
set
(
"warp_pitch"
,
1
);
}
clip
->
setClipState_lambda
(
state
)();
parent
->
registerClip
(
clip
);
clip
->
m_effectStack
->
importEffects
(
producer
,
state
,
result
.
second
);
...
...
@@ -432,16 +440,12 @@ void ClipModel::refreshProducerFromBin(int trackId)
refreshProducerFromBin
(
trackId
,
m_currentState
);
}
bool
ClipModel
::
useTimewarpProducer
(
double
speed
,
bool
changeDuration
,
Fun
&
undo
,
Fun
&
redo
)
bool
ClipModel
::
useTimewarpProducer
(
double
speed
,
bool
pitchCompensate
,
bool
changeDuration
,
Fun
&
undo
,
Fun
&
redo
)
{
if
(
m_endlessResize
)
{
// no timewarp for endless producers
return
false
;
}
if
(
qFuzzyCompare
(
speed
,
m_speed
))
{
// nothing to do
return
true
;
}
std
::
function
<
bool
(
void
)
>
local_undo
=
[]()
{
return
true
;
};
std
::
function
<
bool
(
void
)
>
local_redo
=
[]()
{
return
true
;
};
double
previousSpeed
=
getSpeed
();
...
...
@@ -457,8 +461,9 @@ bool ClipModel::useTimewarpProducer(double speed, bool changeDuration, Fun &undo
}
else
if
(
previousSpeed
<
0
)
{
revertSpeed
=
true
;
}
auto
operation
=
useTimewarpProducer_lambda
(
speed
);
auto
reverse
=
useTimewarpProducer_lambda
(
previousSpeed
);
bool
hasPitch
=
getIntProperty
(
QStringLiteral
(
"warp_pitch"
));
auto
operation
=
useTimewarpProducer_lambda
(
speed
,
pitchCompensate
);
auto
reverse
=
useTimewarpProducer_lambda
(
previousSpeed
,
hasPitch
);
if
(
revertSpeed
||
(
changeDuration
&&
oldOut
>=
newDuration
))
{
// in that case, we are going to shrink the clip when changing the producer. We must undo that when reloading the old producer
reverse
=
[
reverse
,
oldIn
,
oldOut
,
this
]()
{
...
...
@@ -506,12 +511,13 @@ bool ClipModel::useTimewarpProducer(double speed, bool changeDuration, Fun &undo
return
false
;
}
Fun
ClipModel
::
useTimewarpProducer_lambda
(
double
speed
)
Fun
ClipModel
::
useTimewarpProducer_lambda
(
double
speed
,
bool
pitchCompensate
)
{
QWriteLocker
locker
(
&
m_lock
);
return
[
speed
,
this
]()
{
return
[
speed
,
pitchCompensate
,
this
]()
{
qDebug
()
<<
"timeWarp producer"
<<
speed
;
refreshProducerFromBin
(
m_currentTrackId
,
m_currentState
,
speed
);
m_producer
->
parent
().
set
(
"warp_pitch"
,
pitchCompensate
?
1
:
0
);
if
(
m_currentTrackId
>
-
1
)
{
if
(
auto
ptr
=
m_parent
.
lock
())
{
QModelIndex
ix
=
ptr
->
makeClipIndexFromID
(
m_id
);
...
...
@@ -730,6 +736,9 @@ QDomElement ClipModel::toXml(QDomDocument &document)
}
}
container
.
setAttribute
(
QStringLiteral
(
"speed"
),
m_speed
);
if
(
!
qFuzzyCompare
(
m_speed
,
1.
))
{
container
.
setAttribute
(
QStringLiteral
(
"warp_pitch"
),
getIntProperty
(
QStringLiteral
(
"warp_pitch"
)));
}
container
.
appendChild
(
m_effectStack
->
toXml
(
document
));
return
container
;
}
...
...
src/timeline2/model/clipmodel.hpp
View file @
402c251e
...
...
@@ -57,7 +57,7 @@ public:
@param binClip is the id of the bin clip associated
@param id Requested id of the clip. Automatic if -1
*/
static
int
construct
(
const
std
::
shared_ptr
<
TimelineModel
>
&
parent
,
const
QString
&
binClipId
,
int
id
,
PlaylistState
::
ClipState
state
,
double
speed
=
1.
);
static
int
construct
(
const
std
::
shared_ptr
<
TimelineModel
>
&
parent
,
const
QString
&
binClipId
,
int
id
,
PlaylistState
::
ClipState
state
,
double
speed
=
1.
,
bool
warp_pitch
=
false
);
/* @brief Creates a clip, which references itself to the parent timeline
Returns the (unique) id of the created clip
...
...
@@ -182,9 +182,9 @@ protected:
/* @brief This functions replaces the current producer with a slowmotion one
It also resizes the producer so that set of frames contained in the clip is the same
*/
bool
useTimewarpProducer
(
double
speed
,
bool
changeDuration
,
Fun
&
undo
,
Fun
&
redo
);
bool
useTimewarpProducer
(
double
speed
,
bool
pitchCompensate
,
bool
changeDuration
,
Fun
&
undo
,
Fun
&
redo
);
// @brief Lambda that merely changes the speed (in and out are untouched)
Fun
useTimewarpProducer_lambda
(
double
speed
);
Fun
useTimewarpProducer_lambda
(
double
speed
,
bool
pitchCompensate
);
/** @brief Returns the marker model associated with this clip */
std
::
shared_ptr
<
MarkerListModel
>
getMarkerModel
()
const
;
...
...
src/timeline2/model/timelinefunctions.cpp
View file @
402c251e
...
...
@@ -64,7 +64,8 @@ bool TimelineFunctions::cloneClip(const std::shared_ptr<TimelineItemModel> &time
{
// Special case: slowmotion clips
double
clipSpeed
=
timeline
->
m_allClips
[
clipId
]
->
getSpeed
();
bool
res
=
timeline
->
requestClipCreation
(
timeline
->
getClipBinId
(
clipId
),
newId
,
state
,
clipSpeed
,
undo
,
redo
);
bool
warp_pitch
=
timeline
->
m_allClips
[
clipId
]
->
getIntProperty
(
QStringLiteral
(
"warp_pitch"
));
bool
res
=
timeline
->
requestClipCreation
(
timeline
->
getClipBinId
(
clipId
),
newId
,
state
,
clipSpeed
,
warp_pitch
,
undo
,
redo
);
timeline
->
m_allClips
[
newId
]
->
m_endlessResize
=
timeline
->
m_allClips
[
clipId
]
->
m_endlessResize
;
// copy useful timeline properties
...
...
@@ -1430,8 +1431,12 @@ bool TimelineFunctions::pasteClips(const std::shared_ptr<TimelineItemModel> &tim
int
curTrackId
=
tracksMap
.
value
(
prod
.
attribute
(
QStringLiteral
(
"track"
)).
toInt
());
int
pos
=
prod
.
attribute
(
QStringLiteral
(
"position"
)).
toInt
()
-
offset
;
double
speed
=
locale
.
toDouble
(
prod
.
attribute
(
QStringLiteral
(
"speed"
)));
bool
warp_pitch
=
false
;
if
(
!
qFuzzyCompare
(
speed
,
1.
))
{
warp_pitch
=
prod
.
attribute
(
QStringLiteral
(
"warp_pitch"
)).
toInt
();
}
int
newId
;
bool
created
=
timeline
->
requestClipCreation
(
originalId
,
newId
,
timeline
->
getTrackById_const
(
curTrackId
)
->
trackType
(),
speed
,
undo
,
redo
);
bool
created
=
timeline
->
requestClipCreation
(
originalId
,
newId
,
timeline
->
getTrackById_const
(
curTrackId
)
->
trackType
(),
speed
,
warp_pitch
,
undo
,
redo
);
if
(
created
)
{
// Master producer is ready
// ids.removeAll(originalId);
...
...
src/timeline2/model/timelinemodel.cpp
View file @
402c251e
...
...
@@ -97,7 +97,7 @@ RTTR_REGISTRATION
// .method("requestCompositionInsertion", select_overload<bool(const QString &, int, int, int, std::unique_ptr<Mlt::Properties>, int &, bool)>(
// &TimelineModel::requestCompositionInsertion))(
// parameter_names("transitionId", "trackId", "position", "length", "transProps", "id", "logUndo"))
.
method
(
"requestClipTimeWarp"
,
select_overload
<
bool
(
int
,
double
,
bool
)
>
(
&
TimelineModel
::
requestClipTimeWarp
))(
parameter_names
(
"clipId"
,
"speed"
,
"changeDuration"
));
.
method
(
"requestClipTimeWarp"
,
select_overload
<
bool
(
int
,
double
,
bool
,
bool
)
>
(
&
TimelineModel
::
requestClipTimeWarp
))(
parameter_names
(
"clipId"
,
"speed"
,
"
pitchCompensate"
,
"
changeDuration"
));
}
int
TimelineModel
::
next_id
=
0
;
...
...
@@ -928,7 +928,7 @@ int TimelineModel::suggestCompositionMove(int compoId, int trackId, int position
return
currentPos
;
}
bool
TimelineModel
::
requestClipCreation
(
const
QString
&
binClipId
,
int
&
id
,
PlaylistState
::
ClipState
state
,
double
speed
,
Fun
&
undo
,
Fun
&
redo
)
bool
TimelineModel
::
requestClipCreation
(
const
QString
&
binClipId
,
int
&
id
,
PlaylistState
::
ClipState
state
,
double
speed
,
bool
warp_pitch
,
Fun
&
undo
,
Fun
&
redo
)
{
qDebug
()
<<
"requestClipCreation "
<<
binClipId
;
QString
bid
=
binClipId
;
...
...
@@ -947,7 +947,7 @@ bool TimelineModel::requestClipCreation(const QString &binClipId, int &id, Playl
int
clipId
=
TimelineModel
::
getNextId
();
id
=
clipId
;
Fun
local_undo
=
deregisterClip_lambda
(
clipId
);
ClipModel
::
construct
(
shared_from_this
(),
bid
,
clipId
,
state
,
speed
);
ClipModel
::
construct
(
shared_from_this
(),
bid
,
clipId
,
state
,
speed
,
warp_pitch
);
auto
clip
=
m_allClips
[
clipId
];
Fun
local_redo
=
[
clip
,
this
,
state
]()
{
// We capture a shared_ptr to the clip, which means that as long as this undo object lives, the clip object is not deleted. To insert it back it is
...
...
@@ -1040,7 +1040,7 @@ bool TimelineModel::requestClipInsertion(const QString &binClipId, int trackId,
return
false
;
}
bool
audioDrop
=
getTrackById_const
(
trackId
)
->
isAudioTrack
();
res
=
requestClipCreation
(
binClipId
,
id
,
getTrackById_const
(
trackId
)
->
trackType
(),
1.0
,
local_undo
,
local_redo
);
res
=
requestClipCreation
(
binClipId
,
id
,
getTrackById_const
(
trackId
)
->
trackType
(),
1.0
,
false
,
local_undo
,
local_redo
);
res
=
res
&&
requestClipMove
(
id
,
trackId
,
position
,
true
,
refreshView
,
logUndo
,
logUndo
,
local_undo
,
local_redo
);
int
target_track
;
if
(
audioDrop
)
{
...
...
@@ -1075,7 +1075,7 @@ bool TimelineModel::requestClipInsertion(const QString &binClipId, int trackId,
std
::
function
<
bool
(
void
)
>
audio_undo
=
[]()
{
return
true
;
};
std
::
function
<
bool
(
void
)
>
audio_redo
=
[]()
{
return
true
;
};
int
newId
;
res
=
requestClipCreation
(
binClipId
,
newId
,
audioDrop
?
PlaylistState
::
VideoOnly
:
PlaylistState
::
AudioOnly
,
1.0
,
audio_undo
,
audio_redo
);
res
=
requestClipCreation
(
binClipId
,
newId
,
audioDrop
?
PlaylistState
::
VideoOnly
:
PlaylistState
::
AudioOnly
,
1.0
,
false
,
audio_undo
,
audio_redo
);
if
(
res
)
{
bool
move
=
false
;
while
(
!
move
&&
!
possibleTracks
.
isEmpty
())
{
...
...
@@ -1110,7 +1110,7 @@ bool TimelineModel::requestClipInsertion(const QString &binClipId, int trackId,
if
(
normalisedBinId
.
startsWith
(
QLatin1Char
(
'A'
))
||
normalisedBinId
.
startsWith
(
QLatin1Char
(
'V'
)))
{
normalisedBinId
.
remove
(
0
,
1
);
}
res
=
requestClipCreation
(
normalisedBinId
,
id
,
dropType
,
1.0
,
local_undo
,
local_redo
);
res
=
requestClipCreation
(
normalisedBinId
,
id
,
dropType
,
1.0
,
false
,
local_undo
,
local_redo
);
res
=
res
&&
requestClipMove
(
id
,
trackId
,
position
,
true
,
refreshView
,
logUndo
,
logUndo
,
local_undo
,
local_redo
);
}
if
(
!
res
)
{
...
...
@@ -1904,7 +1904,8 @@ int TimelineModel::requestClipResizeAndTimeWarp(int itemId, int size, bool right
pos
+=
getItemPlaytime
(
id
)
-
size
;
}
result
=
getTrackById
(
tid
)
->
requestClipDeletion
(
id
,
true
,
true
,
undo
,
redo
,
false
,
true
);
result
=
result
&&
requestClipTimeWarp
(
id
,
speed
,
true
,
undo
,
redo
);
bool
pitchCompensate
=
m_allClips
[
id
]
->
getIntProperty
(
QStringLiteral
(
"warp_pitch"
));
result
=
result
&&
requestClipTimeWarp
(
id
,
speed
,
pitchCompensate
,
true
,
undo
,
redo
);
result
=
result
&&
requestItemResize
(
id
,
size
,
true
,
true
,
undo
,
redo
);
result
=
result
&&
getTrackById
(
tid
)
->
requestClipInsertion
(
id
,
pos
,
true
,
true
,
undo
,
redo
);
if
(
!
result
)
{
...
...
@@ -3381,12 +3382,9 @@ void TimelineModel::requestClipUpdate(int clipId, const QVector<int> &roles)
notifyChange
(
modelIndex
,
modelIndex
,
roles
);
}
bool
TimelineModel
::
requestClipTimeWarp
(
int
clipId
,
double
speed
,
bool
changeDuration
,
Fun
&
undo
,
Fun
&
redo
)
bool
TimelineModel
::
requestClipTimeWarp
(
int
clipId
,
double
speed
,
bool
pitchCompensate
,
bool
changeDuration
,
Fun
&
undo
,
Fun
&
redo
)
{
QWriteLocker
locker
(
&
m_lock
);
if
(
qFuzzyCompare
(
speed
,
m_allClips
[
clipId
]
->
getSpeed
()))
{
return
true
;
}
std
::
function
<
bool
(
void
)
>
local_undo
=
[]()
{
return
true
;
};
std
::
function
<
bool
(
void
)
>
local_redo
=
[]()
{
return
true
;
};
int
oldPos
=
getClipPosition
(
clipId
);
...
...
@@ -3397,7 +3395,8 @@ bool TimelineModel::requestClipTimeWarp(int clipId, double speed, bool changeDur
success
=
success
&&
getTrackById
(
trackId
)
->
requestClipDeletion
(
clipId
,
true
,
true
,
local_undo
,
local_redo
,
false
,
false
);
}
if
(
success
)
{
success
=
m_allClips
[
clipId
]
->
useTimewarpProducer
(
speed
,
changeDuration
,
local_undo
,
local_redo
);
qDebug
()
<<
"Warp pass 3"
;
success
=
m_allClips
[
clipId
]
->
useTimewarpProducer
(
speed
,
pitchCompensate
,
changeDuration
,
local_undo
,
local_redo
);
}
if
(
trackId
!=
-
1
)
{
success
=
success
&&
getTrackById
(
trackId
)
->
requestClipInsertion
(
clipId
,
oldPos
,
true
,
true
,
local_undo
,
local_redo
);
...
...
@@ -3410,9 +3409,12 @@ bool TimelineModel::requestClipTimeWarp(int clipId, double speed, bool changeDur
return
success
;
}
bool
TimelineModel
::
requestClipTimeWarp
(
int
clipId
,
double
speed
,
bool
changeDuration
)
bool
TimelineModel
::
requestClipTimeWarp
(
int
clipId
,
double
speed
,
bool
pitchCompensate
,
bool
changeDuration
)
{
QWriteLocker
locker
(
&
m_lock
);
if
(
qFuzzyCompare
(
speed
,
m_allClips
[
clipId
]
->
getSpeed
())
&&
pitchCompensate
==
m_allClips
[
clipId
]
->
getIntProperty
(
"warp_pitch"
))
{
return
true
;
}
TRACE
(
clipId
,
speed
);
Fun
undo
=
[]()
{
return
true
;
};
Fun
redo
=
[]()
{
return
true
;
};
...
...
@@ -3423,10 +3425,10 @@ bool TimelineModel::requestClipTimeWarp(int clipId, double speed, bool changeDur
// Check if clip has a split partner
int
splitId
=
m_groups
->
getSplitPartner
(
clipId
);
if
(
splitId
>
-
1
)
{
result
=
requestClipTimeWarp
(
splitId
,
speed
/
100.0
,
changeDuration
,
undo
,
redo
);
result
=
requestClipTimeWarp
(
splitId
,
speed
/
100.0
,
pitchCompensate
,
changeDuration
,
undo
,
redo
);
}
if
(
result
)
{
result
=
requestClipTimeWarp
(
clipId
,
speed
/
100.0
,
changeDuration
,
undo
,
redo
);
result
=
requestClipTimeWarp
(
clipId
,
speed
/
100.0
,
pitchCompensate
,
changeDuration
,
undo
,
redo
);
}
if
(
!
result
)
{
pCore
->
displayMessage
(
i18n
(
"Change speed failed"
),
ErrorMessage
);
...
...
@@ -3436,7 +3438,7 @@ bool TimelineModel::requestClipTimeWarp(int clipId, double speed, bool changeDur
}
}
else
{
// If clip is not inserted on a track, we just change the producer
result
=
m_allClips
[
clipId
]
->
useTimewarpProducer
(
speed
,
changeDuration
,
undo
,
redo
);
result
=
m_allClips
[
clipId
]
->
useTimewarpProducer
(
speed
,
pitchCompensate
,
changeDuration
,
undo
,
redo
);
}
if
(
result
)
{
PUSH_UNDO
(
undo
,
redo
,
i18n
(
"Change clip speed"
));
...
...
src/timeline2/model/timelinemodel.hpp
View file @
402c251e
...
...
@@ -410,7 +410,7 @@ protected:
@param id: return parameter for the id of the newly created clip.
@param state: The desired clip state (original, audio/video only).
*/
bool
requestClipCreation
(
const
QString
&
binClipId
,
int
&
id
,
PlaylistState
::
ClipState
state
,
double
speed
,
Fun
&
undo
,
Fun
&
redo
);
bool
requestClipCreation
(
const
QString
&
binClipId
,
int
&
id
,
PlaylistState
::
ClipState
state
,
double
speed
,
bool
warp_pitch
,
Fun
&
undo
,
Fun
&
redo
);
/* @brief Switch item selection status */
void
setSelected
(
int
itemId
,
bool
sel
);
...
...
@@ -637,10 +637,10 @@ public:
This functions create an undo object and also apply the effect to the corresponding audio if there is any.
Returns true on success, false otherwise (and nothing is modified)
*/
Q_INVOKABLE
bool
requestClipTimeWarp
(
int
clipId
,
double
speed
,
bool
changeDuration
);
Q_INVOKABLE
bool
requestClipTimeWarp
(
int
clipId
,
double
speed
,
bool
pitchCompensate
,
bool
changeDuration
);
/* @brief Same function as above, but doesn't check for paired audio and accumulate undo/redo
*/
bool
requestClipTimeWarp
(
int
clipId
,
double
speed
,
bool
changeDuration
,
Fun
&
undo
,
Fun
&
redo
);
bool
requestClipTimeWarp
(
int
clipId
,
double
speed
,
bool
pitchCompensate
,
bool
changeDuration
,
Fun
&
undo
,
Fun
&
redo
);
void
replugClip
(
int
clipId
);
...
...
src/timeline2/view/dialogs/speeddialog.cpp
View file @
402c251e
...
...
@@ -27,7 +27,7 @@
#include
<KMessageWidget>
#include
<QtMath>
SpeedDialog
::
SpeedDialog
(
QWidget
*
parent
,
double
speed
,
double
minSpeed
,
double
maxSpeed
,
bool
reversed
)
SpeedDialog
::
SpeedDialog
(
QWidget
*
parent
,
double
speed
,
double
minSpeed
,
double
maxSpeed
,
bool
reversed
,
bool
pitch_compensate
)
:
QDialog
(
parent
)
,
ui
(
new
Ui
::
ClipSpeed_UI
)
{
...
...
@@ -48,6 +48,7 @@ SpeedDialog::SpeedDialog(QWidget *parent, double speed, double minSpeed, double
}
ui
->
speedSpin
->
setValue
(
speed
);
ui
->
speedSlider
->
setValue
(
qLn
(
speed
)
*
12
);
ui
->
pitchCompensate
->
setChecked
(
pitch_compensate
);
// Info widget
KMessageWidget
*
infoMessage
=
new
KMessageWidget
(
this
);
...
...
@@ -86,3 +87,8 @@ double SpeedDialog::getValue() const
}
return
val
;
}
bool
SpeedDialog
::
getPitchCompensate
()
const
{
return
ui
->
pitchCompensate
->
isChecked
();
}
src/timeline2/view/dialogs/speeddialog.h
View file @
402c251e
...
...
@@ -33,10 +33,11 @@ class SpeedDialog : public QDialog
Q_OBJECT
public:
explicit
SpeedDialog
(
QWidget
*
parent
,
double
speed
,
double
minSpeed
,
double
maxSpeed
,
bool
reversed
);
explicit
SpeedDialog
(
QWidget
*
parent
,
double
speed
,
double
minSpeed
,
double
maxSpeed
,
bool
reversed
,
bool
pitch_compensate
);
~
SpeedDialog
();
double
getValue
()
const
;
bool
getPitchCompensate
()
const
;
private:
Ui
::
ClipSpeed_UI
*
ui
;
...
...
src/timeline2/view/timelinecontroller.cpp
View file @
402c251e
...
...
@@ -1617,6 +1617,7 @@ void TimelineController::changeItemSpeed(int clipId, double speed)
pCore
->
displayMessage
(
i18n
(
"No item to edit"
),
InformationMessage
,
500
);
return
;
}
bool
pitchCompensate
=
m_model
->
m_allClips
[
clipId
]
->
getIntProperty
(
QStringLiteral
(
"warp_pitch"
));
if
(
qFuzzyCompare
(
speed
,
-
1
))
{
speed
=
100
*
m_model
->
getClipSpeed
(
clipId
);
double
duration
=
m_model
->
getItemPlaytime
(
clipId
);
...
...
@@ -1634,14 +1635,15 @@ void TimelineController::changeItemSpeed(int clipId, double speed)
minSpeed
=
std
::
max
(
minSpeed
,
minSpeed2
);
maxSpeed
=
std
::
min
(
maxSpeed
,
maxSpeed2
);
}
QScopedPointer
<
SpeedDialog
>
d
(
new
SpeedDialog
(
QApplication
::
activeWindow
(),
std
::
abs
(
speed
),
minSpeed
,
maxSpeed
,
speed
<
0
));
QScopedPointer
<
SpeedDialog
>
d
(
new
SpeedDialog
(
QApplication
::
activeWindow
(),
std
::
abs
(
speed
),
minSpeed
,
maxSpeed
,
speed
<
0
,
pitchCompensate
));
if
(
d
->
exec
()
!=
QDialog
::
Accepted
)
{
return
;
}
speed
=
d
->
getValue
();
pitchCompensate
=
d
->
getPitchCompensate
();
qDebug
()
<<
"requesting speed "
<<
speed
;
}
m_model
->
requestClipTimeWarp
(
clipId
,
speed
,
true
);
m_model
->
requestClipTimeWarp
(
clipId
,
speed
,
pitchCompensate
,
true
);
}
void
TimelineController
::
switchCompositing
(
int
mode
)
...
...
src/ui/clipspeed_ui.ui
View file @
402c251e
...
...
@@ -6,15 +6,15 @@
<rect>
<x>
0
</x>
<y>
0
</y>
<width>
476
</width>
<height>
140
</height>
<width>
622
</width>
<height>
244
</height>
</rect>
</property>
<property
name=
"windowTitle"
>
<string>
Dialog
</string>
</property>
<layout
class=
"QGridLayout"
name=
"gridLayout"
>
<item
row=
"
5
"
column=
"2"
>
<item
row=
"
6
"
column=
"2"
>
<widget
class=
"QDialogButtonBox"
name=
"buttonBox"
>
<property
name=
"orientation"
>
<enum>
Qt::Horizontal
</enum>
...
...
@@ -37,27 +37,6 @@
</property>
</spacer>
</item>
<item
row=
"0"
column=
"0"
colspan=
"3"
>
<layout
class=
"QHBoxLayout"
name=
"destination_layout"
>
<item>
<widget
class=
"QLabel"
name=
"label_dest"
>
<property
name=
"text"
>
<string>
Destination
</string>
</property>
</widget>
</item>
<item>
<widget
class=
"KUrlRequester"
name=
"kurlrequester"
>
<property
name=
"sizePolicy"
>
<sizepolicy
hsizetype=
"Preferred"
vsizetype=
"MinimumExpanding"
>
<horstretch>
0
</horstretch>
<verstretch>
0
</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
<item
row=
"1"
column=
"0"
colspan=
"3"
>
<layout
class=
"QHBoxLayout"
name=
"horizontalLayout"
>
<item>
...
...
@@ -114,13 +93,6 @@
</item>
</layout>
</item>
<item
row=
"5"
column=
"1"
>
<widget
class=
"QCheckBox"
name=
"checkBox"
>
<property
name=
"text"
>
<string>
Reverse clip
</string>
</property>
</widget>
</item>
<item
row=
"4"
column=
"0"
colspan=
"3"
>
<layout
class=
"QHBoxLayout"
name=
"infoLayout"
>
<property
name=
"topMargin"
>
...
...
@@ -128,12 +100,47 @@
</property>
</layout>
</item>
<item
row=
"0"
column=
"0"
colspan=
"3"
>
<layout
class=
"QHBoxLayout"
name=
"destination_layout"
>
<item>
<widget
class=
"QLabel"
name=
"label_dest"
>
<property
name=
"text"
>
<string>
Destination
</string>
</property>
</widget>
</item>
<item>
<widget
class=
"KUrlRequester"
name=
"kurlrequester"
>
<property
name=
"sizePolicy"
>
<sizepolicy
hsizetype=
"Preferred"
vsizetype=
"MinimumExpanding"
>
<horstretch>
0
</horstretch>
<verstretch>
0
</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
<item
row=
"2"
column=
"0"
colspan=
"2"
>
<widget
class=
"QCheckBox"
name=
"pitchCompensate"
>
<property
name=
"text"
>
<string>
Pitch compensation
</string>
</property>
</widget>
</item>
<item
row=
"2"
column=
"2"
>
<widget
class=
"QCheckBox"
name=
"checkBox"
>
<property
name=
"text"
>
<string>
Reverse clip
</string>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>
KUrlRequester
</class>
<extends>
Q
Frame
</extends>
<extends>
Q
Widget
</extends>
<header>
kurlrequester.h
</header>
<container>
1
</container>
</customwidget>
...
...
tests/modeltest.cpp
View file @
402c251e
...
...
@@ -1144,7 +1144,7 @@ TEST_CASE("Undo and Redo", "[ClipModel]")
int
temp
;
Fun
undo
=
[]()
{
return
true
;
};
Fun
redo
=
[]()
{
return
true
;
};
REQUIRE_FALSE
(
timeline
->
requestClipCreation
(
"impossible bin id"
,
temp
,
PlaylistState
::
VideoOnly
,
1.
,
undo
,
redo
));
REQUIRE_FALSE
(
timeline
->
requestClipCreation
(
"impossible bin id"
,
temp
,
PlaylistState
::
VideoOnly
,
1.
,
false
,
undo
,
redo
));
}
auto
state0
=
[
&
]()
{
...
...
@@ -1158,7 +1158,7 @@ TEST_CASE("Undo and Redo", "[ClipModel]")
{
Fun
undo
=
[]()
{
return
true
;
};
Fun
redo
=
[]()
{
return
true
;
};
REQUIRE
(
timeline
->
requestClipCreation
(
binId3
,
cid3
,
PlaylistState
::
VideoOnly
,
1.
,
undo
,
redo
));
REQUIRE
(
timeline
->
requestClipCreation
(
binId3
,
cid3
,
PlaylistState
::
VideoOnly
,
1.
,
false
,
undo
,
redo
));
pCore
->
pushUndo
(
undo
,
redo
,
QString
());
}
...
...
@@ -1175,7 +1175,7 @@ TEST_CASE("Undo and Redo", "[ClipModel]")
{
Fun
undo
=
[]()
{
return
true
;
};
Fun
redo
=
[]()
{
return
true
;
};
REQUIRE
(
timeline
->
requestClipCreation
(
binId4
,
cid4
,
PlaylistState
::
VideoOnly
,
1.
,
undo
,
redo
));
REQUIRE
(
timeline
->
requestClipCreation
(
binId4
,
cid4
,
PlaylistState
::
VideoOnly
,
1.
,
false
,
undo
,
redo
));
pCore
->
pushUndo
(
undo
,
redo
,
QString
());
}
...
...
@@ -1638,7 +1638,7 @@ TEST_CASE("Undo and Redo", "[ClipModel]")
{
std
::
function
<
bool
(
void
)
>
undo
=
[]()
{
return
true
;
};
std
::
function
<
bool
(
void
)
>
redo
=
[]()
{
return
true
;
};
REQUIRE
(
timeline
->
requestClipCreation
(
binId
,
cid6
,
PlaylistState
::
VideoOnly
,
1.
,
undo
,
redo
));