#include "commands/razorgroupcommand.h"
#include "profilesdialog.h"
+#include "lib/audio/audioEnvelope.h"
+#include "lib/audio/audioCorrelation.h"
+
#include <KDebug>
#include <KLocale>
#include <KUrl>
#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);
}
event->setDropAction(Qt::MoveAction);
event->accept();
+
+ /// \todo enable when really working
+// alignAudio();
+
} else QGraphicsView::dropEvent(event);
setFocus();
}
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)
}
}
+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);