-/***************************************************************************
- * Copyright (C) 2012 by Simon Andreas Eugster (simon.eu@gmail.com) *
- * This file is part of kdenlive. See www.kdenlive.org. *
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 2 of the License, or *
- * (at your option) any later version. *
- ***************************************************************************/
+/*
+Copyright (C) 2012 Simon A. Eugster (Granjow) <simon.eu@gmail.com>
+This file is part of kdenlive. See www.kdenlive.org.
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+*/
#include <QMap>
#include <QFile>
#include <QTime>
#include <QImage>
+#include <QDebug>
#include <QFileInfo>
#include <QDateTime>
+#include <QStringList>
+#include <QCoreApplication>
#include <mlt++/Mlt.h>
#include <iostream>
#include <cstdlib>
#include <cmath>
-#include "audioInfo.h"
-#include "audioStreamInfo.h"
-#include "audioEnvelope.h"
+#include "../src/lib/audio/audioInfo.h"
+#include "../src/lib/audio/audioStreamInfo.h"
+#include "../src/lib/audio/audioEnvelope.h"
+#include "../src/lib/audio/audioCorrelation.h"
+
+void printUsage(const char *path)
+{
+ std::cout << "This executable takes two audio/video files A and B and determines " << std::endl
+ << "how much B needs to be shifted in order to be synchronized with A." << std::endl << std::endl
+ << path << " <main audio file> <second audio file>" << std::endl
+ << "\t-h, --help\n\t\tDisplay this help" << std::endl
+ << "\t--fft\n\t\tUse Fourier Transform (FFT) to calculate the offset. This only takes" << std::endl
+ << "\t\tO(n log n) time compared to O(n²) when using normal correlation and should be " << std::endl
+ << "\t\tfaster for large data (several minutes)." << std::endl
+ << "\t--profile=<profile>\n\t\tUse the given profile for calculation (run: melt -query profiles)" << std::endl
+ << "\t--no-images\n\t\tDo not save envelope and correlation images" << std::endl
+ ;
+}
int main(int argc, char *argv[])
{
- char *fileMain;
- char *fileSub;
+ QCoreApplication app(argc, argv);
+ QStringList args = app.arguments();
+ args.removeAt(0);
+
+ std::string profile = "atsc_1080p_24";
+ bool saveImages = true;
+ bool useFFT = false;
+
+ // Load arguments
+ foreach (const QString &str, args) {
+
+ if (str.startsWith("--profile=")) {
+ QString s = str;
+ s.remove(0, QString("--profile=").length());
+ profile = s.toStdString();
+ args.removeOne(str);
+
+ } else if (str == "-h" || str == "--help") {
+ printUsage(argv[0]);
+ return 0;
+
+ } else if (str == "--no-images") {
+ saveImages = false;
+ args.removeOne(str);
+
+ } else if (str == "--fft") {
+ useFFT = true;
+ args.removeOne(str);
+ }
+
+ }
+
+ if (args.length() < 2) {
+ printUsage(argv[0]);
+ return 1;
+ }
+
+
+
+ std::string fileMain(args.at(0).toStdString());
+ args.removeFirst();
+ std::string fileSub = args.at(0).toStdString();
+ args.removeFirst();
+
+
+ qDebug() << "Unused arguments: " << args;
+
+
if (argc > 2) {
fileMain = argv[1];
fileSub = argv[2];
std::cout << "Usage: " << argv[0] << " <main audio file> <second audio file>" << std::endl;
return 0;
}
- std::cout << "Trying to align (1)\n\t" << fileSub << "\nto fit on (2)\n\t" << fileMain
- << "\n, result will indicate by how much (1) has to be moved." << std::endl;
+ std::cout << "Trying to align (2)\n\t" << fileSub << "\nto fit on (1)\n\t" << fileMain
+ << "\n, result will indicate by how much (2) has to be moved." << std::endl
+ << "Profile used: " << profile << std::endl
+ ;
+ if (useFFT) {
+ std::cout << "Will use FFT based correlation." << std::endl;
+ }
+
// Initialize MLT
Mlt::Factory::init(NULL);
// Load an arbitrary profile
- Mlt::Profile prof("hdv_1080_25p");
+ Mlt::Profile prof(profile.c_str());
// Load the MLT producers
- Mlt::Producer prodMain(prof, fileMain);
+ Mlt::Producer prodMain(prof, fileMain.c_str());
if (!prodMain.is_valid()) {
std::cout << fileMain << " is invalid." << std::endl;
return 2;
}
- Mlt::Producer prodSub(prof, fileSub);
+ Mlt::Producer prodSub(prof, fileSub.c_str());
if (!prodSub.is_valid()) {
std::cout << fileSub << " is invalid." << std::endl;
return 2;
}
- AudioEnvelope envelopeMain(&prodMain);
- envelopeMain.loadEnvelope();
- envelopeMain.loadStdDev();
- envelopeMain.dumpInfo();
- envelopeMain.normalizeEnvelope();
- envelopeMain.dumpInfo();
-
- AudioEnvelope envelopeSub(&prodSub);
- envelopeSub.loadEnvelope();
- envelopeMain.normalizeEnvelope();
- envelopeSub.dumpInfo();
-
-
- QString outImg = QString("envelope-%1.png")
- .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd-hh:mm:ss"));
- envelopeMain.drawEnvelope().save(outImg);
- std::cout << "Saved volume envelope as "
- << QFileInfo(outImg).absoluteFilePath().toStdString()
- << std::endl;
-
-
-
- const int sizeX = envelopeMain.envelopeSize();
- const int sizeY = envelopeSub.envelopeSize();
- int64_t correlation[sizeX + sizeY + 1];
- const int64_t *envX = envelopeMain.envelope();
- const int64_t *envY = envelopeSub.envelope();
- int64_t const* left;
- int64_t const* right;
- int size;
- int64_t sum;
- int64_t max = 0;
-
- QTime t;
- t.start();
- for (int shift = -sizeX; shift <= sizeY; shift++) {
-
- if (shift <= 0) {
- left = envX-shift;
- right = envY;
- size = std::min(sizeX+shift, sizeY);
- } else {
- left = envX;
- right = envY+shift;
- size = std::min(sizeX, sizeY-shift);
- }
-
- sum = 0;
- for (int i = 0; i < size; i++) {
- sum += (*left) * (*right);
- left++;
- right++;
- }
- correlation[sizeX+shift] = std::abs(sum);
- std::cout << sum << " ";
-
- if (sum > max) {
- max = sum;
- }
+ // Build the audio envelopes for the correlation
+ AudioEnvelope *envelopeMain = new AudioEnvelope(fileMain.c_str(), &prodMain);
+ envelopeMain->loadEnvelope();
+ envelopeMain->loadStdDev();
+ envelopeMain->dumpInfo();
+
+ AudioEnvelope *envelopeSub = new AudioEnvelope(fileSub.c_str(), &prodSub);
+ envelopeSub->loadEnvelope();
+ envelopeSub->loadStdDev();
+ envelopeSub->dumpInfo();
+
+ // Calculate the correlation and hereby the audio shift
+ AudioCorrelation corr(envelopeMain);
+ int index = corr.addChild(envelopeSub, useFFT);
+
+ int shift = corr.getShift(index);
+ std::cout << " Should be shifted by " << shift << " frames: " << fileSub << std::endl
+ << "\trelative to " << fileMain << std::endl
+ << "\tin a " << prodMain.get_fps() << " fps profile (" << profile << ")." << std::endl;
+
+
+ if (saveImages) {
+ QString outImg = QString::fromLatin1("envelope-main-%1.png")
+ .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd-hh:mm:ss"));
+ envelopeMain->drawEnvelope().save(outImg);
+ std::cout << "Saved volume envelope as "
+ << QFileInfo(outImg).absoluteFilePath().toStdString()
+ << std::endl;
+ outImg = QString::fromLatin1("envelope-sub-%1.png")
+ .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd-hh:mm:ss"));
+ envelopeSub->drawEnvelope().save(outImg);
+ std::cout << "Saved volume envelope as "
+ << QFileInfo(outImg).absoluteFilePath().toStdString()
+ << std::endl;
+ outImg = QString::fromLatin1("correlation-%1.png")
+ .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd-hh:mm:ss"));
+ corr.info(index)->toImage().save(outImg);
+ std::cout << "Saved correlation image as "
+ << QFileInfo(outImg).absoluteFilePath().toStdString()
+ << std::endl;
}
- std::cout << "Correlation calculated. Time taken: " << t.elapsed() << " ms." << std::endl;
-
- int val;
- QImage img(sizeX + sizeY + 1, 400, QImage::Format_ARGB32);
- img.fill(qRgb(255,255,255));
- for (int x = 0; x < sizeX+sizeY+1; x++) {
- val = correlation[x]/double(max)*img.height();
- for (int y = img.height()-1; y > img.height() - val - 1; y--) {
- img.setPixel(x, y, qRgb(50, 50, 50));
- }
- }
- outImg = QString("correlation-%1.png")
- .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd-hh:mm:ss"));
- img.save(outImg);
- std::cout << "Saved volume envelope as "
- << QFileInfo(outImg).absoluteFilePath().toStdString()
- << std::endl;
+
+ // Mlt::Factory::close();
return 0;