]> git.sesse.net Git - kdenlive/commitdiff
Merge branch 'audioAlign'
authorSimon A. Eugster <simon.eu@gmail.com>
Thu, 19 Apr 2012 09:57:50 +0000 (11:57 +0200)
committerSimon A. Eugster <simon.eu@gmail.com>
Thu, 19 Apr 2012 09:57:50 +0000 (11:57 +0200)
1  2 
src/CMakeLists.txt
src/clipitem.h
src/customtrackview.cpp
src/customtrackview.h
src/kdenlivesettings.kcfg
src/mainwindow.cpp
src/mainwindow.h
src/renderer.cpp

diff --combined src/CMakeLists.txt
index fdbd92b4e020295f6e862702bf435c30424466ac,556dc715e6a02aa05ab776e75f75f713f51ec112..8585bcb3d6e0c5cc69238bb185593a9f4be23e27
@@@ -76,11 -76,11 +76,11 @@@ macro_log_feature(QJSON_FOUN
  add_subdirectory(beziercurve)
  add_subdirectory(colorcorrection)
  add_subdirectory(commands)
+ add_subdirectory(lib)
  add_subdirectory(projecttree)
  add_subdirectory(utils)
  add_subdirectory(databackup)
  add_subdirectory(effectstack)
- add_subdirectory(kiss_fft)
  add_subdirectory(mimetypes)
  add_subdirectory(onmonitoritems)
  add_subdirectory(scopes)
@@@ -120,6 -120,7 +120,7 @@@ list(APPEND kdenlive_SRC
    customruler.cpp
    customtrackscene.cpp
    customtrackview.cpp
+   definitions.cpp
    docclipbase.cpp
    documentchecker.cpp
    documentvalidator.cpp
@@@ -197,6 -198,7 +198,6 @@@ kde4_add_ui_files(kdenlive_UI
    widgets/clipproperties_ui.ui
    widgets/cliptranscode_ui.ui
    widgets/collapsiblewidget_ui.ui
 -  widgets/collapsiblegroup_ui.ui
    widgets/clipstabilize_ui.ui
    widgets/colorclip_ui.ui
    widgets/colorplaneexport_ui.ui
diff --combined src/clipitem.h
index 1bd69100cb79dec917432e9926d24b7a2fbbad10,7a075c40fd6e9010e04508ffd61f7e937819b25d..088f49bc217fa35ba6dcb492dbebc41e0f837de3
@@@ -39,7 -39,7 +39,7 @@@ class Transition
  namespace Mlt
  {
  class Producer;
- };
+ }
  
  class ClipItem : public AbstractClipItem
  {
@@@ -100,8 -100,6 +100,8 @@@ public
      * @param ix The effect's index in effectlist
      * @param effect The new effect */
      void updateEffect(QDomElement effect);
 +    /** @brief Enable / disable a list of effect from their indexes. */
 +    void enableEffects(QList <int> indexes, bool disable);
      bool moveEffect(QDomElement effect, int ix);
      void flashClip();
      void addTransition(Transition*);
diff --combined src/customtrackview.cpp
index f450fc1598dae81fc166c1aca9947a8a31519aad,37d1bcc88d09d165de918441c813e7a365f79d01..76e4bb1cecb3f927b12eab7e8fce2ebc91b5ab5b
@@@ -48,7 -48,6 +48,7 @@@
  #include "commands/insertspacecommand.h"
  #include "spacerdialog.h"
  #include "commands/addtrackcommand.h"
 +#include "commands/changeeffectstatecommand.h"
  #include "commands/movegroupcommand.h"
  #include "ui_addtrack_ui.h"
  #include "initeffects.h"
@@@ -63,6 -62,9 +63,9 @@@
  #include "commands/razorgroupcommand.h"
  #include "profilesdialog.h"
  
+ #include "lib/audio/audioEnvelope.h"
+ #include "lib/audio/audioCorrelation.h"
  #include <KDebug>
  #include <KLocale>
  #include <KUrl>
@@@ -84,6 -86,8 +87,8 @@@
  #include <QGraphicsDropShadowEffect>
  #endif
  
+ //#define DEBUG
  bool sortGuidesList(const Guide *g1 , const Guide *g2)
  {
      return (*g1).position() < (*g2).position();
  // const int duration = animate ? 1500 : 1;
  
  CustomTrackView::CustomTrackView(KdenliveDoc *doc, CustomTrackScene* projectscene, QWidget *parent) :
-         QGraphicsView(projectscene, parent),
-         m_tracksHeight(KdenliveSettings::trackheight()),
-         m_projectDuration(0),
-         m_cursorPos(0),
-         m_document(doc),
-         m_scene(projectscene),
-         m_cursorLine(NULL),
-         m_operationMode(NONE),
-         m_moveOpMode(NONE),
-         m_dragItem(NULL),
-         m_dragGuide(NULL),
-         m_visualTip(NULL),
-         m_animation(NULL),
-         m_clickPoint(),
-         m_autoScroll(KdenliveSettings::autoscroll()),
-         m_pasteEffectsAction(NULL),
-         m_ungroupAction(NULL),
-         m_scrollOffset(0),
-         m_clipDrag(false),
-         m_findIndex(0),
-         m_tool(SELECTTOOL),
-         m_copiedItems(),
-         m_menuPosition(),
-         m_blockRefresh(false),
-         m_selectionGroup(NULL),
-         m_selectedTrack(0),
-         m_controlModifier(false)
- {
-     if (doc)
+     QGraphicsView(projectscene, parent),
+     m_tracksHeight(KdenliveSettings::trackheight()),
+     m_projectDuration(0),
+     m_cursorPos(0),
+     m_document(doc),
+     m_scene(projectscene),
+     m_cursorLine(NULL),
+     m_operationMode(NONE),
+     m_moveOpMode(NONE),
+     m_dragItem(NULL),
+     m_dragGuide(NULL),
+     m_visualTip(NULL),
+     m_animation(NULL),
+     m_clickPoint(),
+     m_autoScroll(KdenliveSettings::autoscroll()),
+     m_pasteEffectsAction(NULL),
+     m_ungroupAction(NULL),
+     m_scrollOffset(0),
+     m_clipDrag(false),
+     m_findIndex(0),
+     m_tool(SELECTTOOL),
+     m_copiedItems(),
+     m_menuPosition(),
+     m_blockRefresh(false),
+     m_selectionGroup(NULL),
+     m_selectedTrack(0),
+     m_audioCorrelator(NULL),
+     m_audioAlignmentReference(NULL),
+     m_controlModifier(false)
+ {
+     if (doc) {
          m_commandStack = doc->commandStack();
-     else
+     } else {
          m_commandStack = NULL;
+     }
      m_ct = 0;
      setMouseTracking(true);
      setAcceptDrops(true);
@@@ -1291,7 -1298,7 +1299,7 @@@ void CustomTrackView::mouseDoubleClickE
              EditEffectCommand *command = new EditEffectCommand(this, m_document->tracksCount() - item->track(), item->startPos(), oldEffect, newEffect, item->selectedEffectIndex(), false, false);
              //EditKeyFrameCommand *command = new EditKeyFrameCommand(this, m_dragItem->track(), m_dragItem->startPos(), item->selectedEffectIndex(), previous, next, false);
              m_commandStack->push(command);
 -            updateEffect(m_document->tracksCount() - item->track(), item->startPos(), item->selectedEffect(), item->selectedEffectIndex());
 +            updateEffect(m_document->tracksCount() - item->track(), item->startPos(), item->selectedEffect());
              emit clipItemSelected(item, item->selectedEffectIndex());
          }
      } else if (m_dragItem && !m_dragItem->isItemLocked()) {
@@@ -1928,14 -1935,12 +1936,14 @@@ void CustomTrackView::slotDeleteEffect(
      setDocumentModified();
  }
  
 -void CustomTrackView::updateEffect(int track, GenTime pos, QDomElement insertedEffect, int ix, bool updateEffectStack)
 +void CustomTrackView::updateEffect(int track, GenTime pos, QDomElement insertedEffect, bool updateEffectStack)
  {
      if (insertedEffect.isNull()) {
 +      kDebug()<<"// Trying to add null effect";
          emit displayMessage(i18n("Problem editing effect"), ErrorMessage);
          return;
      }
 +    int ix = insertedEffect.attribute("kdenlive_ix").toInt();
      QDomElement effect = insertedEffect.cloneNode().toElement();
      //kDebug() << "// update effect ix: " << effect.attribute("kdenlive_ix")<<", GAIN: "<<EffectsList::parameter(effect, "gain");
      if (pos < GenTime()) {
              clip->initEffect(effect);
              effectParams = getEffectArgs(effect);
          }*/
 -        if (!m_document->renderer()->mltEditEffect(m_document->tracksCount() - track, pos, effectParams))
 +        if (!m_document->renderer()->mltEditEffect(m_document->tracksCount() - track, pos, effectParams)) {
              emit displayMessage(i18n("Problem editing effect"), ErrorMessage);
 +      }
          m_document->setTrackEffect(m_document->tracksCount() - track - 1, ix, effect);
          emit updateTrackEffectState(track);
          setDocumentModified();
            if (ix == clip->selectedEffectIndex()) {
                // make sure to update display of clip keyframes
                clip->setSelectedEffect(ix);
 -          } else emit displayMessage(i18n("Problem editing effect"), ErrorMessage);
 +          }
              return;
          }
  
      setDocumentModified();
  }
  
 +void CustomTrackView::updateEffectState(int track, GenTime pos, QList <int> effectIndexes, bool disable, bool updateEffectStack)
 +{
 +    if (pos < GenTime()) {
 +        // editing a track effect
 +        if (!m_document->renderer()->mltEnableEffects(m_document->tracksCount() - track, pos, effectIndexes, disable)) {
 +            emit displayMessage(i18n("Problem editing effect"), ErrorMessage);
 +          return;
 +      }
 +        m_document->enableTrackEffects(m_document->tracksCount() - track - 1, effectIndexes, disable);
 +        emit updateTrackEffectState(track);
 +        setDocumentModified();
 +        return;
 +    }
 +    // editing a clip effect
 +    ClipItem *clip = getClipItemAt((int)pos.frames(m_document->fps()), m_document->tracksCount() - track);
 +    if (clip) {
 +      bool success = m_document->renderer()->mltEnableEffects(m_document->tracksCount() - clip->track(), clip->startPos(), effectIndexes, disable);
 +      if (success) {
 +          clip->enableEffects(effectIndexes, disable);
 +          if (updateEffectStack && clip->isSelected()) {
 +              emit clipItemSelected(clip);
 +          }
 +          if (effectIndexes.contains(clip->selectedEffectIndex())) {
 +              // make sure to update display of clip keyframes
 +              clip->setSelectedEffect(clip->selectedEffectIndex());
 +          }
 +      }
 +      else emit displayMessage(i18n("Problem editing effect"), ErrorMessage);
 +    }
 +    else emit displayMessage(i18n("Cannot find clip to update effect"), ErrorMessage);
 +}
 +
  void CustomTrackView::moveEffect(int track, GenTime pos, QList <int> oldPos, QList <int> newPos)
  {
      if (pos < GenTime()) {
      } else emit displayMessage(i18n("Cannot move effect"), ErrorMessage);
  }
  
 -void CustomTrackView::slotChangeEffectState(ClipItem *clip, int track, int effectPos, bool disable)
 +void CustomTrackView::slotChangeEffectState(ClipItem *clip, int track, QList <int> effectIndexes, bool disable)
  {
 -    EditEffectCommand *command;
 -    QDomElement effect;
 -    if (clip == NULL) effect = m_document->getTrackEffect(track - 1, effectPos);
 -    else effect = clip->effectAt(effectPos);
 -    QDomElement oldEffect = effect.cloneNode().toElement();
 -    effect.setAttribute("disable", (int) disable);
 -
 -
 +    ChangeEffectStateCommand *command;
      if (clip == NULL) {
          // editing track effect
 -        command = new EditEffectCommand(this, m_document->tracksCount() - track, GenTime(-1), oldEffect, effect, effectPos, false, true);
 +        command = new ChangeEffectStateCommand(this, m_document->tracksCount() - track, GenTime(-1), effectIndexes, disable, false, true);
      } else {
 -        command = new EditEffectCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), oldEffect, effect, effectPos, false, true);
 +      // Check if we have a speed effect, disabling / enabling it needs a special procedure since it is a pseudoo effect
 +      QList <int> speedEffectIndexes;
 +      for (int i = 0; i < effectIndexes.count(); i++) {
 +          QDomElement effect = clip->effectAt(effectIndexes.at(i));
 +          if (effect.attribute("id") == "speed") {
 +              // speed effect
 +              speedEffectIndexes << effectIndexes.at(i);
 +              QDomElement newEffect = effect.cloneNode().toElement();
 +              newEffect.setAttribute("disable", (int) disable);
 +              EditEffectCommand *editcommand = new EditEffectCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), effect, newEffect, effectIndexes.at(i), false, true);
 +              m_commandStack->push(editcommand);
 +          }
 +      }
 +      for (int j = 0; j < speedEffectIndexes.count(); j++) {
 +          effectIndexes.removeAll(speedEffectIndexes.at(j));
 +      }
 +        command = new ChangeEffectStateCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), effectIndexes, disable, false, true);
      }
      m_commandStack->push(command);
 -    setDocumentModified();;
 +    setDocumentModified();
  }
  
  void CustomTrackView::slotChangeEffectPosition(ClipItem *clip, int track, QList <int> currentPos, int newPos)
@@@ -2590,6 -2553,10 +2598,10 @@@ void CustomTrackView::dropEvent(QDropEv
          }
          event->setDropAction(Qt::MoveAction);
          event->accept();
+         /// \todo enable when really working
+ //        alignAudio();
      } else QGraphicsView::dropEvent(event);
      setFocus();
  }
@@@ -3872,7 -3839,7 +3884,7 @@@ void CustomTrackView::mouseReleaseEvent
          EditEffectCommand *command = new EditEffectCommand(this, m_document->tracksCount() - item->track(), item->startPos(), oldEffect, newEffect, item->selectedEffectIndex(), false, false);
  
          m_commandStack->push(command);
 -        updateEffect(m_document->tracksCount() - item->track(), item->startPos(), item->selectedEffect(), item->selectedEffectIndex());
 +        updateEffect(m_document->tracksCount() - item->track(), item->startPos(), item->selectedEffect());
          emit clipItemSelected(item);
      }
      if (m_dragItem && m_dragItem->type() == TRANSITIONWIDGET && m_dragItem->isSelected()) {
@@@ -4416,18 -4383,26 +4428,26 @@@ Transition *CustomTrackView::getTransit
      return clip;
  }
  
void CustomTrackView::moveClip(const ItemInfo &start, const ItemInfo &end, bool refresh)
bool CustomTrackView::moveClip(const ItemInfo &start, const ItemInfo &end, bool refresh, ItemInfo *out_actualEnd)
  {
      if (m_selectionGroup) resetSelectionGroup(false);
      ClipItem *item = getClipItemAt((int) start.startPos.frames(m_document->fps()), start.track);
      if (!item) {
          emit displayMessage(i18n("Cannot move clip at time: %1 on track %2", m_document->timecode().getTimecodeFromFrames(start.startPos.frames(m_document->fps())), start.track), ErrorMessage);
          kDebug() << "----------------  ERROR, CANNOT find clip to move at.. ";
-         return;
+         return false;
      }
      Mlt::Producer *prod = item->getProducer(end.track);
  
-     bool success = m_document->renderer()->mltMoveClip((int)(m_document->tracksCount() - start.track), (int)(m_document->tracksCount() - end.track), (int) start.startPos.frames(m_document->fps()), (int)end.startPos.frames(m_document->fps()), prod);
+ #ifdef DEBUG
+     qDebug() << "Moving item " << (long)item << " from .. to:";
+     qDebug() << item->info();
+     qDebug() << start;
+     qDebug() << end;
+ #endif
+     bool success = m_document->renderer()->mltMoveClip((int)(m_document->tracksCount() - start.track), (int)(m_document->tracksCount() - end.track),
+                                                        (int) start.startPos.frames(m_document->fps()), (int)end.startPos.frames(m_document->fps()),
+                                                        prod);
      if (success) {
          bool snap = KdenliveSettings::snaptopoints();
          KdenliveSettings::setSnaptopoints(false);
          emit displayMessage(i18n("Cannot move clip to position %1", m_document->timecode().getTimecodeFromFrames(end.startPos.frames(m_document->fps()))), ErrorMessage);
      }
      if (refresh) m_document->renderer()->doRefresh();
+     if (out_actualEnd != NULL) {
+         *out_actualEnd = item->info();
+ #ifdef DEBUG
+         qDebug() << "Actual end position updated:" << *out_actualEnd;
+ #endif
+     }
+ #ifdef DEBUG
+     qDebug() << item->info();
+ #endif
+     return success;
  }
  
  void CustomTrackView::moveGroup(QList <ItemInfo> startClip, QList <ItemInfo> startTransition, const GenTime &offset, const int trackOffset, bool reverseMove)
@@@ -6078,6 -6063,141 +6108,141 @@@ void CustomTrackView::splitAudio(
      }
  }
  
+ void CustomTrackView::setAudioAlignReference()
+ {
+     QList<QGraphicsItem *> selection = scene()->selectedItems();
+     if (selection.isEmpty() || selection.size() > 1) {
+         emit displayMessage(i18n("You must select exactly one clip for the audio reference."), ErrorMessage);
+         return;
+     }
+     if (m_audioCorrelator != NULL) {
+         delete m_audioCorrelator;
+     }
+     if (selection.at(0)->type() == AVWIDGET) {
+         ClipItem *clip = static_cast<ClipItem*>(selection.at(0));
+         if (clip->clipType() == AV || clip->clipType() == AUDIO) {
+             m_audioAlignmentReference = clip;
+             AudioEnvelope *envelope = new AudioEnvelope(clip->getProducer(clip->track()));
+             m_audioCorrelator = new AudioCorrelation(envelope);
+             envelope->drawEnvelope().save("kdenlive-audio-reference-envelope.png");
+             envelope->dumpInfo();
+             emit displayMessage(i18n("Audio align reference set."), InformationMessage);
+         }
+         return;
+     }
+     emit displayMessage(i18n("Reference for audio alignment must contain audio data."), ErrorMessage);
+ }
+ void CustomTrackView::alignAudio()
+ {
+     bool referenceOK = true;
+     if (m_audioCorrelator == NULL) {
+         referenceOK = false;
+     }
+     if (referenceOK) {
+         if (!scene()->items().contains(m_audioAlignmentReference)) {
+             // The reference item has been deleted from the timeline (or so)
+             referenceOK = false;
+         }
+     }
+     if (!referenceOK) {
+         emit displayMessage(i18n("Audio alignment reference not yet set."), InformationMessage);
+         return;
+     }
+     int counter = 0;
+     QList<QGraphicsItem *> selection = scene()->selectedItems();
+     foreach (QGraphicsItem *item, selection) {
+         if (item->type() == AVWIDGET) {
+             ClipItem *clip = static_cast<ClipItem*>(item);
+             if (clip == m_audioAlignmentReference) {
+                 continue;
+             }
+             if (clip->clipType() == AV || clip->clipType() == AUDIO) {
+                 AudioEnvelope *envelope = new AudioEnvelope(clip->getProducer(clip->track()),
+                                                             clip->info().cropStart.frames(m_document->fps()),
+                                                             clip->info().cropDuration.frames(m_document->fps()));
+                 // FFT only for larger vectors. We could use it all time, but for small vectors
+                 // the (anyway not noticeable) overhead is smaller with a nested for loop correlation.
+                 int index = m_audioCorrelator->addChild(envelope, envelope->envelopeSize() > 200);
+                 int shift = m_audioCorrelator->getShift(index);
+                 counter++;
+                 m_audioCorrelator->info(index)->toImage().save("kdenlive-audio-align-cross-correlation.png");
+                 envelope->drawEnvelope().save("kdenlive-audio-align-envelope.png");
+                 envelope->dumpInfo();
+ #ifdef DEBUG
+                 int targetPos = m_audioAlignmentReference->startPos().frames(m_document->fps()) + shift;
+                 qDebug() << "Reference starts at " << m_audioAlignmentReference->startPos().frames(m_document->fps());
+                 qDebug() << "We will start at " << targetPos;
+                 qDebug() << "to shift by " << shift;
+                 qDebug() << "(eventually)";
+                 qDebug() << "(maybe)";
+ #endif
+                 QUndoCommand *moveCommand = new QUndoCommand();
+                 GenTime add(shift, m_document->fps());
+                 ItemInfo start = clip->info();
+                 ItemInfo end = start;
+                 end.startPos = m_audioAlignmentReference->startPos() + add - m_audioAlignmentReference->cropStart();
+                 end.endPos = end.startPos + start.cropDuration;
+                 if ( end.startPos.seconds() < 0 ) {
+                     // Clip would start before 0, so crop it first
+                     GenTime cropBy = -end.startPos;
+ #ifdef DEBUG
+                     qDebug() << "Need to crop clip. " << start;
+                     qDebug() << "end.startPos: " << end.startPos.toString() << ", cropBy: " << cropBy.toString();
+ #endif
+                     ItemInfo resized = start;
+                     resized.startPos += cropBy;
+                     resizeClip(start, resized);
+                     new ResizeClipCommand(this, start, resized, false, false, moveCommand);
+                     start = clip->info();
+                     end.startPos += cropBy;
+ #ifdef DEBUG
+                     qDebug() << "Clip cropped. " << start;
+                     qDebug() << "Moving to: " << end;
+ #endif
+                 }
+                 if (itemCollision(clip, end)) {
+                     delete moveCommand;
+                     emit displayMessage(i18n("Unable to move clip due to collision."), ErrorMessage);
+                     return;
+                 }
+                 moveCommand->setText(i18n("Auto-align clip"));
+                 new MoveClipCommand(this, start, end, true, moveCommand);
+                 updateTrackDuration(clip->track(), moveCommand);
+                 m_commandStack->push(moveCommand);
+             }
+         }
+     }
+     if (counter == 0) {
+         emit displayMessage(i18n("No audio clips selected."), ErrorMessage);
+     } else {
+         emit displayMessage(i18n("Auto-aligned %1 clips.").arg(counter), InformationMessage);
+     }
+ }
  void CustomTrackView::doSplitAudio(const GenTime &pos, int track, EffectsList effects, bool split)
  {
      ClipItem *clip = getClipItemAt(pos, track);
                  deleteClip(clp->info());
                  clip->setVideoOnly(false);
                  Mlt::Tractor *tractor = m_document->renderer()->lockService();
-                 if (!m_document->renderer()->mltUpdateClipProducer(tractor, m_document->tracksCount() - info.track, info.startPos.frames(m_document->fps()), clip->baseClip()->getProducer(info.track))) {
-                     emit displayMessage(i18n("Cannot update clip (time: %1, track: %2)", info.startPos.frames(m_document->fps()), info.track), ErrorMessage);
+                 if (!m_document->renderer()->mltUpdateClipProducer(
+                             tractor,
+                             m_document->tracksCount() - info.track,
+                             info.startPos.frames(m_document->fps()),
+                             clip->baseClip()->getProducer(info.track))) {
+                     emit displayMessage(i18n("Cannot update clip (time: %1, track: %2)",
+                                              info.startPos.frames(m_document->fps()), info.track),
+                                         ErrorMessage);
                  }
                  m_document->renderer()->unlockService(tractor);
  
diff --combined src/customtrackview.h
index d3f04980a8c24f292d8876410f3aa07863230c98,3590495a03050ad52c6f93c99bd81fc3bd80e437..0df31b42816866a99f5f32ba566e4af022712d99
@@@ -42,6 -42,7 +42,7 @@@ class ClipItem
  class AbstractClipItem;
  class AbstractGroupItem;
  class Transition;
+ class AudioCorrelation;
  
  class CustomTrackView : public QGraphicsView
  {
@@@ -60,7 -61,13 +61,13 @@@ public
      void configTracks(QList <TrackInfo> trackInfos);
      int cursorPos();
      void checkAutoScroll();
-     void moveClip(const ItemInfo &start, const ItemInfo &end, bool refresh);
+     /**
+       Move the clip at \c start to \c end.
+       If \c out_actualEnd is not NULL, it will be set to the position the clip really ended up at.
+       For example, attempting to move a clip to t = -1 s will actually move it to t = 0 s.
+       */
+     bool moveClip(const ItemInfo &start, const ItemInfo &end, bool refresh, ItemInfo *out_actualEnd = NULL);
      void moveGroup(QList <ItemInfo> startClip, QList <ItemInfo> startTransition, const GenTime &offset, const int trackOffset, bool reverseMove = false);
      /** move transition, startPos = (old start, old end), endPos = (new start, new end) */
      void moveTransition(const ItemInfo &start, const ItemInfo &end, bool refresh);
@@@ -76,9 -83,7 +83,9 @@@
      void slotAddGroupEffect(QDomElement effect, AbstractGroupItem *group);
      void addEffect(int track, GenTime pos, QDomElement effect);
      void deleteEffect(int track, GenTime pos, QDomElement effect);
 -    void updateEffect(int track, GenTime pos, QDomElement insertedEffect, int ix, bool refreshEffectStack = false);
 +    void updateEffect(int track, GenTime pos, QDomElement insertedEffect, bool refreshEffectStack = false);
 +    /** @brief Enable / disable a list of effects */
 +    void updateEffectState(int track, GenTime pos, QList <int> effectIndexes, bool disable, bool updateEffectStack);
      void moveEffect(int track, GenTime pos, QList <int> oldPos, QList <int> newPos);
      void addTransition(ItemInfo transitionInfo, int endTrack, QDomElement params, bool refresh);
      void deleteTransition(ItemInfo transitionInfo, int endTrack, QDomElement params, bool refresh);
      /** @brief Creates SplitAudioCommands for selected clips. */
      void splitAudio();
  
+     /// Define which clip to take as reference for automatic audio alignment
+     void setAudioAlignReference();
+     /// Automatically align the currently selected clips to synchronize their audio with the reference's audio
+     void alignAudio();
      /** @brief Seperates the audio of a clip to a audio track.
      * @param pos Position of the clip to split
      * @param track Track of the clip
@@@ -194,7 -205,7 +207,7 @@@ public slots
      void moveCursorPos(int delta);
      void updateCursorPos();
      void slotDeleteEffect(ClipItem *clip, int track, QDomElement effect, bool affectGroup = true);
 -    void slotChangeEffectState(ClipItem *clip, int track, int effectPos, bool disable);
 +    void slotChangeEffectState(ClipItem *clip, int track, QList <int> effectIndexes, bool disable);
      void slotChangeEffectPosition(ClipItem *clip, int track, QList <int> currentPos, int newPos);
      void slotUpdateClipEffect(ClipItem *clip, int track, QDomElement oldeffect, QDomElement effect, int ix, bool refreshEffectStack = true);
      void slotUpdateClipRegion(ClipItem *clip, int ix, QString region);
@@@ -361,6 -372,9 +374,9 @@@ private
      QWaitCondition m_producerNotReady;
      KStatefulBrush m_activeTrackBrush;
  
+     AudioCorrelation *m_audioCorrelator;
+     ClipItem *m_audioAlignmentReference;
      /** stores the state of the control modifier during mouse press.
       * Will then be used to identify whether we resize a group or only one item of it. */
      bool m_controlModifier;
index 92c47373faa353da4d276bb41e1c576fdba5dc41,2d6e67cdeea5ed69497ed96be75236311d70b7c7..ea681bce728d794524cf70c59f205a5e7f977d94
        <default>true</default>
      </entry>
  
+     <entry name="enableaudioalign" type="Bool">
+       <label>Enable automatic audio alignment (experimental)</label>
+       <default>false</default>
+     </entry>
      <entry name="verticalzoom" type="Bool">
        <label>Vertical drag in timeline ruler zooms.</label>
        <default>false</default>
        <label>Show overlay info on monitor (in / out points, markers,...).</label>
        <default>true</default>
      </entry>
 +    
 +    <entry name="showOnMonitorScene" type="Bool">
 +      <label>Show on monitor adjustable effect parameter (geometry, ..).</label>
 +      <default>true</default>
 +    </entry>
  
      <entry name="autoaudiodrivername" type="String">
        <label>Audio driver selected automatically.</label>
diff --combined src/mainwindow.cpp
index 94b8b594eee63307d1212ef7708c3349449fa8ab,971009ebc1b69f76de0484ec9a4dfd0e0fa54e85..34ad719ff6c1c37930bda8b0b831c7600f7897bc
@@@ -140,8 -140,6 +140,8 @@@ EffectsList MainWindow::audioEffects
  EffectsList MainWindow::customEffects;
  EffectsList MainWindow::transitions;
  
 +QMap <QString,QImage> MainWindow::m_lumacache;
 +
  MainWindow::MainWindow(const QString &MltPath, const KUrl & Url, const QString & clipsToLoad, QWidget *parent) :
      KXmlGuiWindow(parent),
      m_activeDocument(NULL),
      // FIXME: the next call returns a newly allocated object, which leaks
      initEffects::parseEffectFiles();
      //initEffects::parseCustomEffectsFile();
 -
 +    
      m_monitorManager = new MonitorManager();
  
      m_shortcutRemoveFocus = new QShortcut(QKeySequence("Esc"), this);
      m_timelineContextClipMenu->addAction(actionCollection()->action("group_clip"));
      m_timelineContextClipMenu->addAction(actionCollection()->action("ungroup_clip"));
      m_timelineContextClipMenu->addAction(actionCollection()->action("split_audio"));
+     if (KdenliveSettings::enableaudioalign()) {
+         m_timelineContextClipMenu->addAction(actionCollection()->action("set_audio_align_ref"));
+         m_timelineContextClipMenu->addAction(actionCollection()->action("align_audio"));
+     }
      m_timelineContextClipMenu->addSeparator();
      m_timelineContextClipMenu->addAction(actionCollection()->action("cut_timeline_clip"));
      m_timelineContextClipMenu->addAction(actionCollection()->action(KStandardAction::name(KStandardAction::Copy)));
@@@ -1463,7 -1465,7 +1467,7 @@@ void MainWindow::setupActions(
      collection.addAction("edit_clip_marker", editClipMarker);
      connect(editClipMarker, SIGNAL(triggered(bool)), this, SLOT(slotEditClipMarker()));
  
-     KAction *addMarkerGuideQuickly = new KAction(KIcon("bookmark-new"), i18n("Add Marker/Guide quickly"), this);
+     KActionaddMarkerGuideQuickly = new KAction(KIcon("bookmark-new"), i18n("Add Marker/Guide quickly"), this);
      addMarkerGuideQuickly->setShortcut(Qt::Key_Asterisk);
      collection.addAction("add_marker_guide_quickly", addMarkerGuideQuickly);
      connect(addMarkerGuideQuickly, SIGNAL(triggered(bool)), this, SLOT(slotAddMarkerGuideQuickly()));
      collection.addAction("split_audio", splitAudio);
      connect(splitAudio, SIGNAL(triggered(bool)), this, SLOT(slotSplitAudio()));
  
+     KAction* setAudioAlignReference = new KAction(i18n("Set Audio Reference"), this);
+     collection.addAction("set_audio_align_ref", setAudioAlignReference);
+     connect(setAudioAlignReference, SIGNAL(triggered()), this, SLOT(slotSetAudioAlignReference()));
+     KAction* alignAudio = new KAction(i18n("Align Audio to Reference"), this);
+     collection.addAction("align_audio", alignAudio);
+     connect(alignAudio, SIGNAL(triggered()), this, SLOT(slotAlignAudio()));
      KAction* audioOnly = new KAction(KIcon("document-new"), i18n("Audio Only"), this);
      collection.addAction("clip_audio_only", audioOnly);
      audioOnly->setData("clip_audio_only");
@@@ -2487,7 -2497,7 +2499,7 @@@ void MainWindow::connectDocument(TrackV
              disconnect(m_effectStack, SIGNAL(updateEffect(ClipItem*, int, QDomElement, QDomElement, int,bool)), m_activeTimeline->projectView(), SLOT(slotUpdateClipEffect(ClipItem*, int, QDomElement, QDomElement, int,bool)));
              disconnect(m_effectStack, SIGNAL(removeEffect(ClipItem*, int, QDomElement)), m_activeTimeline->projectView(), SLOT(slotDeleteEffect(ClipItem*, int, QDomElement)));
            disconnect(m_effectStack, SIGNAL(addEffect(ClipItem*, QDomElement)), trackView->projectView(), SLOT(slotAddEffect(ClipItem*, QDomElement)));
 -            disconnect(m_effectStack, SIGNAL(changeEffectState(ClipItem*, int, int, bool)), m_activeTimeline->projectView(), SLOT(slotChangeEffectState(ClipItem*, int, int, bool)));
 +            disconnect(m_effectStack, SIGNAL(changeEffectState(ClipItem*, int, QList <int>, bool)), m_activeTimeline->projectView(), SLOT(slotChangeEffectState(ClipItem*, int, QList <int>, bool)));
              disconnect(m_effectStack, SIGNAL(changeEffectPosition(ClipItem*, int, QList<int>, int)), m_activeTimeline->projectView(), SLOT(slotChangeEffectPosition(ClipItem*, int, QList <int>, int)));
              disconnect(m_effectStack, SIGNAL(refreshEffectStack(ClipItem*)), m_activeTimeline->projectView(), SLOT(slotRefreshEffects(ClipItem*)));
              disconnect(m_effectStack, SIGNAL(reloadEffects()), this, SLOT(slotReloadEffects()));
      connect(m_effectStack, SIGNAL(updateClipRegion(ClipItem*, int, QString)), trackView->projectView(), SLOT(slotUpdateClipRegion(ClipItem*, int, QString)));
      connect(m_effectStack, SIGNAL(removeEffect(ClipItem*, int, QDomElement)), trackView->projectView(), SLOT(slotDeleteEffect(ClipItem*, int, QDomElement)));
      connect(m_effectStack, SIGNAL(addEffect(ClipItem*, QDomElement)), trackView->projectView(), SLOT(slotAddEffect(ClipItem*, QDomElement)));
 -    connect(m_effectStack, SIGNAL(changeEffectState(ClipItem*, int, int, bool)), trackView->projectView(), SLOT(slotChangeEffectState(ClipItem*, int, int, bool)));
 +    connect(m_effectStack, SIGNAL(changeEffectState(ClipItem*, int, QList <int>, bool)), trackView->projectView(), SLOT(slotChangeEffectState(ClipItem*, int, QList <int>, bool)));
      connect(m_effectStack, SIGNAL(changeEffectPosition(ClipItem*, int, QList <int>, int)), trackView->projectView(), SLOT(slotChangeEffectPosition(ClipItem*, int, QList <int>, int)));
      connect(m_effectStack, SIGNAL(refreshEffectStack(ClipItem*)), trackView->projectView(), SLOT(slotRefreshEffects(ClipItem*)));
      connect(m_effectStack, SIGNAL(seekTimeline(int)), trackView->projectView() , SLOT(setCursorPos(int)));
@@@ -3772,6 -3782,20 +3784,20 @@@ void MainWindow::slotSplitAudio(
          m_activeTimeline->projectView()->splitAudio();
  }
  
+ void MainWindow::slotSetAudioAlignReference()
+ {
+     if (m_activeTimeline) {
+         m_activeTimeline->projectView()->setAudioAlignReference();
+     }
+ }
+ void MainWindow::slotAlignAudio()
+ {
+     if (m_activeTimeline) {
+         m_activeTimeline->projectView()->alignAudio();
+     }
+ }
  void MainWindow::slotUpdateClipType(QAction *action)
  {
      if (m_activeTimeline) {
diff --combined src/mainwindow.h
index 22265d61e0970d93a37c00a5488b0e0808c1d2e7,91ddcc2636d0c6782548bb22b4f0d7405865c0fe..04861cd0471eecde29f2f9c2389f8f2e4d06c12a
@@@ -28,9 -28,6 +28,9 @@@
  #include <QEvent>
  #include <QTimer>
  #include <QShortcut>
 +#include <QMap>
 +#include <QString>
 +#include <QImage>
  
  #include <KXmlGuiWindow>
  #include <KTextEdit>
@@@ -41,7 -38,6 +41,7 @@@
  #include <KComboBox>
  #include <kautosavefile.h>
  #include <KActionCategory>
 +#include <KImageCache>
  
  #include "effectslist.h"
  #include "gentime.h"
@@@ -113,10 -109,6 +113,10 @@@ public
      static EffectsList audioEffects;
      static EffectsList customEffects;
      static EffectsList transitions;
 +    
 +    /** @brief Cache for luma files thumbnails. */
 +    static QMap <QString,QImage> m_lumacache;
 +
  protected:
  
      /** @brief Closes the window.
@@@ -493,6 -485,8 +493,8 @@@ private slots
      void slotClipInProjectTree();
      //void slotClipToProjectTree();
      void slotSplitAudio();
+     void slotSetAudioAlignReference();
+     void slotAlignAudio();
      void slotUpdateClipType(QAction *action);
      void slotShowTimeline(bool show);
      void slotMaximizeCurrent(bool show);
diff --combined src/renderer.cpp
index 2591146c27d61e30135d3f74d9a46b0fcf65e4d7,f5761878c2edbb1f046a486734a3f4dd7a2316e9..39f154b801c396b71fa0c530c6250d9a32c5c9d8
@@@ -1691,16 -1691,6 +1691,16 @@@ void Render::showFrame(Mlt::Frame& fram
      }
  }
  
 +void Render::disablePreview(bool disable)
 +{
 +    if (m_mltConsumer) {
 +      m_mltConsumer->stop();
 +      m_mltConsumer->set("preview_off", (int) disable);
 +      m_mltConsumer->set("refresh", 0);
 +      m_mltConsumer->start();
 +    }
 +}
 +
  void Render::showAudio(Mlt::Frame& frame)
  {
      if (!frame.is_valid() || frame.get_int("test_audio") != 0) {
@@@ -2778,6 -2768,7 +2778,6 @@@ bool Render::mltEditTrackEffect(int tra
  
      refresh();
      return true;
 -
  }
  
  bool Render::mltEditEffect(int track, GenTime position, EffectsParameterList params)
      return true;
  }
  
 +bool Render::mltEnableEffects(int track, GenTime position, QList <int> effectIndexes, bool disable)
 +{
 +    if (position < GenTime()) {
 +        return mltEnableTrackEffects(track, effectIndexes, disable);
 +    }
 +    // find filter
 +    Mlt::Service service(m_mltProducer->parent().get_service());
 +    Mlt::Tractor tractor(service);
 +    Mlt::Producer trackProducer(tractor.track(track));
 +    Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
 +
 +    int clipIndex = trackPlaylist.get_clip_index_at((int) position.frames(m_fps));
 +    Mlt::Producer *clip = trackPlaylist.get_clip(clipIndex);
 +    if (!clip) {
 +        kDebug() << "WARINIG, CANNOT FIND CLIP ON track: " << track << ", AT POS: " << position.frames(m_fps);
 +        return false;
 +    }
 +
 +    int duration = clip->get_playtime();
 +    bool doRefresh = true;
 +    // Check if clip is visible in monitor
 +    int diff = trackPlaylist.clip_start(clipIndex) + duration - m_mltProducer->position();
 +    if (diff < 0 || diff > duration)
 +        doRefresh = false;
 +    int ct = 0;
 +
 +    Mlt::Filter *filter = clip->filter(ct);
 +    while (filter) {
 +        if (effectIndexes.contains(filter->get_int("kdenlive_ix"))) {
 +            filter->set("disable", (int) disable);
 +        }
 +        ct++;
 +        filter = clip->filter(ct);
 +    }
 +
 +    delete clip;
 +    service.unlock();
 +
 +    if (doRefresh) refresh();
 +    return true;
 +}
 +
 +bool Render::mltEnableTrackEffects(int track, QList <int> effectIndexes, bool disable)
 +{
 +    Mlt::Service service(m_mltProducer->parent().get_service());
 +    Mlt::Tractor tractor(service);
 +    Mlt::Producer trackProducer(tractor.track(track));
 +    Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
 +    Mlt::Service clipService(trackPlaylist.get_service());
 +    int ct = 0;
 +
 +    Mlt::Filter *filter = clipService.filter(ct);
 +    while (filter) {
 +        if (effectIndexes.contains(filter->get_int("kdenlive_ix"))) {
 +            filter->set("disable", (int) disable);
 +        }
 +        ct++;
 +        filter = clipService.filter(ct);
 +    }
 +    service.unlock();
 +
 +    refresh();
 +    return true;
 +}
 +
  void Render::mltUpdateEffectPosition(int track, GenTime position, int oldPos, int newPos)
  {
      Mlt::Service service(m_mltProducer->parent().get_service());
@@@ -3441,37 -3367,53 +3441,53 @@@ bool Render::mltMoveClip(int startTrack
      bool checkLength = false;
      if (endTrack == startTrack) {
          Mlt::Producer *clipProducer = trackPlaylist.replace_with_blank(clipIndex);
-         if ((!overwrite && !trackPlaylist.is_blank_at(moveEnd)) || !clipProducer || !clipProducer->is_valid() || clipProducer->is_blank()) {
-             // error, destination is not empty
-             if (clipProducer) {
-                 if (!trackPlaylist.is_blank_at(moveEnd) && clipProducer->is_valid()) trackPlaylist.insert_at(moveStart, clipProducer, 1);
-                 delete clipProducer;
+         trackPlaylist.consolidate_blanks(0);
+         if (!overwrite) {
+             bool success = true;
+             if (!trackPlaylist.is_blank_at(moveEnd) || !clipProducer || !clipProducer->is_valid() || clipProducer->is_blank()) success = false;
+             else {
+                 // Check that the destination region is empty
+                 int destinationIndex = trackPlaylist.get_clip_index_at(moveEnd);
+                 if (destinationIndex < trackPlaylist.count() - 1) {
+                     // We are not at the end of the track
+                     int blankSize = trackPlaylist.blanks_from(destinationIndex, 0);
+                     // Make sure we have enough place to insert clip
+                     if (blankSize - clipProducer->get_length() - (moveEnd - trackPlaylist.clip_start(destinationIndex)) < 0) success = false;
+                 }
              }
-             //int ix = trackPlaylist.get_clip_index_at(moveEnd);
-             kDebug() << "// ERROR MOVING CLIP TO : " << moveEnd;
-             service.unlock();
-             return false;
-         } else {
-             trackPlaylist.consolidate_blanks(0);
-             if (overwrite) {
-                 trackPlaylist.remove_region(moveEnd, clipProducer->get_playtime());
-                 int clipIndex = trackPlaylist.get_clip_index_at(moveEnd);
-                 trackPlaylist.insert_blank(clipIndex, clipProducer->get_playtime() - 1);
+             if (!success) {
+                 if (clipProducer) {
+                     trackPlaylist.insert_at(moveStart, clipProducer, 1);
+                     delete clipProducer;
+                 }
+                 kDebug() << "// ERROR MOVING CLIP TO : " << moveEnd;
+                 service.unlock();
+                 return false;
              }
-             int newIndex = trackPlaylist.insert_at(moveEnd, clipProducer, 1);
-             trackPlaylist.consolidate_blanks(1);
+         }
+         
+         if (overwrite) {
+             trackPlaylist.remove_region(moveEnd, clipProducer->get_playtime());
+             int clipIndex = trackPlaylist.get_clip_index_at(moveEnd);
+             trackPlaylist.insert_blank(clipIndex, clipProducer->get_playtime() - 1);
+         }
+         int newIndex = trackPlaylist.insert_at(moveEnd, clipProducer, 1);
+         if (newIndex == -1) {
+             kDebug()<<"// CANNOT MOVE CLIP TO: "<<moveEnd;
+             trackPlaylist.insert_at(moveStart, clipProducer, 1);
              delete clipProducer;
-             /*if (QString(clipProducer.parent().get("transparency")).toInt() == 1) {
-             mltMoveTransparency(moveStart, moveEnd, startTrack, endTrack, QString(clipProducer.parent().get("id")).toInt());
-             }*/
-             if (newIndex + 1 == trackPlaylist.count()) checkLength = true;
+             service.unlock();
+             return false;
          }
-         //service.unlock();
+         trackPlaylist.consolidate_blanks(1);
+         delete clipProducer;
+         if (newIndex + 1 == trackPlaylist.count()) checkLength = true;
      } else {
          Mlt::Producer destTrackProducer(tractor.track(endTrack));
          Mlt::Playlist destTrackPlaylist((mlt_playlist) destTrackProducer.get_service());
          if (!overwrite && !destTrackPlaylist.is_blank_at(moveEnd)) {
              // error, destination is not empty
+             kDebug() << "Cannot move: Destination is not empty";
              service.unlock();
              return false;
          } else {