]> git.sesse.net Git - kdenlive/blob - testingArea/audioOffsetFFT.cpp
FFT based correlation works.
[kdenlive] / testingArea / audioOffsetFFT.cpp
1 /*
2 Copyright (C) 2012  Simon A. Eugster (Granjow)  <simon.eu@gmail.com>
3 This file is part of kdenlive. See www.kdenlive.org.
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9 */
10
11 #include "../src/lib/audio/audioEnvelope.h"
12 #include "../src/lib/audio/fftCorrelation.h"
13
14 #include <QCoreApplication>
15 #include <QStringList>
16 #include <QTime>
17 #include <QImage>
18 #include <QDebug>
19 #include <iostream>
20 #include <cmath>
21
22 void printUsage(const char *path)
23 {
24     std::cout << "This executable takes two audio/video files A and B and determines " << std::endl
25               << "how much B needs to be shifted in order to be synchronized with A." << std::endl
26               << "Other than audioOffset this executable will use Fast Fourier Tranform " << std::endl
27               << "which should be faster especially for large files." << std::endl << std::endl
28               << path << " <main audio file> <second audio file>" << std::endl
29               << "\t-h, --help\n\t\tDisplay this help" << std::endl
30               << "\t--profile=<profile>\n\t\tUse the given profile for calculation (run: melt -query profiles)" << std::endl
31               << "\t--no-images\n\t\tDo not save envelope and correlation images" << std::endl
32                  ;
33 }
34
35 int main(int argc, char *argv[])
36 {
37     QCoreApplication app(argc, argv);
38     QStringList args = app.arguments();
39     args.removeAt(0);
40
41     std::string profile = "atsc_1080p_24";
42     bool saveImages = true;
43
44     // Load arguments
45     foreach (QString str, args) {
46
47         if (str.startsWith("--profile=")) {
48             QString s = str;
49             s.remove(0, QString("--profile=").length());
50             profile = s.toStdString();
51             args.removeOne(str);
52
53         } else if (str == "-h" || str == "--help") {
54             printUsage(argv[0]);
55             return 0;
56
57         } else if (str == "--no-images") {
58             saveImages = false;
59             args.removeOne(str);
60         }
61
62     }
63
64     if (args.length() < 2) {
65         printUsage(argv[0]);
66         return 1;
67     }
68
69
70
71     std::string fileMain(args.at(0).toStdString());
72     args.removeFirst();
73     std::string fileSub = args.at(0).toStdString();
74     args.removeFirst();
75
76
77     qDebug() << "Unused arguments: " << args;
78
79
80     if (argc > 2) {
81         fileMain = argv[1];
82         fileSub = argv[2];
83     } else {
84         std::cout << "Usage: " << argv[0] << " <main audio file> <second audio file>" << std::endl;
85         return 0;
86     }
87     std::cout << "Trying to align (2)\n\t" << fileSub << "\nto fit on (1)\n\t" << fileMain
88               << "\n, result will indicate by how much (2) has to be moved." << std::endl
89               << "Profile used: " << profile << std::endl
90                  ;
91
92
93     // Initialize MLT
94     Mlt::Factory::init(NULL);
95
96     // Load an arbitrary profile
97     Mlt::Profile prof(profile.c_str());
98
99     // Load the MLT producers
100     Mlt::Producer prodMain(prof, fileMain.c_str());
101     if (!prodMain.is_valid()) {
102         std::cout << fileMain << " is invalid." << std::endl;
103         return 2;
104     }
105     Mlt::Producer prodSub(prof, fileSub.c_str());
106     if (!prodSub.is_valid()) {
107         std::cout << fileSub << " is invalid." << std::endl;
108         return 2;
109     }
110
111
112     // Build the audio envelopes for the correlation
113     AudioEnvelope *envelopeMain = new AudioEnvelope(&prodMain);
114     envelopeMain->loadEnvelope();
115     envelopeMain->loadStdDev();
116     envelopeMain->dumpInfo();
117
118     AudioEnvelope *envelopeSub = new AudioEnvelope(&prodSub);
119     envelopeSub->loadEnvelope();
120     envelopeSub->loadStdDev();
121     envelopeSub->dumpInfo();
122
123
124     float *correlated;
125     int corrSize = 0;
126
127     FFTCorrelation::correlate(envelopeMain->envelope(), envelopeMain->envelopeSize(),
128                               envelopeSub->envelope(), envelopeSub->envelopeSize(),
129                               &correlated, corrSize);
130
131
132     int maxIndex = 0;
133     float max = 0;
134     for (int i = 0; i < corrSize; i++) {
135         if (correlated[i] > max) {
136             max = correlated[i];
137             maxIndex = i;
138         }
139     }
140     int shift = envelopeMain->envelopeSize() - maxIndex-1;
141     qDebug() << "Max correlation value is " << max << " at " << maxIndex;
142     qDebug() << "Will have to move by " << shift << " frames";
143
144     std::cout << fileSub << " should be shifted by " << shift << " frames" << std::endl
145               << "\trelative to " << fileMain << std::endl
146               << "\tin a " << prodMain.get_fps() << " fps profile (" << profile << ")." << std::endl
147                  ;
148
149     if (saveImages) {
150         QString filename = QString("correlation-fft-%1.png")
151                         .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd-hh:mm:ss"));
152         QImage img(corrSize/2, 400, QImage::Format_ARGB32);
153         img.fill(qRgb(255,255,255));
154         for (int x = 0; x < img.width(); x++) {
155             float val = fabs(correlated[x]/max);
156             for (int y = 0; y < img.height()*val; y++) {
157                 img.setPixel(x, img.height()-1-y, qRgb(50,50,50));
158             }
159         }
160         img.save(filename);
161         qDebug() << "Saved image to " << filename;
162     }
163
164
165     delete correlated;
166
167 }