Mercurial > hg > svapp
diff align/LinearAligner.cpp @ 778:83a7b10b7415
Merge from branch pitch-align
author | Chris Cannam |
---|---|
date | Fri, 26 Jun 2020 13:48:52 +0100 |
parents | 699b5b130ea2 |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/align/LinearAligner.cpp Fri Jun 26 13:48:52 2020 +0100 @@ -0,0 +1,172 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + 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. See the file + COPYING included with this distribution for more information. +*/ + +#include "LinearAligner.h" + +#include "system/System.h" + +#include "data/model/Path.h" +#include "data/model/AlignmentModel.h" + +#include "framework/Document.h" + +#include "svcore/data/model/DenseTimeValueModel.h" + +#include <QApplication> + +LinearAligner::LinearAligner(Document *doc, + ModelId reference, + ModelId toAlign, + bool trimmed) : + m_document(doc), + m_reference(reference), + m_toAlign(toAlign), + m_trimmed(trimmed) +{ +} + +LinearAligner::~LinearAligner() +{ +} + +void +LinearAligner::begin() +{ + bool ready = false; + while (!ready) { + { // scope so as to release input shared_ptr before sleeping + auto reference = ModelById::get(m_reference); + auto toAlign = ModelById::get(m_toAlign); + if (!reference || !reference->isOK() || + !toAlign || !toAlign->isOK()) { + return; + } + ready = (reference->isReady() && toAlign->isReady()); + } + if (!ready) { + SVDEBUG << "LinearAligner: Waiting for models..." << endl; + QApplication::processEvents(QEventLoop::ExcludeUserInputEvents | + QEventLoop::ExcludeSocketNotifiers, + 500); + } + } + + auto reference = ModelById::get(m_reference); + auto toAlign = ModelById::get(m_toAlign); + + if (!reference || !reference->isOK() || + !toAlign || !toAlign->isOK()) { + return; + } + + sv_frame_t s0, e0, s1, e1; + s0 = reference->getStartFrame(); + e0 = reference->getEndFrame(); + s1 = toAlign->getStartFrame(); + e1 = toAlign->getEndFrame(); + + if (m_trimmed) { + getTrimmedExtents(m_reference, s0, e0); + getTrimmedExtents(m_toAlign, s1, e1); + SVCERR << "Trimmed extents: reference: " << s0 << " to " << e0 + << ", toAlign: " << s1 << " to " << e1 << endl; + } + + sv_frame_t d0 = e0 - s0, d1 = e1 - s1; + + if (d1 == 0) { + return; + } + + double ratio = double(d0) / double(d1); + int resolution = 1024; + + Path path(reference->getSampleRate(), resolution); + + for (sv_frame_t f = s1; f < e1; f += resolution) { + sv_frame_t target = s0 + sv_frame_t(double(f - s1) * ratio); + path.add(PathPoint(f, target)); + } + + auto alignment = std::make_shared<AlignmentModel>(m_reference, + m_toAlign, + ModelId()); + + auto alignmentModelId = ModelById::add(alignment); + + alignment->setPath(path); + alignment->setCompletion(100); + toAlign->setAlignment(alignmentModelId); + m_document->addNonDerivedModel(alignmentModelId); + + emit complete(alignmentModelId); +} + +bool +LinearAligner::getTrimmedExtents(ModelId modelId, + sv_frame_t &start, + sv_frame_t &end) +{ + auto model = ModelById::getAs<DenseTimeValueModel>(modelId); + if (!model) return false; + + sv_frame_t chunksize = 1024; + double threshold = 1e-2; + + auto rms = [](const floatvec_t &samples) { + double rms = 0.0; + for (auto s: samples) { + rms += s * s; + } + rms /= double(samples.size()); + rms = sqrt(rms); + return rms; + }; + + while (start < end) { + floatvec_t samples = model->getData(-1, start, chunksize); + if (samples.empty()) { + return false; // no non-silent content found + } + if (rms(samples) > threshold) { + for (auto s: samples) { + if (fabsf(s) > threshold) { + break; + } + ++start; + } + break; + } + start += chunksize; + } + + if (start >= end) { + return false; + } + + while (end > start) { + sv_frame_t probe = end - chunksize; + if (probe < 0) probe = 0; + floatvec_t samples = model->getData(-1, probe, chunksize); + if (samples.empty()) { + break; + } + if (rms(samples) > threshold) { + break; + } + end = probe; + } + + return (end > start); +}