File indexing completed on 2024-04-21 04:52:40
0001 /* 0002 SPDX-FileCopyrightText: 2012 Simon A. Eugster (Granjow) <simon.eu@gmail.com> 0003 This file is part of kdenlive. See www.kdenlive.org. 0004 0005 SPDX-License-Identifier: GPL-3.0-or-later 0006 */ 0007 0008 #include <QCoreApplication> 0009 #include <QDateTime> 0010 #include <QDebug> 0011 #include <QFile> 0012 #include <QFileInfo> 0013 #include <QStringList> 0014 #include <cmath> 0015 #include <cstdlib> 0016 #include <iostream> 0017 #include <mlt++/Mlt.h> 0018 0019 #include "../src/lib/audio/audioCorrelation.h" 0020 #include "../src/lib/audio/audioEnvelope.h" 0021 #include "../src/lib/audio/audioInfo.h" 0022 #include "../src/lib/audio/audioStreamInfo.h" 0023 0024 void printUsage(const char *path) 0025 { 0026 std::cout << "This executable takes two audio/video files A and B and determines " << std::endl 0027 << "how much B needs to be shifted in order to be synchronized with A." << std::endl 0028 << std::endl 0029 << path << " <main audio file> <second audio file>" << std::endl 0030 << "\t-h, --help\n\t\tDisplay this help" << std::endl 0031 << "\t--fft\n\t\tUse Fourier Transform (FFT) to calculate the offset. This only takes" << std::endl 0032 << "\t\tO(n log n) time compared to O(n²) when using normal correlation and should be " << std::endl 0033 << "\t\tfaster for large data (several minutes)." << std::endl 0034 << "\t--profile=<profile>\n\t\tUse the given profile for calculation (run: melt -query profiles)" << std::endl 0035 << "\t--no-images\n\t\tDo not save envelope and correlation images" << std::endl; 0036 } 0037 0038 int main(int argc, char *argv[]) 0039 { 0040 QCoreApplication app(argc, argv); 0041 QStringList args = app.arguments(); 0042 args.removeAt(0); 0043 0044 std::string profile = "atsc_1080p_24"; 0045 bool saveImages = true; 0046 bool useFFT = false; 0047 0048 // Load arguments 0049 foreach (const QString &str, args) { 0050 0051 if (str.startsWith(QLatin1String("--profile="))) { 0052 QString s = str; 0053 s.remove(0, QString("--profile=").length()); 0054 profile = s.toStdString(); 0055 args.removeOne(str); 0056 0057 } else if (str == "-h" || str == "--help") { 0058 printUsage(argv[0]); 0059 return 0; 0060 0061 } else if (str == "--no-images") { 0062 saveImages = false; 0063 args.removeOne(str); 0064 0065 } else if (str == "--fft") { 0066 useFFT = true; 0067 args.removeOne(str); 0068 } 0069 } 0070 0071 if (args.length() < 2) { 0072 printUsage(argv[0]); 0073 return 1; 0074 } 0075 0076 std::string fileMain(args.at(0).toStdString()); 0077 args.removeFirst(); 0078 std::string fileSub = args.at(0).toStdString(); 0079 args.removeFirst(); 0080 0081 qDebug() << "Unused arguments: " << args; 0082 0083 if (argc > 2) { 0084 fileMain = argv[1]; 0085 fileSub = argv[2]; 0086 } else { 0087 std::cout << "Usage: " << argv[0] << " <main audio file> <second audio file>" << std::endl; 0088 return 0; 0089 } 0090 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." 0091 << std::endl 0092 << "Profile used: " << profile << std::endl; 0093 if (useFFT) { 0094 std::cout << "Will use FFT based correlation." << std::endl; 0095 } 0096 0097 // Initialize MLT 0098 Mlt::Factory::init(NULL); 0099 0100 // Load an arbitrary profile 0101 Mlt::Profile prof(profile.c_str()); 0102 0103 // Load the MLT producers 0104 Mlt::Producer prodMain(prof, fileMain.c_str()); 0105 if (!prodMain.is_valid()) { 0106 std::cout << fileMain << " is invalid." << std::endl; 0107 return 2; 0108 } 0109 Mlt::Producer prodSub(prof, fileSub.c_str()); 0110 if (!prodSub.is_valid()) { 0111 std::cout << fileSub << " is invalid." << std::endl; 0112 return 2; 0113 } 0114 0115 // Build the audio envelopes for the correlation 0116 AudioEnvelope *envelopeMain = new AudioEnvelope(fileMain.c_str(), &prodMain); 0117 envelopeMain->loadEnvelope(); 0118 envelopeMain->loadStdDev(); 0119 envelopeMain->dumpInfo(); 0120 0121 AudioEnvelope *envelopeSub = new AudioEnvelope(fileSub.c_str(), &prodSub); 0122 envelopeSub->loadEnvelope(); 0123 envelopeSub->loadStdDev(); 0124 envelopeSub->dumpInfo(); 0125 0126 // Calculate the correlation and hereby the audio shift 0127 AudioCorrelation corr(envelopeMain); 0128 int index = 0; 0129 corr.addChild(envelopeSub /*, useFFT*/); 0130 0131 int shift = corr.getShift(index); 0132 std::cout << " Should be shifted by " << shift << " frames: " << fileSub << std::endl 0133 << "\trelative to " << fileMain << std::endl 0134 << "\tin a " << prodMain.get_fps() << " fps profile (" << profile << ")." << std::endl; 0135 0136 if (saveImages) { 0137 QString outImg = QString::fromLatin1("envelope-main-%1.png").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd-hh:mm:ss")); 0138 envelopeMain->drawEnvelope().save(outImg); 0139 std::cout << "Saved volume envelope as " << QFileInfo(outImg).absoluteFilePath().toStdString() << std::endl; 0140 outImg = QString::fromLatin1("envelope-sub-%1.png").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd-hh:mm:ss")); 0141 envelopeSub->drawEnvelope().save(outImg); 0142 std::cout << "Saved volume envelope as " << QFileInfo(outImg).absoluteFilePath().toStdString() << std::endl; 0143 outImg = QString::fromLatin1("correlation-%1.png").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd-hh:mm:ss")); 0144 corr.info(index)->toImage().save(outImg); 0145 std::cout << "Saved correlation image as " << QFileInfo(outImg).absoluteFilePath().toStdString() << std::endl; 0146 } 0147 0148 // Mlt::Factory::close(); 0149 0150 return 0; 0151 }