]> git.sesse.net Git - kdenlive/blobdiff - src/clipitem.cpp
Fix LADSPA effects, should work now:
[kdenlive] / src / clipitem.cpp
index e9f81b2cfa18a2d7b16663ec29d4c9d763aa82b7..1b493385ae4661d36d449d56581480c230129950 100644 (file)
@@ -42,8 +42,9 @@
 
 ClipItem::ClipItem(DocClipBase *clip, ItemInfo info, double fps, bool generateThumbs)
         : AbstractClipItem(info, QRectF(), fps), m_clip(clip), m_resizeMode(NONE), m_grabPoint(0), m_maxTrack(0), m_hasThumbs(false), startThumbTimer(NULL), endThumbTimer(NULL), audioThumbWasDrawn(false), m_opacity(1.0), m_timeLine(0), m_startThumbRequested(false), m_endThumbRequested(false), m_startFade(0), m_endFade(0), m_hover(false), m_selectedEffect(-1), m_speed(1.0), framePixelWidth(0), m_startPix(QPixmap()), m_endPix(QPixmap()) {
-    setRect(0, 0, (info.endPos - info.startPos).frames(fps) - 0.02, (qreal)(KdenliveSettings::trackheight() - 2));
-    setPos((qreal) info.startPos.frames(fps), (qreal)(info.track * KdenliveSettings::trackheight()) + 1);
+    setZValue(1);
+    setRect(0, 0, (info.endPos - info.startPos).frames(fps) - 0.02, (double)(KdenliveSettings::trackheight() - 2));
+    setPos(info.startPos.frames(fps), (double)(info.track * KdenliveSettings::trackheight()) + 1);
 
     m_clipName = clip->name();
     m_producer = clip->getId();
@@ -138,6 +139,7 @@ void ClipItem::initEffect(QDomElement effect) {
     QDomNodeList params = effect.elementsByTagName("parameter");
     for (int i = 0; i < params.count(); i++) {
         QDomElement e = params.item(i).toElement();
+        kDebug() << "// inint eff: " << e.attribute("name");
         if (!e.isNull() && e.attribute("type") == "keyframe") {
             QString def = e.attribute("default");
             // Effect has a keyframe type parameter, we need to set the values
@@ -148,6 +150,20 @@ void ClipItem::initEffect(QDomElement effect) {
             }
         }
     }
+
+    if (effect.attribute("tag") == "volume") {
+        if (effect.attribute("id") == "fadeout") {
+            int end = (duration() + cropStart()).frames(m_fps);
+            int start = end - EffectsList::parameter(effect, "in").toInt();
+            EffectsList::setParameter(effect, "in", QString::number(start));
+            EffectsList::setParameter(effect, "out", QString::number(end));
+        } else if (effect.attribute("id") == "fadein") {
+            int start = cropStart().frames(m_fps);
+            int end = start + EffectsList::parameter(effect, "out").toInt();
+            EffectsList::setParameter(effect, "in", QString::number(start));
+            EffectsList::setParameter(effect, "out", QString::number(end));
+        }
+    }
 }
 
 bool ClipItem::checkKeyFrames() {
@@ -336,7 +352,7 @@ void ClipItem::slotFetchThumbs() {
     if (m_endPix.isNull() && m_startPix.isNull()) {
         m_startThumbRequested = true;
         m_endThumbRequested = true;
-        emit getThumb((int)m_cropStart.frames(m_fps), (int)(m_cropStart + m_cropDuration).frames(m_fps) - 1);
+        emit getThumb((int)cropStart().frames(m_fps), (int)(cropStart() + cropDuration()).frames(m_fps) - 1);
     } else {
         if (m_endPix.isNull()) {
             slotGetEndThumb();
@@ -362,14 +378,14 @@ void ClipItem::slotFetchThumbs() {
 
 void ClipItem::slotGetStartThumb() {
     m_startThumbRequested = true;
-    emit getThumb((int)m_cropStart.frames(m_fps), -1);
+    emit getThumb((int)cropStart().frames(m_fps), -1);
     //videoThumbProducer.setThumbFrames(m_clip->producer(), (int)m_cropStart.frames(m_fps),  - 1);
     //videoThumbProducer.start(QThread::LowestPriority);
 }
 
 void ClipItem::slotGetEndThumb() {
     m_endThumbRequested = true;
-    emit getThumb(-1, (int)(m_cropStart + m_cropDuration).frames(m_fps) - 1);
+    emit getThumb(-1, (int)(cropStart() + cropDuration()).frames(m_fps) - 1);
     //videoThumbProducer.setThumbFrames(m_clip->producer(), -1, (int)(m_cropStart + m_cropDuration).frames(m_fps) - 1);
     //videoThumbProducer.start(QThread::LowestPriority);
 }
@@ -399,12 +415,12 @@ void ClipItem::slotThumbReady(int frame, QPixmap pix) {
     if (scene() == NULL) return;
     QRectF r = sceneBoundingRect();
     double width = m_startPix.width() / projectScene()->scale();
-    if (m_startThumbRequested && frame == m_cropStart.frames(m_fps)) {
+    if (m_startThumbRequested && frame == cropStart().frames(m_fps)) {
         m_startPix = pix;
         m_startThumbRequested = false;
         double height = r.height();
         update(r.x(), r.y(), width, height);
-    } else if (m_endThumbRequested && frame == (m_cropStart + m_cropDuration).frames(m_fps) - 1) {
+    } else if (m_endThumbRequested && frame == (cropStart() + cropDuration()).frames(m_fps) - 1) {
         m_endPix = pix;
         m_endThumbRequested = false;
         double height = r.height();
@@ -412,14 +428,22 @@ void ClipItem::slotThumbReady(int frame, QPixmap pix) {
     }
 }
 
-void ClipItem::slotSetStartThumb(QPixmap pix) {
+void ClipItem::slotSetStartThumb(const QPixmap pix) {
     m_startPix = pix;
 }
 
-void ClipItem::slotSetEndThumb(QPixmap pix) {
+void ClipItem::slotSetEndThumb(const QPixmap pix) {
     m_endPix = pix;
 }
 
+QPixmap ClipItem::startThumb() const {
+    return m_startPix;
+}
+
+QPixmap ClipItem::endThumb() const {
+    return m_endPix;
+}
+
 void ClipItem::slotGotAudioData() {
     audioThumbReady = true;
     if (m_clipType == AV) {
@@ -472,7 +496,9 @@ void ClipItem::animate(qreal value) {
 void ClipItem::paint(QPainter *painter,
                      const QStyleOptionGraphicsItem *option,
                      QWidget *) {
-    painter->setOpacity(m_opacity);
+    /*if (parentItem()) m_opacity = 0.5;
+    else m_opacity = 1.0;
+    painter->setOpacity(m_opacity);*/
     QBrush paintColor = brush();
     if (isSelected()) paintColor = QBrush(QColor(79, 93, 121));
     QRectF br = rect();
@@ -484,16 +510,15 @@ void ClipItem::paint(QPainter *painter,
     const double scale = option->matrix.m11();
 
 
-
     //painter->setRenderHints(QPainter::Antialiasing);
 
     //QPainterPath roundRectPathUpper = upperRectPart(br), roundRectPathLower = lowerRectPart(br);
     painter->setClipRect(exposed);
 
-    // build path around clip
+    //build path around clip
     //QPainterPath resultClipPath = roundRectPathUpper.united(roundRectPathLower);
     //painter->fillPath(resultClipPath, paintColor);
-    painter->fillRect(br, paintColor);
+    painter->fillRect(exposed, paintColor);
 
     //painter->setClipPath(resultClipPath, Qt::IntersectClip);
 
@@ -529,7 +554,7 @@ void ClipItem::paint(QPainter *painter,
     }
 
     // draw audio thumbnails
-    if (KdenliveSettings::audiothumbnails() && ((m_clipType == AV && exposed.bottom() > (itemHeight / 2)) || m_clipType == AUDIO) && audioThumbReady) {
+    if (KdenliveSettings::audiothumbnails() && m_speed == 1.0 && ((m_clipType == AV && exposed.bottom() > (itemHeight / 2)) || m_clipType == AUDIO) && audioThumbReady) {
 
         double startpixel = exposed.left();
         if (startpixel < 0)
@@ -557,7 +582,9 @@ void ClipItem::paint(QPainter *painter,
         const int mappedEndPixel =  painter->matrix().map(QPointF(endpixel + cropLeft, 0)).x() - clipStart;
         cropLeft = cropLeft * scale;
 
-        emit prepareAudioThumb(scale, mappedStartPixel, mappedEndPixel, channels);
+        if (channels >= 1) {
+            emit prepareAudioThumb(scale, mappedStartPixel, mappedEndPixel, channels);
+        }
 
         for (int startCache = mappedStartPixel - (mappedStartPixel) % 100; startCache < mappedEndPixel; startCache += 100) {
             if (audioThumbCachePic.contains(startCache) && !audioThumbCachePic[startCache].isNull())
@@ -692,11 +719,10 @@ void ClipItem::paint(QPainter *painter,
 
     //kDebug()<<"/// ITEM PAINTING:: exposed="<<exposed<<", RECT = "<<rect();
 
-    exposed.setRight(exposed.right() + 1);
+    // expand clip rect to allow correct painting of clip border
+    exposed.setRight(exposed.right() + 1 / scale + 0.5);
     exposed.setBottom(exposed.bottom() + 1);
     painter->setClipRect(exposed);
-    //painter->setClipping(false);
-    //painter->fillRect(exposed, Qt::blue);
     painter->setPen(pen);
     painter->drawRect(br);
 }
@@ -793,6 +819,12 @@ void ClipItem::slotPrepareAudioThumb(double pixelForOneFrame, int startpixel, in
         pixpainter.setPen(audiopen);
         //pixpainter.setRenderHint(QPainter::Antialiasing,true);
         //pixpainter.drawLine(0,0,100,re.height());
+        // Bail out, if caller provided invalid data
+        if (channels <= 0) {
+            kWarning() << "Unable to draw image with " << channels << "number of channels";
+            return;
+        }
+
         int channelHeight = audioThumbCachePic[startCache].height() / channels;
 
         for (int i = 0;i < channels;i++) {
@@ -858,21 +890,23 @@ uint ClipItem::fadeOut() const {
 
 
 void ClipItem::setFadeIn(int pos) {
+    if (pos == m_startFade) return;
     int oldIn = m_startFade;
     if (pos < 0) pos = 0;
-    if (pos > m_cropDuration.frames(m_fps)) pos = (int)(m_cropDuration.frames(m_fps) / 2);
+    if (pos > m_cropDuration.frames(m_fps)) pos = (int)(m_cropDuration.frames(m_fps));
     m_startFade = pos;
     QRectF rect = boundingRect();
     update(rect.x(), rect.y(), qMax(oldIn, pos), rect.height());
 }
 
 void ClipItem::setFadeOut(int pos) {
+    if (pos == m_endFade) return;
     int oldOut = m_endFade;
     if (pos < 0) pos = 0;
-    if (pos > m_cropDuration.frames(m_fps)) pos = (int)(m_cropDuration.frames(m_fps) / 2);
+    if (pos > m_cropDuration.frames(m_fps)) pos = (int)(m_cropDuration.frames(m_fps));
     m_endFade = pos;
     QRectF rect = boundingRect();
-    update(rect.x() + rect.width() - qMax(oldOut, pos), rect.y(), pos, rect.height());
+    update(rect.x() + rect.width() - qMax(oldOut, pos), rect.y(), qMax(oldOut, pos), rect.height());
 
 }
 
@@ -915,12 +949,12 @@ void ClipItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *) {
     update(r.right() - width, r.y() + height, width, height);
 }
 
-void ClipItem::resizeStart(int posx) {
+void ClipItem::resizeStart(int posx, double speed) {
     const int min = (startPos() - cropStart()).frames(m_fps);
     if (posx < min) posx = min;
     if (posx == startPos().frames(m_fps)) return;
     const int previous = cropStart().frames(m_fps);
-    AbstractClipItem::resizeStart(posx);
+    AbstractClipItem::resizeStart(posx, m_speed);
     checkEffectsKeyframesPos(previous, cropStart().frames(m_fps), true);
     if (m_hasThumbs && KdenliveSettings::videothumbnails()) {
         /*connect(m_clip->thumbProducer(), SIGNAL(thumbReady(int, QPixmap)), this, SLOT(slotThumbReady(int, QPixmap)));*/
@@ -928,12 +962,13 @@ void ClipItem::resizeStart(int posx) {
     }
 }
 
-void ClipItem::resizeEnd(int posx, bool updateKeyFrames) {
+void ClipItem::resizeEnd(int posx, double speed, bool updateKeyFrames) {
     const int max = (startPos() - cropStart() + maxDuration()).frames(m_fps) + 1;
     if (posx > max) posx = max;
     if (posx == endPos().frames(m_fps)) return;
+    //kDebug() << "// NEW POS: " << posx << ", OLD END: " << endPos().frames(m_fps);
     const int previous = (cropStart() + duration()).frames(m_fps);
-    AbstractClipItem::resizeEnd(posx);
+    AbstractClipItem::resizeEnd(posx, m_speed);
     if (updateKeyFrames) checkEffectsKeyframesPos(previous, (cropStart() + duration()).frames(m_fps), false);
     if (m_hasThumbs && KdenliveSettings::videothumbnails()) {
         /*connect(m_clip->thumbProducer(), SIGNAL(thumbReady(int, QPixmap)), this, SLOT(slotThumbReady(int, QPixmap)));*/
@@ -977,18 +1012,17 @@ void ClipItem::checkEffectsKeyframesPos(const int previous, const int current, b
     if (m_selectedEffect >= 0) setSelectedEffect(m_selectedEffect);
 }
 
-
 //virtual
 QVariant ClipItem::itemChange(GraphicsItemChange change, const QVariant &value) {
     if (change == ItemPositionChange && scene()) {
         // calculate new position.
-        if (group() != 0) return pos();
+        if (parentItem()) return pos();
         QPointF newPos = value.toPointF();
-        //kDebug() << "/// MOVING CLIP ITEM.------------";
+        //kDebug() << "/// MOVING CLIP ITEM.------------\n++++++++++";
         int xpos = projectScene()->getSnapPointForPos((int) newPos.x(), KdenliveSettings::snaptopoints());
         xpos = qMax(xpos, 0);
         newPos.setX(xpos);
-        int newTrack = (newPos.y() + KdenliveSettings::trackheight() / 2) / KdenliveSettings::trackheight();
+        int newTrack = newPos.y() / KdenliveSettings::trackheight();
         newTrack = qMin(newTrack, projectScene()->tracksCount() - 1);
         newTrack = qMax(newTrack, 0);
         newPos.setY((int)(newTrack  * KdenliveSettings::trackheight() + 1));
@@ -997,38 +1031,37 @@ QVariant ClipItem::itemChange(GraphicsItemChange change, const QVariant &value)
         sceneShape.translate(newPos);
         QList<QGraphicsItem*> items = scene()->items(sceneShape, Qt::IntersectsItemShape);
         items.removeAll(this);
-
+        bool forwardMove = newPos.x() > pos().x();
+        int offset = 0;
         if (!items.isEmpty()) {
             for (int i = 0; i < items.count(); i++) {
                 if (items.at(i)->type() == type()) {
                     // Collision!
                     QPointF otherPos = items.at(i)->pos();
-                    if ((int) otherPos.y() != (int) pos().y()) return pos();
-                    if (pos().x() < otherPos.x()) {
-                        // move clip just before colliding clip
-                        int npos = (static_cast < AbstractClipItem* >(items.at(i))->startPos() - m_cropDuration).frames(m_fps);
-                        // check we don't run into another clip
-                        newPos.setX(npos);
-                        sceneShape = rect();
-                        sceneShape.translate(newPos);
-                        QList<QGraphicsItem*> subitems = scene()->items(sceneShape, Qt::IntersectsItemShape);
-                        items.removeAll(this);
-                        for (int j = 0; j < subitems.count(); j++) {
-                            if (subitems.at(j)->type() == type()) return pos();
-                        }
+                    if ((int) otherPos.y() != (int) pos().y()) {
+                        return pos();
+                    }
+                    if (forwardMove) {
+                        offset = qMax(offset, (int)(newPos.x() - (static_cast < AbstractClipItem* >(items.at(i))->startPos() - m_cropDuration).frames(m_fps)));
                     } else {
-                        // get pos just after colliding clip
-                        int npos = static_cast < AbstractClipItem* >(items.at(i))->endPos().frames(m_fps);
-                        // check we don't run into another clip
-                        newPos.setX(npos);
-                        sceneShape = rect();
-                        sceneShape.translate(newPos);
+                        offset = qMax(offset, (int)((static_cast < AbstractClipItem* >(items.at(i))->endPos().frames(m_fps)) - newPos.x()));
+                    }
+
+                    if (offset > 0) {
+                        if (forwardMove) {
+                            sceneShape.translate(QPointF(-offset, 0));
+                            newPos.setX(newPos.x() - offset);
+                        } else {
+                            sceneShape.translate(QPointF(offset, 0));
+                            newPos.setX(newPos.x() + offset);
+                        }
                         QList<QGraphicsItem*> subitems = scene()->items(sceneShape, Qt::IntersectsItemShape);
-                        items.removeAll(this);
+                        subitems.removeAll(this);
                         for (int j = 0; j < subitems.count(); j++) {
                             if (subitems.at(j)->type() == type()) return pos();
                         }
                     }
+
                     m_track = newTrack;
                     m_startPos = GenTime((int) newPos.x(), m_fps);
                     return newPos;
@@ -1069,7 +1102,7 @@ QDomElement ClipItem::effectAt(int ix) {
 }
 
 void ClipItem::setEffectAt(int ix, QDomElement effect) {
-    kDebug() << "CHange EFFECT AT: " << ix << ", CURR: " << m_effectList.at(ix).attribute("tag") << ", NEW: " << effect.attribute("tag");
+    kDebug() << "CHange EFFECT AT: " << ix << ", CURR: " << m_effectList.at(ix).attribute("tag") << ", NEW: " << effect.attribute("tag");
     effect.setAttribute("kdenlive_ix", ix + 1);
     m_effectList.insert(ix, effect);
     m_effectList.removeAt(ix + 1);
@@ -1082,38 +1115,47 @@ void ClipItem::setEffectAt(int ix, QDomElement effect) {
     }
 }
 
-QHash <QString, QString> ClipItem::addEffect(QDomElement effect, bool animate) {
-    QHash <QString, QString> effectParams;
+EffectsParameterList ClipItem::addEffect(QDomElement effect, bool animate) {
+
     bool needRepaint = false;
     /*QDomDocument doc;
     doc.appendChild(doc.importNode(effect, true));
     kDebug() << "///////  CLIP ADD EFFECT: " << doc.toString();*/
     m_effectList.append(effect);
-    effectParams["tag"] = effect.attribute("tag");
+
+    EffectsParameterList parameters;
+    parameters.addParam("tag", effect.attribute("tag"));
+    parameters.addParam("kdenlive_ix", effect.attribute("kdenlive_ix"));
+    if (effect.hasAttribute("src")) parameters.addParam("src", effect.attribute("src"));
+
+    QString state = effect.attribute("disabled");
+    if (!state.isEmpty()) {
+        parameters.addParam("disabled", state);
+    }
+
     QString effectId = effect.attribute("id");
     if (effectId.isEmpty()) effectId = effect.attribute("tag");
-    effectParams["id"] = effectId;
-    effectParams["kdenlive_ix"] = effect.attribute("kdenlive_ix");
-    QString state = effect.attribute("disabled");
-    if (!state.isEmpty()) effectParams["disabled"] = state;
+    parameters.addParam("id", effectId);
+
     QDomNodeList params = effect.elementsByTagName("parameter");
     int fade = 0;
     for (int i = 0; i < params.count(); i++) {
         QDomElement e = params.item(i).toElement();
         if (!e.isNull()) {
             if (e.attribute("type") == "keyframe") {
-                effectParams["keyframes"] = e.attribute("keyframes");
-                effectParams["min"] = e.attribute("min");
-                effectParams["max"] = e.attribute("max");
-                effectParams["factor"] = e.attribute("factor", "1");
-                effectParams["starttag"] = e.attribute("starttag", "start");
-                effectParams["endtag"] = e.attribute("endtag", "end");
+                parameters.addParam("keyframes", e.attribute("keyframes"));
+                parameters.addParam("max", e.attribute("max"));
+                parameters.addParam("min", e.attribute("min"));
+                parameters.addParam("factor", e.attribute("factor", "1"));
+                parameters.addParam("starttag", e.attribute("starttag", "start"));
+                parameters.addParam("endtag", e.attribute("endtag", "end"));
             }
 
             double f = e.attribute("factor", "1").toDouble();
 
             if (f == 1) {
-                effectParams[e.attribute("name")] = e.attribute("value");
+                parameters.addParam(e.attribute("name"), e.attribute("value"));
+
                 // check if it is a fade effect
                 if (effectId == "fadein") {
                     needRepaint = true;
@@ -1125,7 +1167,7 @@ QHash <QString, QString> ClipItem::addEffect(QDomElement effect, bool animate) {
                     else if (e.attribute("name") == "in") fade += e.attribute("value").toInt();
                 }
             } else {
-                effectParams[e.attribute("name")] =  QString::number(e.attribute("value").toDouble() / f);
+                parameters.addParam(e.attribute("name"), QString::number(e.attribute("value").toDouble() / f));
             }
         }
     }
@@ -1144,28 +1186,32 @@ QHash <QString, QString> ClipItem::addEffect(QDomElement effect, bool animate) {
         m_selectedEffect = 0;
         setSelectedEffect(m_selectedEffect);
     }
-    return effectParams;
+    return parameters;
 }
 
-QHash <QString, QString> ClipItem::getEffectArgs(QDomElement effect) {
-    QHash <QString, QString> effectParams;
-    effectParams["tag"] = effect.attribute("tag");
-    effectParams["kdenlive_ix"] = effect.attribute("kdenlive_ix");
-    effectParams["id"] = effect.attribute("id");
+EffectsParameterList ClipItem::getEffectArgs(QDomElement effect) {
+    EffectsParameterList parameters;
+    parameters.addParam("tag", effect.attribute("tag"));
+    parameters.addParam("kdenlive_ix", effect.attribute("kdenlive_ix"));
+    parameters.addParam("id", effect.attribute("id"));
+    if (effect.hasAttribute("src")) parameters.addParam("src", effect.attribute("src"));
     QString state = effect.attribute("disabled");
-    if (!state.isEmpty()) effectParams["disabled"] = state;
+    if (!state.isEmpty()) {
+        parameters.addParam("disabled", state);
+    }
+
     QDomNodeList params = effect.elementsByTagName("parameter");
     for (int i = 0; i < params.count(); i++) {
         QDomElement e = params.item(i).toElement();
         //kDebug() << "/ / / /SENDING EFFECT PARAM: " << e.attribute("type") << ", NAME_ " << e.attribute("tag");
         if (e.attribute("type") == "keyframe") {
             kDebug() << "/ / / /SENDING KEYFR EFFECT TYPE";
-            effectParams["keyframes"] = e.attribute("keyframes");
-            effectParams["max"] = e.attribute("max");
-            effectParams["min"] = e.attribute("min");
-            effectParams["factor"] = e.attribute("factor", "1");
-            effectParams["starttag"] = e.attribute("starttag", "start");
-            effectParams["endtag"] = e.attribute("endtag", "end");
+            parameters.addParam("keyframes", e.attribute("keyframes"));
+            parameters.addParam("max", e.attribute("max"));
+            parameters.addParam("min", e.attribute("min"));
+            parameters.addParam("factor", e.attribute("factor", "1"));
+            parameters.addParam("starttag", e.attribute("starttag", "start"));
+            parameters.addParam("endtag", e.attribute("endtag", "end"));
         } else if (e.attribute("namedesc").contains(";")) {
             QString format = e.attribute("format");
             QStringList separators = format.split("%d", QString::SkipEmptyParts);
@@ -1178,14 +1224,16 @@ QHash <QString, QString> ClipItem::getEffectArgs(QDomElement effect) {
                 txtNeu << separators[i];
                 txtNeu << (int)(values[i+1].toDouble());
             }
-            effectParams["start"] = neu;
+            parameters.addParam("start", neu);
         } else {
-            if (e.attribute("factor", "1") != "1")
-                effectParams[e.attribute("name")] =  QString::number(e.attribute("value").toDouble() / e.attribute("factor").toDouble());
-            else effectParams[e.attribute("name")] = e.attribute("value");
+            if (e.attribute("factor", "1") != "1") {
+                parameters.addParam(e.attribute("name"), QString::number(e.attribute("value").toDouble() / e.attribute("factor").toDouble()));
+            } else {
+                parameters.addParam(e.attribute("name"), e.attribute("value"));
+            }
         }
     }
-    return effectParams;
+    return parameters;
 }
 
 void ClipItem::deleteEffect(QString index) {
@@ -1217,13 +1265,25 @@ void ClipItem::setSpeed(const double speed) {
     m_speed = speed;
     if (m_speed == 1.0) m_clipName = baseClip()->name();
     else m_clipName = baseClip()->name() + " - " + QString::number(speed * 100, 'f', 0) + "%";
-    update();
+    //update();
 }
 
 GenTime ClipItem::maxDuration() const {
     return m_maxDuration / m_speed;
 }
 
+GenTime ClipItem::cropStart() const {
+    return m_cropStart / m_speed;
+}
+
+GenTime ClipItem::cropDuration() const {
+    return m_cropDuration / m_speed;
+}
+
+GenTime ClipItem::endPos() const {
+    return m_startPos + cropDuration();
+}
+
 //virtual
 void ClipItem::dropEvent(QGraphicsSceneDragDropEvent * event) {
     QString effects = QString(event->mimeData()->data("kdenlive/effectslist"));