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)
customruler.cpp
customtrackscene.cpp
customtrackview.cpp
+ definitions.cpp
docclipbase.cpp
documentchecker.cpp
documentvalidator.cpp
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
namespace Mlt
{
class Producer;
- };
+ }
class ClipItem : public AbstractClipItem
{
* @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*);
#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"
#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);
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()) {
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)
}
event->setDropAction(Qt::MoveAction);
event->accept();
+
+ /// \todo enable when really working
+ // alignAudio();
+
} else QGraphicsView::dropEvent(event);
setFocus();
}
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()) {
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);
class AbstractClipItem;
class AbstractGroupItem;
class Transition;
+ class AudioCorrelation;
class CustomTrackView : public QGraphicsView
{
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);
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
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);
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;
<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>
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)));
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);
+ KAction* addMarkerGuideQuickly = 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");
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)));
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) {
#include <QEvent>
#include <QTimer>
#include <QShortcut>
+#include <QMap>
+#include <QString>
+#include <QImage>
#include <KXmlGuiWindow>
#include <KTextEdit>
#include <KComboBox>
#include <kautosavefile.h>
#include <KActionCategory>
+#include <KImageCache>
#include "effectslist.h"
#include "gentime.h"
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.
void slotClipInProjectTree();
//void slotClipToProjectTree();
void slotSplitAudio();
+ void slotSetAudioAlignReference();
+ void slotAlignAudio();
void slotUpdateClipType(QAction *action);
void slotShowTimeline(bool show);
void slotMaximizeCurrent(bool show);
}
}
+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) {
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());
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 {