Members of the KDE Community are recommended to subscribe to the kde-community mailing list at https://mail.kde.org/mailman/listinfo/kde-community to allow them to participate in important discussions and receive other important announcements

First steps to support slowmotion (through timeline context menu)

parent 1c0e968f
......@@ -291,7 +291,10 @@ void ClipModel::refreshProducerFromBin(PlaylistState::ClipState state)
Q_ASSERT(false);
}
}
return useTimewarpProducer(m_producer->get_double("warp_speed"), space);
std::function<bool(void)> local_undo = []() { return true; };
std::function<bool(void)> local_redo = []() { return true; };
useTimewarpProducer(m_producer->get_double("warp_speed"), space, local_undo, local_redo);
return;
}
QWriteLocker locker(&m_lock);
int in = getIn();
......@@ -308,7 +311,20 @@ void ClipModel::refreshProducerFromBin(PlaylistState::ClipState state)
m_endlessResize = !binClip->hasLimitedDuration();
}
void ClipModel::useTimewarpProducer(double speed, int extraSpace)
bool ClipModel::useTimewarpProducer(double speed, int extraSpace, Fun &undo, Fun &redo)
{
double previousSpeed = getSpeed();
auto operation = useTimewarpProducer_lambda(speed, extraSpace);
if (operation()) {
auto reverse = useTimewarpProducer_lambda(previousSpeed, extraSpace);
UPDATE_UNDO_REDO(operation, reverse, undo, redo);
return true;
}
return false;
}
Fun ClipModel::useTimewarpProducer_lambda(double speed, int extraSpace)
{
Q_UNUSED(extraSpace)
......@@ -318,7 +334,6 @@ void ClipModel::useTimewarpProducer(double speed, int extraSpace)
int out = getOut();
int warp_in;
int warp_out;
qDebug() << "// SLOWMO CLIP SERVICE: " << getProperty("mlt_service");
if (getProperty("mlt_service") == QLatin1String("timewarp")) {
// slowmotion producer, get current speed
warp_in = m_producer->get_int("warp_in");
......@@ -333,20 +348,28 @@ void ClipModel::useTimewarpProducer(double speed, int extraSpace)
out = warp_out / speed;
std::shared_ptr<ProjectClip> binClip = pCore->projectItemModel()->getClipByBinID(m_binClipId);
std::shared_ptr<Mlt::Producer> originalProducer = binClip->originalProducer();
if (qFuzzyCompare(speed, 1.0)) {
m_producer.reset(originalProducer->cut(in, out));
} else {
QString resource = QString("%1:%2").arg(speed).arg(originalProducer->get("resource"));
std::shared_ptr<Mlt::Producer> warpProducer(new Mlt::Producer(*originalProducer->profile(), "timewarp", resource.toUtf8().constData()));
m_producer.reset(warpProducer->cut(in, out));
}
// replant effect stack in updated service
m_effectStack->resetService(m_producer);
m_producer->set("kdenlive:id", binClip->AbstractProjectItem::clipId().toUtf8().constData());
m_producer->set("_kdenlive_cid", m_id);
m_producer->set("warp_in", warp_in);
m_producer->set("warp_out", warp_out);
m_endlessResize = !binClip->hasLimitedDuration();
bool limitedDuration = binClip->hasLimitedDuration();
return [originalProducer, speed, in, out, warp_in, warp_out, limitedDuration, this]() {
if (qFuzzyCompare(speed, 1.0)) {
m_producer.reset(originalProducer->cut(in, out));
} else {
QString resource = QString("timewarp:%1:%2").arg(speed).arg(originalProducer->get("resource"));
Mlt::Profile *prof = new Mlt::Profile(pCore->getCurrentProfilePath().toUtf8().constData());
std::shared_ptr<Mlt::Producer> warpProducer(new Mlt::Producer(*prof, resource.toUtf8().constData()));
m_producer = std::move(warpProducer);
setInOut(in, out);
}
// replant effect stack in updated service
m_effectStack->resetService(m_producer);
m_producer->set("kdenlive:id", m_binClipId.toUtf8().constData());
m_producer->set("_kdenlive_cid", m_id);
m_producer->set("warp_in", warp_in);
m_producer->set("warp_out", warp_out);
m_endlessResize = !limitedDuration;
return true;
};
return []() { return false; };
}
QVariant ClipModel::getAudioWaveform()
......
......@@ -134,7 +134,8 @@ protected:
/* @brief This functions should be called when the producer of the binClip changes, to allow refresh */
void refreshProducerFromBin(PlaylistState::ClipState state = PlaylistState::Original);
/* @brief This functions replaces the current producer with a slowmotion one */
void useTimewarpProducer(double speed, int extraSpace);
bool useTimewarpProducer(double speed, int extraSpace, Fun &undo, Fun &redo);
Fun useTimewarpProducer_lambda(double speed, int extraSpace);
/** @brief Returns the marker model associated with this clip */
std::shared_ptr<MarkerListModel> getMarkerModel() const;
......
......@@ -286,6 +286,8 @@ QVariant TimelineItemModel::data(const QModelIndex &index, int role) const
return clip->fadeOut();
case ReloadThumbRole:
return clip->forceThumbReload;
case SpeedRole:
return clip->getSpeed();
default:
break;
}
......
......@@ -623,9 +623,6 @@ bool TimelineModel::requestClipInsertion(const QString &binClipId, int trackId,
int newTrack = possibleTracks.takeFirst();
move = requestClipMove(newId, newTrack, position, true, false, undo, redo);
}
/*std::unordered_set<int> groupClips;
groupClips.insert(cid);
groupClips.insert(newId);*/
if (!res || !move) {
pCore->displayMessage(i18n("Audio split failed"), ErrorMessage);
} else {
......@@ -2044,7 +2041,7 @@ void TimelineModel::requestClipUpdate(int clipId, const QVector<int> &roles)
notifyChange(modelIndex, modelIndex, roles);
}
bool TimelineModel::requestClipTimeWarp(int clipId, double speed)
bool TimelineModel::requestClipTimeWarp(int clipId, double speed, Fun &undo, Fun &redo)
{
QWriteLocker locker(&m_lock);
std::function<bool(void)> local_undo = []() { return true; };
......@@ -2057,26 +2054,27 @@ bool TimelineModel::requestClipTimeWarp(int clipId, double speed)
if (old_trackId != -1) {
int blankSpace = getTrackById(old_trackId)->getBlankSizeNearClip(clipId, true);
qDebug() << "// FOUND BLANK AFTER CLIP: " << blankSpace;
getTrackById(old_trackId)->requestClipDeletion(clipId, false, local_undo, local_redo);
m_allClips[clipId]->useTimewarpProducer(speed, blankSpace);
getTrackById(old_trackId)->requestClipDeletion(clipId, true, local_undo, local_redo);
m_allClips[clipId]->useTimewarpProducer(speed, blankSpace, local_undo, local_redo);
getTrackById(old_trackId)->requestClipInsertion(clipId, oldPos, true, local_undo, local_redo);
} else {
m_allClips[clipId]->useTimewarpProducer(speed, -1);
m_allClips[clipId]->useTimewarpProducer(speed, -1, local_undo, local_redo);
}
QModelIndex modelIndex = makeClipIndexFromID(clipId);
notifyChange(modelIndex, modelIndex, false, true, true);
QVector<int> roles;
roles.push_back(SpeedRole);
notifyChange(modelIndex, modelIndex, roles);
UPDATE_UNDO_REDO(local_redo, local_undo, undo, redo);
return true;
}
bool TimelineModel::changeItemSpeed(int clipId, int speed)
{
Fun local_undo = []() { return true; };
Fun local_redo = []() { return true; };
double currentSpeed = m_allClips[clipId]->getSpeed();
Fun operation = [this, clipId, speed]() { return requestClipTimeWarp(clipId, speed / 100.0); };
Fun reverse = [this, clipId, currentSpeed]() { return requestClipTimeWarp(clipId, currentSpeed); };
if (operation()) {
UPDATE_UNDO_REDO(operation, reverse, local_undo, local_redo);
Fun undo = []() { return true; };
Fun redo = []() { return true; };
bool res = requestClipTimeWarp(clipId, speed / 100.0, undo, redo);
if (res) {
PUSH_UNDO(undo, redo, i18n("Change clip speed"));
return true;
}
return false;
......
......@@ -497,7 +497,7 @@ public:
std::shared_ptr<EffectStackModel> getTrackEffectStackModel(int trackId);
/** @brief Add slowmotion effect to clip in timeline. */
bool requestClipTimeWarp(int clipId, double speed);
bool requestClipTimeWarp(int clipId, double speed, Fun &undo, Fun &redo);
bool changeItemSpeed(int clipId, int speed);
void replugClip(int clipId);
......
......@@ -403,7 +403,7 @@ Rectangle {
visible: clipRoot.width > width / 2
Text {
id: label
text: clipName
text: clipName + (clipRoot.speed != 1.0 ? ' [' + Math.round(clipRoot.speed*100) + '%]': '')
font.pixelSize: root.baseUnit * 1.2
anchors {
top: labelRect.top
......
......@@ -59,6 +59,12 @@ OLD.Menu {
OLD.MenuSeparator {
visible: true
}
OLD.MenuItem {
visible: true
text: i18n('Change Speed')
onTriggered: timeline.changeItemSpeed(clipId, -1)
}
OLD.MenuItem {
text: i18n('Clip in Project Bin')
onTriggered: timeline.triggerAction('clip_in_project_tree')
......
......@@ -179,6 +179,12 @@ Column{
value: model.resource
when: loader.status == Loader.Ready && !loader.item.isComposition
}
Binding {
target: loader.item
property: "speed"
value: model.speed
when: loader.status == Loader.Ready && !loader.item.isComposition
}
Binding {
target: loader.item
property: "forceReloadThumb"
......@@ -211,7 +217,6 @@ Column{
//item.aTrack = model.a_track
}
item.trackId= trackRoot.trackId
item.speed= 1 //model.speed
//item.selected= trackRoot.selection.indexOf(item.clipId) !== -1
//console.log(width, height);
}
......
......@@ -44,6 +44,7 @@
#include <KActionCollection>
#include <QApplication>
#include <QQuickItem>
#include <QInputDialog>
int TimelineController::m_duration = 0;
......@@ -1225,6 +1226,15 @@ void TimelineController::invalidateClip(int cid)
void TimelineController::changeItemSpeed(int clipId, int speed)
{
if (speed == -1) {
speed = 100 * m_model->m_allClips[clipId]->getSpeed();
bool ok;
speed = QInputDialog::getInt(QApplication::activeWindow(), i18n("Clip Speed"), i18n("Percentage"), speed, -100000, 100000, 1, &ok);
if (!ok) {
return;
}
}
m_model->changeItemSpeed(clipId, speed);
}
......
......@@ -309,7 +309,7 @@ public:
void switchCompositing(int mode);
/** @brief Change a clip item's speed in timeline */
void changeItemSpeed(int clipId, int speed);
Q_INVOKABLE void changeItemSpeed(int clipId, int speed);
/** @brief Delete selected zone and fill gap by moving following clips*/
void extractZone();
/** @brief Delete selected zone */
......
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