File indexing completed on 2024-04-28 04:51:59

0001 /*
0002 SPDX-FileCopyrightText: 2012 Simon A. Eugster (Granjow)  <simon.eu@gmail.com>
0003 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0004 */
0005 
0006 #include "audioCorrelation.h"
0007 #include "fftCorrelation.h"
0008 
0009 #include "kdenlive_debug.h"
0010 #include "klocalizedstring.h"
0011 #include <QElapsedTimer>
0012 #include <cmath>
0013 #include <iostream>
0014 
0015 AudioCorrelation::AudioCorrelation(std::unique_ptr<AudioEnvelope> mainTrackEnvelope)
0016     : m_mainTrackEnvelope(std::move(mainTrackEnvelope))
0017 {
0018     // Q_ASSERT(!mainTrackEnvelope->hasComputationStarted());
0019     connect(m_mainTrackEnvelope.get(), &AudioEnvelope::envelopeReady, this, &AudioCorrelation::slotAnnounceEnvelope);
0020     m_mainTrackEnvelope->startComputeEnvelope();
0021 }
0022 
0023 AudioCorrelation::~AudioCorrelation()
0024 {
0025     for (AudioEnvelope *envelope : qAsConst(m_children)) {
0026         delete envelope;
0027     }
0028     for (AudioCorrelationInfo *info : qAsConst(m_correlations)) {
0029         delete info;
0030     }
0031 
0032     qCDebug(KDENLIVE_LOG) << "Envelope deleted.";
0033 }
0034 
0035 void AudioCorrelation::slotAnnounceEnvelope()
0036 {
0037     Q_EMIT displayMessage(i18n("Audio analysis finished"), OperationCompletedMessage, 300);
0038 }
0039 
0040 void AudioCorrelation::addChild(AudioEnvelope *envelope)
0041 {
0042     // We need to connect before starting the computation, to make sure
0043     // there is no race condition where the signal 'envelopeReady' is
0044     // lost.
0045     Q_ASSERT(!envelope->hasComputationStarted());
0046     connect(envelope, &AudioEnvelope::envelopeReady, this, &AudioCorrelation::slotProcessChild);
0047     envelope->startComputeEnvelope();
0048 }
0049 
0050 void AudioCorrelation::slotProcessChild(AudioEnvelope *envelope)
0051 {
0052     // Note that at this point the computation of the envelope of the
0053     // main track might not be finished. envelope() will block until
0054     // the computation is done.
0055     const size_t sizeMain = m_mainTrackEnvelope->envelope().size();
0056     const size_t sizeSub = envelope->envelope().size();
0057 
0058     auto *info = new AudioCorrelationInfo(sizeMain, sizeSub);
0059     qint64 *correlation = info->correlationVector();
0060 
0061     const std::vector<qint64> &envMain = m_mainTrackEnvelope->envelope();
0062     const std::vector<qint64> &envSub = envelope->envelope();
0063     qint64 max = 0;
0064 
0065     if (sizeSub > 200) {
0066         FFTCorrelation::correlate(&envMain[0], sizeMain, &envSub[0], sizeSub, correlation);
0067     } else {
0068         correlate(&envMain[0], sizeMain, &envSub[0], sizeSub, correlation, &max);
0069         info->setMax(max);
0070     }
0071 
0072     m_children.append(envelope);
0073     m_correlations.append(info);
0074 
0075     Q_ASSERT(m_correlations.size() == m_children.size());
0076     int index = m_children.indexOf(envelope);
0077     int shift = getShift(index);
0078     Q_EMIT gotAudioAlignData(envelope->clipId(), shift);
0079 }
0080 
0081 int AudioCorrelation::getShift(int childIndex) const
0082 {
0083     Q_ASSERT(childIndex >= 0);
0084     Q_ASSERT(childIndex < m_correlations.size());
0085 
0086     size_t indexOffset = m_correlations.at(childIndex)->maxIndex();
0087     indexOffset -= m_children.at(childIndex)->envelope().size();
0088     indexOffset += m_children.at(childIndex)->offset();
0089 
0090     return int(indexOffset);
0091 }
0092 
0093 AudioCorrelationInfo const *AudioCorrelation::info(int childIndex) const
0094 {
0095     Q_ASSERT(childIndex >= 0);
0096     Q_ASSERT(childIndex < m_correlations.size());
0097 
0098     return m_correlations.at(childIndex);
0099 }
0100 
0101 void AudioCorrelation::correlate(const qint64 *envMain, size_t sizeMain, const qint64 *envSub, size_t sizeSub, qint64 *correlation, qint64 *out_max)
0102 {
0103     Q_ASSERT(correlation != nullptr);
0104 
0105     qint64 const *left;
0106     qint64 const *right;
0107     size_t size;
0108     qint64 sum;
0109     qint64 max = 0;
0110 
0111     /*
0112       Correlation:
0113 
0114       SHIFT \in [-sS..sM]
0115 
0116       <--sS----
0117       [  sub  ]----sM--->[ sub ]
0118                [  main  ]
0119 
0120             ^ correlation vector index = SHIFT + sS
0121 
0122       main is fixed, sub is shifted along main.
0123 
0124     */
0125 
0126     QElapsedTimer t;
0127     t.start();
0128     for (size_t shift = -sizeSub; shift <= sizeMain; ++shift) {
0129 
0130         if (shift == 0) {
0131             left = envSub - shift;
0132             right = envMain;
0133             size = std::min(sizeSub + shift, sizeMain);
0134         } else {
0135             left = envSub;
0136             right = envMain + shift;
0137             size = std::min(sizeSub, sizeMain - shift);
0138         }
0139 
0140         sum = 0;
0141         for (size_t i = 0; i < size; ++i) {
0142             sum += (*left) * (*right);
0143             left++;
0144             right++;
0145         }
0146         correlation[sizeSub + shift] = qAbs(sum);
0147 
0148         if (sum > max) {
0149             max = sum;
0150         }
0151     }
0152     qCDebug(KDENLIVE_LOG) << "Correlation calculated. Time taken: " << t.elapsed() << " ms.";
0153 
0154     if (out_max != nullptr) {
0155         *out_max = max;
0156     }
0157 }