diff --git a/src/R3StretcherImpl.h b/src/R3StretcherImpl.h index 16d6e5d..420a03f 100644 --- a/src/R3StretcherImpl.h +++ b/src/R3StretcherImpl.h @@ -27,8 +27,9 @@ #include #include -#include "dsp/BinClassifier.h" +#include "dsp/BinSegmenter.h" #include "dsp/FFT.h" +#include "system/Allocators.h" namespace RubberBand { @@ -88,14 +89,6 @@ protected: Range channelLock; }; - struct BinSegmentation { - float percussiveBelow; - float percussiveAbove; - float residualAbove; - BinSegmentation(float _pb, float _pa, float _ra) : - percussiveBelow(_pb), percussiveAbove(_pa), residualAbove(_ra) { } - }; - struct ChannelScaleData { int fftSize; int bufSize; // size of every array here: fftSize/2 + 1 @@ -134,10 +127,10 @@ protected: struct ChannelData { std::map> scales; - std::unique_ptr classifier; - BinSegmentation segmentation; - BinSegmentation prevSegmentation; - BinSegmentation nextSegmentation; + std::unique_ptr segmenter; + BinSegmenter::Segmentation segmentation; + BinSegmenter::Segmentation prevSegmentation; + BinSegmenter::Segmentation nextSegmentation; Guidance guidance; }; diff --git a/src/dsp/BinClassifier.h b/src/dsp/BinClassifier.h index 348d0d7..59e17fd 100644 --- a/src/dsp/BinClassifier.h +++ b/src/dsp/BinClassifier.h @@ -33,8 +33,9 @@ namespace RubberBand { -class BinClassifier { - +class BinClassifier +{ +public: enum class Classification { Harmonic = 0, Percussive = 1, @@ -104,21 +105,9 @@ class BinClassifier { m_hFilters[i]->push(mag[i]); m_hf[i] = m_hFilters[i]->get(); } - - m_vFilter->reset(); - int vFilterLag = m_parameters.verticalFilterLength / 2; - - for (int i = 0; i < vFilterLag; ++i) { - m_vFilter->push(mag[i]); - } - for (int i = vFilterLag; i < n; ++i) { - m_vFilter->push(mag[i]); - m_vf[i-vFilterLag] = m_vFilter->get(); - } - for (int i = n; i < n + vFilterLag; ++i) { - m_vFilter->push(0.f); - m_vf[i-vFilterLag] = m_vFilter->get(); - } + + v_copy(m_vf, mag, n); + MovingMedian::filter(*m_vFilter, m_vf); if (m_parameters.horizontalFilterLag > 0) { float *lagged = m_vfQueue.readOne(); @@ -152,6 +141,9 @@ protected: float *m_hf; float *m_vf; RingBuffer m_vfQueue; + + BinClassifier(const BinClassifier &) =delete; + BinClassifier &operator=(const BinClassifier &) =delete; }; } diff --git a/src/dsp/BinSegmenter.h b/src/dsp/BinSegmenter.h new file mode 100644 index 0000000..dd3b4ea --- /dev/null +++ b/src/dsp/BinSegmenter.h @@ -0,0 +1,129 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rubber Band Library + An audio time-stretching and pitch-shifting library. + Copyright 2007-2022 Particular Programs Ltd. + + 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. + + Alternatively, if you have a valid commercial licence for the + Rubber Band Library obtained by agreement with the copyright + holders, you may redistribute and/or modify it under the terms + described in that licence. + + If you wish to distribute code using the Rubber Band Library + under terms other than those of the GNU General Public License, + you must obtain a valid commercial licence before doing so. +*/ + +#ifndef RUBBERBAND_BIN_SEGMENTER_H +#define RUBBERBAND_BIN_SEGMENTER_H + +#include "BinClassifier.h" +#include + +namespace RubberBand { + +class BinSegmenter +{ +public: + struct Segmentation { + double percussiveBelow; + double percussiveAbove; + double residualAbove; + Segmentation(double _pb, double _pa, double _ra) : + percussiveBelow(_pb), percussiveAbove(_pa), residualAbove(_ra) { } + }; + + struct Parameters { + int fftSize; + double sampleRate; + Parameters(int _fftSize, double _sampleRate) : + fftSize(_fftSize), sampleRate(_sampleRate) { } + }; + + BinSegmenter(Parameters parameters, + BinClassifier::Parameters classifierParameters) : + m_parameters(parameters), + m_classifierParameters(classifierParameters), + m_classifier(classifierParameters), + m_classification(classifierParameters.binCount, + BinClassifier::Classification::Silent), + m_numeric(classifierParameters.binCount, 0), + m_classFilter(classifierParameters.binCount / 64) + { + } + + Segmentation segment(const float *const mag) { + int n = m_classifierParameters.binCount; + m_classifier.classify(mag, m_classification.data()); + for (int i = 0; i < n; ++i) { + switch (m_classification[i]) { + case BinClassifier::Classification::Harmonic: + m_numeric[i] = 0; break; + case BinClassifier::Classification::Percussive: + m_numeric[i] = 1; break; + default: + m_numeric[i] = 2; break; + } + } + MovingMedian::filter(m_classFilter, m_numeric.data()); + double f0 = 0.0; + for (int i = 1; i < n; ++i) { + if (m_numeric[i] != 1) { + f0 = frequencyForBin(i); + break; + } + } + double nyquist = m_parameters.sampleRate / 2.0; + int top = binForFrequency(16000.0); + if (top >= n) top = n-1; + double f1 = nyquist; + double f2 = nyquist; + bool inPercussive = false; + for (int i = top; i > 0; --i) { + if (m_numeric[i] == 1) { // percussive + if (!inPercussive) { + inPercussive = true; + f2 = frequencyForBin(i); + continue; + } + } else if (m_numeric[i] == 0) { // harmonic + if (inPercussive) { + f1 = frequencyForBin(i); + } + break; // always when harmonic reached + } + } + return Segmentation(f0, f1, f2); + } + +protected: + Parameters m_parameters; + BinClassifier::Parameters m_classifierParameters; + BinClassifier m_classifier; + std::vector m_classification; + std::vector m_numeric; + MovingMedian m_classFilter; + + int binForFrequency(double f) { + return int(round(f * double(m_parameters.fftSize) / + m_parameters.sampleRate)); + } + double frequencyForBin(int b) { + return (double(b) * m_parameters.sampleRate) + / double(m_parameters.fftSize); + } + + BinSegmenter(const BinSegmenter &) =delete; + BinSegmenter &operator=(const BinSegmenter &) =delete; +}; + +} + +#endif diff --git a/src/dsp/MovingMedian.h b/src/dsp/MovingMedian.h index 4d6389c..12b144d 100644 --- a/src/dsp/MovingMedian.h +++ b/src/dsp/MovingMedian.h @@ -80,6 +80,27 @@ public: v_zero(m_sorted, P::m_size); } + // Convenience function that applies a given filter to an array + // in-place. Array must have length equal to getSize(). Modifies + // both the filter and the array. + // + static void filter(MovingMedian &mm, T *v) { + int n = mm.getSize(); + int lag = n / 2; + mm.reset(); + for (int i = 0; i < lag; ++i) { + mm.push(v[i]); + } + for (int i = lag; i < n; ++i) { + mm.push(v[i]); + v[i-lag] = mm.get(); + } + for (int i = n; i < n + lag; ++i) { + mm.push(T()); + v[i-lag] = mm.get(); + } + } + private: T *const m_frame; T *const m_sorted;