X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fcustomtrackview.cpp;h=37d1bcc88d09d165de918441c813e7a365f79d01;hb=b4fef878fdcf0cfb2fb384a59fb2d1888f1d5f4e;hp=61fb07b85694fa73b4b7b8c53b9edf1bb360708d;hpb=ab468d522e50520c500c676713142c523a97ce37;p=kdenlive diff --git a/src/customtrackview.cpp b/src/customtrackview.cpp index 61fb07b8..37d1bcc8 100644 --- a/src/customtrackview.cpp +++ b/src/customtrackview.cpp @@ -62,6 +62,9 @@ #include "commands/razorgroupcommand.h" #include "profilesdialog.h" +#include "lib/audio/audioEnvelope.h" +#include "lib/audio/audioCorrelation.h" + #include #include #include @@ -83,6 +86,8 @@ #include #endif +//#define DEBUG + bool sortGuidesList(const Guide *g1 , const Guide *g2) { return (*g1).position() < (*g2).position(); @@ -98,38 +103,41 @@ bool sortGuidesList(const Guide *g1 , const Guide *g2) // 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); @@ -1287,7 +1295,7 @@ void CustomTrackView::mouseDoubleClickEvent(QMouseEvent *event) //item->updateKeyframeEffect(); //QString next = item->keyframes(item->selectedEffectIndex()); QDomElement newEffect = item->selectedEffect().cloneNode().toElement(); - EditEffectCommand *command = new EditEffectCommand(this, m_document->tracksCount() - item->track(), item->startPos(), oldEffect, newEffect, item->selectedEffectIndex(), false); + 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()); @@ -1678,6 +1686,7 @@ void CustomTrackView::addEffect(int track, GenTime pos, QDomElement effect) EffectsParameterList params = clip->addEffect(effect); if (!m_document->renderer()->mltAddEffect(track, pos, params)) emit displayMessage(i18n("Problem adding effect to clip"), ErrorMessage); + clip->setSelectedEffect(params.paramValue("kdenlive_ix").toInt()); if (clip->isSelected()) emit clipItemSelected(clip); } else emit displayMessage(i18n("Cannot find clip to add effect"), ErrorMessage); } @@ -1727,32 +1736,32 @@ void CustomTrackView::slotAddGroupEffect(QDomElement effect, AbstractGroupItem * if (!namenode.isNull()) effectName = i18n(namenode.text().toUtf8().data()); else effectName = i18n("effect"); effectCommand->setText(i18n("Add %1", effectName)); - int count = 0; for (int i = 0; i < itemList.count(); i++) { if (itemList.at(i)->type() == AVWIDGET) { ClipItem *item = static_cast (itemList.at(i)); - if (effect.attribute("type") == "audio") { - // Don't add audio effects on video clips - if (item->isVideoOnly() || (item->clipType() != AUDIO && item->clipType() != AV && item->clipType() != PLAYLIST)) continue; - } else if (effect.hasAttribute("type") == false) { - // Don't add video effect on audio clips - if (item->isAudioOnly() || item->clipType() == AUDIO) continue; - } - - if (effect.attribute("unique", "0") != "0" && item->hasEffect(effect.attribute("tag"), effect.attribute("id")) != -1) { - emit displayMessage(i18n("Effect already present in clip"), ErrorMessage); - continue; - } - if (item->isItemLocked()) { - continue; - } - item->initEffect(effect); - - new AddEffectCommand(this, m_document->tracksCount() - item->track(), item->startPos(), effect, true, effectCommand); - count++; + if (effect.tagName() == "effectgroup") { + QDomNodeList effectlist = effect.elementsByTagName("effect"); + for (int j = 0; j < effectlist.count(); j++) { + QDomElement subeffect = effectlist.at(j).toElement(); + if (subeffect.hasAttribute("kdenlive_info")) { + // effect is in a group + EffectInfo effectInfo; + effectInfo.fromString(subeffect.attribute("kdenlive_info")); + if (effectInfo.groupIndex < 0) { + // group needs to be appended + effectInfo.groupIndex = item->nextFreeEffectGroupIndex(); + subeffect.setAttribute("kdenlive_info", effectInfo.toString()); + } + } + processEffect(item, subeffect, effectCommand); + } + } + else { + processEffect(item, effect, effectCommand); + } } } - if (count > 0) { + if (effectCommand->childCount() > 0) { m_commandStack->push(effectCommand); setDocumentModified(); } else delete effectCommand; @@ -1768,9 +1777,13 @@ void CustomTrackView::slotAddEffect(QDomElement effect, GenTime pos, int track) QList itemList; QUndoCommand *effectCommand = new QUndoCommand(); QString effectName; - QDomElement namenode = effect.firstChildElement("name"); - if (!namenode.isNull()) effectName = i18n(namenode.text().toUtf8().data()); - else effectName = i18n("effect"); + if (effect.tagName() == "effectgroup") { + effectName = effect.attribute("name"); + } else { + QDomElement namenode = effect.firstChildElement("name"); + if (!namenode.isNull()) effectName = i18n(namenode.text().toUtf8().data()); + else effectName = i18n("effect"); + } effectCommand->setText(i18n("Add %1", effectName)); if (track == -1) itemList = scene()->selectedItems(); @@ -1793,40 +1806,24 @@ void CustomTrackView::slotAddEffect(QDomElement effect, GenTime pos, int track) for (int i = 0; i < itemList.count(); i++) { if (itemList.at(i)->type() == AVWIDGET) { ClipItem *item = static_cast (itemList.at(i)); - if (effect.attribute("type") == "audio") { - // Don't add audio effects on video clips - if (item->isVideoOnly() || (item->clipType() != AUDIO && item->clipType() != AV && item->clipType() != PLAYLIST)) { - /* do not show error message when item is part of a group as the user probably knows what he does then - * and the message is annoying when working with the split audio feature */ - if (!item->parentItem() || item->parentItem() == m_selectionGroup) - emit displayMessage(i18n("Cannot add an audio effect to this clip"), ErrorMessage); - continue; - } - } else if (effect.attribute("type") == "video" || !effect.hasAttribute("type")) { - // Don't add video effect on audio clips - if (item->isAudioOnly() || item->clipType() == AUDIO) { - /* do not show error message when item is part of a group as the user probably knows what he does then - * and the message is annoying when working with the split audio feature */ - if (!item->parentItem() || item->parentItem() == m_selectionGroup) - emit displayMessage(i18n("Cannot add a video effect to this clip"), ErrorMessage); - continue; - } - } - if (item->hasEffect(effect.attribute("tag"), effect.attribute("id")) != -1 && effect.attribute("unique", "0") != "0") { - emit displayMessage(i18n("Effect already present in clip"), ErrorMessage); - continue; - } - if (item->isItemLocked()) { - continue; - } - - if (effect.attribute("id") == "freeze" && m_cursorPos > item->startPos().frames(m_document->fps()) && m_cursorPos < item->endPos().frames(m_document->fps())) { - item->initEffect(effect, m_cursorPos - item->startPos().frames(m_document->fps())); - } else { - kDebug()<<"PREIT 0: "<initEffect(effect); - } - new AddEffectCommand(this, m_document->tracksCount() - item->track(), item->startPos(), effect, true, effectCommand); + if (effect.tagName() == "effectgroup") { + QDomNodeList effectlist = effect.elementsByTagName("effect"); + for (int j = 0; j < effectlist.count(); j++) { + QDomElement subeffect = effectlist.at(j).toElement(); + if (subeffect.hasAttribute("kdenlive_info")) { + // effect is in a group + EffectInfo effectInfo; + effectInfo.fromString(subeffect.attribute("kdenlive_info")); + if (effectInfo.groupIndex < 0) { + // group needs to be appended + effectInfo.groupIndex = item->nextFreeEffectGroupIndex(); + subeffect.setAttribute("kdenlive_info", effectInfo.toString()); + } + } + processEffect(item, subeffect, effectCommand); + } + } + else processEffect(item, effect, effectCommand); } } if (effectCommand->childCount() > 0) { @@ -1837,11 +1834,11 @@ void CustomTrackView::slotAddEffect(QDomElement effect, GenTime pos, int track) for (int i = 0; i < itemList.count(); i++) { if (itemList.at(i)->type() == AVWIDGET) { ClipItem *clip = static_cast(itemList.at(i)); - clip->setSelectedEffect(clip->effectsCount() - 1); + clip->setSelectedEffect(clip->effectsCount()); if (!clip->isSelected()) { clearSelection(false); clip->setSelected(true); - emit clipItemSelected(clip, clip->selectedEffectIndex()); + emit clipItemSelected(clip); } break; } @@ -1850,6 +1847,43 @@ void CustomTrackView::slotAddEffect(QDomElement effect, GenTime pos, int track) } else delete effectCommand; } +void CustomTrackView::processEffect(ClipItem *item, QDomElement effect, QUndoCommand *effectCommand) +{ + if (effect.attribute("type") == "audio") { + // Don't add audio effects on video clips + if (item->isVideoOnly() || (item->clipType() != AUDIO && item->clipType() != AV && item->clipType() != PLAYLIST)) { + /* do not show error message when item is part of a group as the user probably knows what he does then + * and the message is annoying when working with the split audio feature */ + if (!item->parentItem() || item->parentItem() == m_selectionGroup) + emit displayMessage(i18n("Cannot add an audio effect to this clip"), ErrorMessage); + return; + } + } else if (effect.attribute("type") == "video" || !effect.hasAttribute("type")) { + // Don't add video effect on audio clips + if (item->isAudioOnly() || item->clipType() == AUDIO) { + /* do not show error message when item is part of a group as the user probably knows what he does then + * and the message is annoying when working with the split audio feature */ + if (!item->parentItem() || item->parentItem() == m_selectionGroup) + emit displayMessage(i18n("Cannot add a video effect to this clip"), ErrorMessage); + return; + } + } + if (effect.attribute("unique", "0") != "0" && item->hasEffect(effect.attribute("tag"), effect.attribute("id")) != -1) { + emit displayMessage(i18n("Effect already present in clip"), ErrorMessage); + return; + } + if (item->isItemLocked()) { + return; + } + + if (effect.attribute("id") == "freeze" && m_cursorPos > item->startPos().frames(m_document->fps()) && m_cursorPos < item->endPos().frames(m_document->fps())) { + item->initEffect(effect, m_cursorPos - item->startPos().frames(m_document->fps())); + } else { + item->initEffect(effect); + } + new AddEffectCommand(this, m_document->tracksCount() - item->track(), item->startPos(), effect, true, effectCommand); +} + void CustomTrackView::slotDeleteEffect(ClipItem *clip, int track, QDomElement effect, bool affectGroup) { if (clip == NULL) { @@ -1901,7 +1935,7 @@ void CustomTrackView::slotDeleteEffect(ClipItem *clip, int track, QDomElement ef setDocumentModified(); } -void CustomTrackView::updateEffect(int track, GenTime pos, QDomElement insertedEffect, int ix, bool triggeredByUser) +void CustomTrackView::updateEffect(int track, GenTime pos, QDomElement insertedEffect, int ix, bool updateEffectStack) { if (insertedEffect.isNull()) { emit displayMessage(i18n("Problem editing effect"), ErrorMessage); @@ -1938,12 +1972,12 @@ void CustomTrackView::updateEffect(int track, GenTime pos, QDomElement insertedE if (strobe == 0) strobe = 1; doChangeClipSpeed(clip->info(), clip->speedIndependantInfo(), speed, clip->speed(), strobe, clip->baseClip()->getId()); } - if (clip->updateEffect(effect)) { - if (ix == clip->selectedEffectIndex()) { - clip->setSelectedEffect(ix); - if (!triggeredByUser) - emit clipItemSelected(clip, ix); - } + clip->updateEffect(effect); + if (updateEffectStack && clip->isSelected()) + emit clipItemSelected(clip); + if (ix == clip->selectedEffectIndex()) { + // make sure to update display of clip keyframes + clip->setSelectedEffect(ix); } else emit displayMessage(i18n("Problem editing effect"), ErrorMessage); return; } @@ -1966,14 +2000,16 @@ void CustomTrackView::updateEffect(int track, GenTime pos, QDomElement insertedE clip->setFadeOut(pos); } } - bool success = true; - if (!m_document->renderer()->mltEditEffect(m_document->tracksCount() - clip->track(), clip->startPos(), effectParams)) success = false; + bool success = m_document->renderer()->mltEditEffect(m_document->tracksCount() - clip->track(), clip->startPos(), effectParams); - if (success && clip->updateEffect(effect)) { + if (success) { + clip->updateEffect(effect); + if (updateEffectStack && clip->isSelected()) { + emit clipItemSelected(clip); + } if (ix == clip->selectedEffectIndex()) { + // make sure to update display of clip keyframes clip->setSelectedEffect(ix); - if (!triggeredByUser) - emit clipItemSelected(clip, ix); } } else emit displayMessage(i18n("Problem editing effect"), ErrorMessage); @@ -1982,43 +2018,62 @@ void CustomTrackView::updateEffect(int track, GenTime pos, QDomElement insertedE setDocumentModified(); } -void CustomTrackView::moveEffect(int track, GenTime pos, int oldPos, int newPos) +void CustomTrackView::moveEffect(int track, GenTime pos, QList oldPos, QList newPos) { if (pos < GenTime()) { // Moving track effect - if (newPos > m_document->getTrackEffects(m_document->tracksCount() - track - 1).count()) { - newPos = m_document->getTrackEffects(m_document->tracksCount() - track - 1).count(); + int documentTrack = m_document->tracksCount() - track - 1; + int max = m_document->getTrackEffects(documentTrack).count(); + int new_position = newPos.at(0); + if (new_position > max) { + new_position = max; + } + int old_position = oldPos.at(0); + for (int i = 0; i < newPos.count(); i++) { + QDomElement act = m_document->getTrackEffect(documentTrack, new_position); + if (old_position > new_position) { + // Moving up, we need to adjust index + old_position = oldPos.at(i); + new_position = newPos.at(i); + } + QDomElement before = m_document->getTrackEffect(documentTrack, old_position); + if (!act.isNull() && !before.isNull()) { + m_document->setTrackEffect(documentTrack, new_position, before); + m_document->renderer()->mltMoveEffect(m_document->tracksCount() - track, pos, old_position, new_position); + } else emit displayMessage(i18n("Cannot move effect"), ErrorMessage); } - QDomElement act = m_document->getTrackEffect(m_document->tracksCount() - track - 1, newPos); - QDomElement before = m_document->getTrackEffect(m_document->tracksCount() - track - 1, oldPos); - if (!act.isNull() && !before.isNull()) { - //m_document->setTrackEffect(m_document->tracksCount() - track - 1, oldPos, act); - m_document->setTrackEffect(m_document->tracksCount() - track - 1, newPos, before); - m_document->renderer()->mltMoveEffect(m_document->tracksCount() - track, pos, oldPos, newPos); - emit showTrackEffects(m_document->tracksCount() - track, m_document->trackInfoAt(m_document->tracksCount() - track - 1)); - } else emit displayMessage(i18n("Cannot move effect"), ErrorMessage); + emit showTrackEffects(m_document->tracksCount() - track, m_document->trackInfoAt(documentTrack)); return; } ClipItem *clip = getClipItemAt((int)pos.frames(m_document->fps()), m_document->tracksCount() - track); - if (newPos > clip->effectsCount()) { - newPos = clip->effectsCount(); - } if (clip) { - QDomElement act = clip->effectAt(newPos); - QDomElement before = clip->effectAt(oldPos); - if (act.isNull() || before.isNull()) { - emit displayMessage(i18n("Cannot move effect"), ErrorMessage); - return; + int new_position = newPos.at(0); + if (new_position > clip->effectsCount()) { + new_position = clip->effectsCount(); } - //clip->moveEffect(act, oldPos); - clip->moveEffect(before, newPos); - // special case: speed effect, which is a pseudo-effect, not appearing in MLT's effects - if (act.attribute("id") == "speed") { - m_document->renderer()->mltUpdateEffectPosition(track, pos, oldPos, newPos); - } else if (before.attribute("id") == "speed") { - m_document->renderer()->mltUpdateEffectPosition(track, pos, newPos, oldPos); - } else m_document->renderer()->mltMoveEffect(track, pos, oldPos, newPos); - emit clipItemSelected(clip, newPos); + int old_position = oldPos.at(0); + for (int i = 0; i < newPos.count(); i++) { + QDomElement act = clip->effectAt(new_position); + if (old_position > new_position) { + // Moving up, we need to adjust index + old_position = oldPos.at(i); + new_position = newPos.at(i); + } + QDomElement before = clip->effectAt(old_position); + if (act.isNull() || before.isNull()) { + emit displayMessage(i18n("Cannot move effect"), ErrorMessage); + return; + } + clip->moveEffect(before, new_position); + // special case: speed effect, which is a pseudo-effect, not appearing in MLT's effects + if (act.attribute("id") == "speed") { + m_document->renderer()->mltUpdateEffectPosition(track, pos, old_position, new_position); + } else if (before.attribute("id") == "speed") { + m_document->renderer()->mltUpdateEffectPosition(track, pos, new_position, old_position); + } else m_document->renderer()->mltMoveEffect(track, pos, old_position, new_position); + } + clip->setSelectedEffect(newPos.at(0)); + emit clipItemSelected(clip); setDocumentModified(); } else emit displayMessage(i18n("Cannot move effect"), ErrorMessage); } @@ -2035,15 +2090,15 @@ void CustomTrackView::slotChangeEffectState(ClipItem *clip, int track, int effec if (clip == NULL) { // editing track effect - command = new EditEffectCommand(this, m_document->tracksCount() - track, GenTime(-1), oldEffect, effect, effectPos, true); + command = new EditEffectCommand(this, m_document->tracksCount() - track, GenTime(-1), oldEffect, effect, effectPos, false, true); } else { - command = new EditEffectCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), oldEffect, effect, effectPos, true); + command = new EditEffectCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), oldEffect, effect, effectPos, false, true); } m_commandStack->push(command); setDocumentModified();; } -void CustomTrackView::slotChangeEffectPosition(ClipItem *clip, int track, int currentPos, int newPos) +void CustomTrackView::slotChangeEffectPosition(ClipItem *clip, int track, QList currentPos, int newPos) { MoveEffectCommand *command; if (clip == NULL) { @@ -2054,11 +2109,11 @@ void CustomTrackView::slotChangeEffectPosition(ClipItem *clip, int track, int cu setDocumentModified(); } -void CustomTrackView::slotUpdateClipEffect(ClipItem *clip, int track, QDomElement oldeffect, QDomElement effect, int ix) +void CustomTrackView::slotUpdateClipEffect(ClipItem *clip, int track, QDomElement oldeffect, QDomElement effect, int ix, bool refreshEffectStack) { EditEffectCommand *command; - if (clip) command = new EditEffectCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), oldeffect, effect, ix, true); - else command = new EditEffectCommand(this, m_document->tracksCount() - track, GenTime(-1), oldeffect, effect, ix, true); + if (clip) command = new EditEffectCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), oldeffect, effect, ix, refreshEffectStack, true); + else command = new EditEffectCommand(this, m_document->tracksCount() - track, GenTime(-1), oldeffect, effect, ix, refreshEffectStack, true); m_commandStack->push(command); } @@ -2067,7 +2122,7 @@ void CustomTrackView::slotUpdateClipRegion(ClipItem *clip, int ix, QString regio QDomElement effect = clip->getEffectAt(ix); QDomElement oldeffect = effect.cloneNode().toElement(); effect.setAttribute("region", region); - EditEffectCommand *command = new EditEffectCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), oldeffect, effect, ix, true); + EditEffectCommand *command = new EditEffectCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), oldeffect, effect, ix, true, true); m_commandStack->push(command); } @@ -2494,10 +2549,14 @@ void CustomTrackView::dropEvent(QDropEvent * event) groupSelectedItems(true); } else if (items.count() == 1) { m_dragItem = static_cast (items.at(0)); - emit clipItemSelected((ClipItem*)m_dragItem, -1, false); + emit clipItemSelected((ClipItem*)m_dragItem, false); } event->setDropAction(Qt::MoveAction); event->accept(); + + /// \todo enable when really working +// alignAudio(); + } else QGraphicsView::dropEvent(event); setFocus(); } @@ -2661,13 +2720,14 @@ int CustomTrackView::duration() const void CustomTrackView::addTrack(TrackInfo type, int ix) { + QList transitionInfos; if (ix == -1 || ix == m_document->tracksCount()) { m_document->insertTrack(0, type); - m_document->renderer()->mltInsertTrack(1, type.type == VIDEOTRACK); + transitionInfos = m_document->renderer()->mltInsertTrack(1, type.type == VIDEOTRACK); } else { m_document->insertTrack(m_document->tracksCount() - ix, type); // insert track in MLT playlist - m_document->renderer()->mltInsertTrack(m_document->tracksCount() - ix, type.type == VIDEOTRACK); + transitionInfos = m_document->renderer()->mltInsertTrack(m_document->tracksCount() - ix, type.type == VIDEOTRACK); double startY = ix * m_tracksHeight + 1 + m_tracksHeight / 2; QRectF r(0, startY, sceneRect().width(), sceneRect().height() - startY); @@ -2709,14 +2769,24 @@ void CustomTrackView::addTrack(TrackInfo type, int ix) emit displayMessage(i18n("Cannot update clip (time: %1, track: %2)", clipinfo.startPos.frames(m_document->fps()), clipinfo.track), ErrorMessage); } } - } else if (item->type() == TRANSITIONWIDGET) { + } /*else if (item->type() == TRANSITIONWIDGET) { Transition *tr = static_cast (item); int track = tr->transitionEndTrack(); if (track >= ix) { tr->updateTransitionEndTrack(getPreviousVideoTrack(clipinfo.track)); } - } - } + }*/ + } + // Sync transition tracks with MLT playlist + Transition *tr; + TransitionInfo info; + for (int i = 0; i < transitionInfos.count(); i++) { + info = transitionInfos.at(i); + tr = getTransitionItem(info); + if (tr) tr->setForcedTrack(info.forceTrack, info.a_track); + else kDebug()<<"// Cannot update TRANSITION AT: "<fps()); + } + resetSelectionGroup(false); m_document->renderer()->unlockService(tractor); } @@ -3670,7 +3740,7 @@ void CustomTrackView::mouseReleaseEvent(QMouseEvent * event) EffectsList::setParameter(oldeffect, "in", QString::number(start)); EffectsList::setParameter(oldeffect, "out", QString::number(end)); slotUpdateClipEffect(item, -1, effect, oldeffect, ix); - emit clipItemSelected(item, ix); + emit clipItemSelected(item); } } else if (item->fadeIn() != 0 && ix2 == -1) { QDomElement effect; @@ -3693,7 +3763,7 @@ void CustomTrackView::mouseReleaseEvent(QMouseEvent * event) EffectsList::setParameter(oldeffect, "in", QString::number(start)); EffectsList::setParameter(oldeffect, "out", QString::number(end)); slotUpdateClipEffect(item, -1, effect, oldeffect, ix2); - emit clipItemSelected(item, ix2); + emit clipItemSelected(item); } } } else if (m_operationMode == FADEOUT) { @@ -3714,7 +3784,7 @@ void CustomTrackView::mouseReleaseEvent(QMouseEvent * event) EffectsList::setParameter(oldeffect, "out", QString::number(end)); // kDebug()<<"EDIT FADE OUT : "<fadeOut() != 0 && ix2 == -1) { QDomElement effect; @@ -3739,7 +3809,7 @@ void CustomTrackView::mouseReleaseEvent(QMouseEvent * event) EffectsList::setParameter(oldeffect, "out", QString::number(end)); // kDebug()<<"EDIT FADE OUT : "<updateKeyframeEffect(); //QString next = item->keyframes(item->selectedEffectIndex()); //EditKeyFrameCommand *command = new EditKeyFrameCommand(this, item->track(), item->startPos(), item->selectedEffectIndex(), previous, next, false); - EditEffectCommand *command = new EditEffectCommand(this, m_document->tracksCount() - item->track(), item->startPos(), oldEffect, newEffect, item->selectedEffectIndex(), false); + 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()); - emit clipItemSelected(item, item->selectedEffectIndex()); + emit clipItemSelected(item); } if (m_dragItem && m_dragItem->type() == TRANSITIONWIDGET && m_dragItem->isSelected()) { // A transition is selected @@ -4252,6 +4322,14 @@ ClipItem *CustomTrackView::getClipItemAt(GenTime pos, int track) return getClipItemAt((int) pos.frames(m_document->fps()), track); } + +Transition *CustomTrackView::getTransitionItem(TransitionInfo info) +{ + int pos = info.startPos.frames(m_document->fps()); + int track = m_document->tracksCount() - info.b_track; + return getTransitionItemAt(pos, track); +} + Transition *CustomTrackView::getTransitionItemAt(int pos, int track) { const QPointF p(pos, track * m_tracksHeight + Transition::itemOffset() + 1); @@ -4305,18 +4383,26 @@ Transition *CustomTrackView::getTransitionItemAtStart(GenTime pos, int track) 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); @@ -4346,6 +4432,16 @@ void CustomTrackView::moveClip(const ItemInfo &start, const ItemInfo &end, bool 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 startClip, QList startTransition, const GenTime &offset, const int trackOffset, bool reverseMove) @@ -4782,7 +4878,7 @@ void CustomTrackView::updatePositionEffects(ClipItem* item, ItemInfo info, bool emit displayMessage(i18n("Problem editing effect"), ErrorMessage); // if fade effect is displayed, update the effect edit widget with new clip duration if (item->isSelected() && effectPos == item->selectedEffectIndex()) - emit clipItemSelected(item, effectPos); + emit clipItemSelected(item); } } effectPos = item->hasEffect("brightness", "fade_from_black"); @@ -4803,7 +4899,7 @@ void CustomTrackView::updatePositionEffects(ClipItem* item, ItemInfo info, bool emit displayMessage(i18n("Problem editing effect"), ErrorMessage); // if fade effect is displayed, update the effect edit widget with new clip duration if (item->isSelected() && effectPos == item->selectedEffectIndex()) - emit clipItemSelected(item, effectPos); + emit clipItemSelected(item); } } } @@ -4829,7 +4925,7 @@ void CustomTrackView::updatePositionEffects(ClipItem* item, ItemInfo info, bool emit displayMessage(i18n("Problem editing effect"), ErrorMessage); // if fade effect is displayed, update the effect edit widget with new clip duration if (item->isSelected() && effectPos == item->selectedEffectIndex()) - emit clipItemSelected(item, effectPos); + emit clipItemSelected(item); } } effectPos = item->hasEffect("brightness", "fade_to_black"); @@ -4850,7 +4946,7 @@ void CustomTrackView::updatePositionEffects(ClipItem* item, ItemInfo info, bool emit displayMessage(i18n("Problem editing effect"), ErrorMessage); // if fade effect is displayed, update the effect edit widget with new clip duration if (item->isSelected() && effectPos == item->selectedEffectIndex()) - emit clipItemSelected(item, effectPos); + emit clipItemSelected(item); } } } @@ -4865,7 +4961,7 @@ void CustomTrackView::updatePositionEffects(ClipItem* item, ItemInfo info, bool EffectsList::setParameter(eff, "frame", QString::number(freeze_pos)); if (standalone) { if (item->isSelected() && item->selectedEffect().attribute("id") == "freeze") { - emit clipItemSelected(item, item->selectedEffectIndex()); + emit clipItemSelected(item); } } } @@ -5549,7 +5645,7 @@ void CustomTrackView::pasteClipEffects() for (int i = 0; i < clips.count(); ++i) { if (clips.at(i)->type() == AVWIDGET) { ClipItem *item = static_cast < ClipItem *>(clips.at(i)); - for (int j = 0; j < clip->effectsCount(); j++) { + for (int j = 1; j <= clip->effectsCount(); j++) { QDomElement eff = clip->effectAt(j); if (eff.attribute("unique", "0") == "0" || item->hasEffect(eff.attribute("tag"), eff.attribute("id")) == -1) { adjustKeyfames(clip->cropStart(), item->cropStart(), item->cropDuration(), eff); @@ -5967,6 +6063,141 @@ void CustomTrackView::splitAudio() } } +void CustomTrackView::setAudioAlignReference() +{ + QList 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(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 selection = scene()->selectedItems(); + foreach (QGraphicsItem *item, selection) { + if (item->type() == AVWIDGET) { + + ClipItem *clip = static_cast(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); @@ -6050,8 +6281,15 @@ void CustomTrackView::doSplitAudio(const GenTime &pos, int track, EffectsList ef 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); @@ -6515,7 +6753,6 @@ void CustomTrackView::updatePalette() pen1.setColor(palette().text().color()); m_cursorLine->setPen(pen1); } - emit tracksChanged(); } void CustomTrackView::removeTipAnimation() @@ -6622,9 +6859,42 @@ bool CustomTrackView::hasAudio(int track) const void CustomTrackView::slotAddTrackEffect(const QDomElement &effect, int ix) { - AddEffectCommand *command = new AddEffectCommand(this, m_document->tracksCount() - ix, GenTime(-1), effect, true); - m_commandStack->push(command); - setDocumentModified(); + + QUndoCommand *effectCommand = new QUndoCommand(); + QString effectName; + if (effect.tagName() == "effectgroup") { + effectName = effect.attribute("name"); + } else { + QDomElement namenode = effect.firstChildElement("name"); + if (!namenode.isNull()) effectName = i18n(namenode.text().toUtf8().data()); + else effectName = i18n("effect"); + } + effectCommand->setText(i18n("Add %1", effectName)); + if (effect.tagName() == "effectgroup") { + QDomNodeList effectlist = effect.elementsByTagName("effect"); + for (int j = 0; j < effectlist.count(); j++) { + QDomElement trackeffect = effectlist.at(j).toElement(); + if (trackeffect.attribute("unique", "0") != "0" && m_document->hasTrackEffect(m_document->tracksCount() - ix - 1, trackeffect.attribute("tag"), trackeffect.attribute("id")) != -1) { + emit displayMessage(i18n("Effect already present in track"), ErrorMessage); + continue; + } + new AddEffectCommand(this, m_document->tracksCount() - ix, GenTime(-1), trackeffect, true, effectCommand); + } + } + else { + if (effect.attribute("unique", "0") != "0" && m_document->hasTrackEffect(m_document->tracksCount() - ix - 1, effect.attribute("tag"), effect.attribute("id")) != -1) { + emit displayMessage(i18n("Effect already present in track"), ErrorMessage); + delete effectCommand; + return; + } + new AddEffectCommand(this, m_document->tracksCount() - ix, GenTime(-1), effect, true, effectCommand); + } + + if (effectCommand->childCount() > 0) { + m_commandStack->push(effectCommand); + setDocumentModified(); + } + else delete effectCommand; } @@ -6633,7 +6903,7 @@ EffectsParameterList CustomTrackView::getEffectArgs(const QDomElement &effect) EffectsParameterList parameters; QLocale locale; parameters.addParam("tag", effect.attribute("tag")); - if (effect.hasAttribute("region")) parameters.addParam("region", effect.attribute("region")); + //if (effect.hasAttribute("region")) parameters.addParam("region", effect.attribute("region")); parameters.addParam("kdenlive_ix", effect.attribute("kdenlive_ix")); parameters.addParam("kdenlive_info", effect.attribute("kdenlive_info")); parameters.addParam("id", effect.attribute("id")); @@ -6641,16 +6911,37 @@ EffectsParameterList CustomTrackView::getEffectArgs(const QDomElement &effect) if (effect.hasAttribute("disable")) parameters.addParam("disable", effect.attribute("disable")); if (effect.hasAttribute("in")) parameters.addParam("in", effect.attribute("in")); if (effect.hasAttribute("out")) parameters.addParam("out", effect.attribute("out")); + if (effect.attribute("id") == "region") { + QDomNodeList subeffects = effect.elementsByTagName("effect"); + for (int i = 0; i < subeffects.count(); i++) { + QDomElement subeffect = subeffects.at(i).toElement(); + int subeffectix = subeffect.attribute("region_ix").toInt(); + parameters.addParam(QString("filter%1").arg(subeffectix), subeffect.attribute("id")); + parameters.addParam(QString("filter%1.tag").arg(subeffectix), subeffect.attribute("tag")); + parameters.addParam(QString("filter%1.kdenlive_info").arg(subeffectix), subeffect.attribute("kdenlive_info")); + QDomNodeList subparams = subeffect.elementsByTagName("parameter"); + adjustEffectParameters(parameters, subparams, QString("filter%1.").arg(subeffectix)); + } + } QDomNodeList params = effect.elementsByTagName("parameter"); - for (int i = 0; i < params.count(); i++) { + adjustEffectParameters(parameters, params); + + return parameters; +} + + +void CustomTrackView::adjustEffectParameters(EffectsParameterList ¶meters, QDomNodeList params, const QString &prefix) +{ + QLocale locale; + for (int i = 0; i < params.count(); i++) { QDomElement e = params.item(i).toElement(); + QString paramname = prefix + e.attribute("name"); if (e.attribute("type") == "geometry" && !e.hasAttribute("fixed")) { // effects with geometry param need in / out synced with the clip, request it... parameters.addParam("_sync_in_out", "1"); } if (e.attribute("type") == "simplekeyframe") { - QStringList values = e.attribute("keyframes").split(";", QString::SkipEmptyParts); double factor = e.attribute("factor", "1").toDouble(); double offset = e.attribute("offset", "0").toDouble(); @@ -6660,7 +6951,7 @@ EffectsParameterList CustomTrackView::getEffectArgs(const QDomElement &effect) values[j] = pos + "=" + locale.toString(val); } // kDebug() << "/ / / /SENDING KEYFR:" << values; - parameters.addParam(e.attribute("name"), values.join(";")); + parameters.addParam(paramname, values.join(";")); /*parameters.addParam(e.attribute("name"), e.attribute("keyframes").replace(":", "=")); parameters.addParam("max", e.attribute("max")); parameters.addParam("min", e.attribute("min")); @@ -6696,15 +6987,15 @@ EffectsParameterList CustomTrackView::getEffectArgs(const QDomElement &effect) fact = e.attribute("factor", "1").toDouble(); } double offset = e.attribute("offset", "0").toDouble(); - parameters.addParam(e.attribute("name"), locale.toString((e.attribute("value").toDouble() - offset) / fact)); + parameters.addParam(paramname, locale.toString((e.attribute("value").toDouble() - offset) / fact)); } else { - parameters.addParam(e.attribute("name"), e.attribute("value")); + parameters.addParam(paramname, e.attribute("value")); } } } - return parameters; } + void CustomTrackView::updateTrackNames(int track, bool added) { QList tracks = m_document->tracksList(); @@ -6794,7 +7085,7 @@ void CustomTrackView::adjustEffects(ClipItem* item, ItemInfo oldInfo, QUndoComma if (effects.count()) { QMap::const_iterator i = effects.constBegin(); while (i != effects.constEnd()) { - new EditEffectCommand(this, m_document->tracksCount() - item->track(), item->startPos(), i.value(), item->effectAt(i.key()), i.key(), false, command); + new EditEffectCommand(this, m_document->tracksCount() - item->track(), item->startPos(), i.value(), item->effectAt(i.key()), i.key(), false, false, command); ++i; } } @@ -6818,9 +7109,9 @@ void CustomTrackView::slotGotFilterJobResults(const QString &/*id*/, int startPo kDebug()<<"// RESULT FILTER: "<tracksCount() - clip->track(), clip->startPos(), effect, newEffect, clip->selectedEffectIndex(), true); + EditEffectCommand *command = new EditEffectCommand(this, m_document->tracksCount() - clip->track(), clip->startPos(), effect, newEffect, clip->selectedEffectIndex(), true, true); m_commandStack->push(command); - emit clipItemSelected(clip, clip->selectedEffectIndex()); + emit clipItemSelected(clip); } }