From d6aa3a59c21618b174188fa200816d91ac1013a2 Mon Sep 17 00:00:00 2001 From: Chris Cannam Date: Wed, 18 May 2022 17:51:20 +0100 Subject: [PATCH] Begin some R3 work --- meson.build | 1 + src/R3StretcherImpl.h | 150 +++++++++++++++++++++++++++++++++++++ src/StretcherImpl.cpp | 8 +- src/StretcherImpl.h | 2 - src/dsp/BinClassifier.h | 159 ++++++++++++++++++++++++++++++++++++++++ src/dsp/MovingMedian.h | 3 + src/dsp/Window.h | 4 +- src/temporary.cpp | 1 + 8 files changed, 319 insertions(+), 9 deletions(-) create mode 100644 src/R3StretcherImpl.h create mode 100644 src/dsp/BinClassifier.h create mode 100644 src/temporary.cpp diff --git a/meson.build b/meson.build index 679b897..4a6dc38 100644 --- a/meson.build +++ b/meson.build @@ -48,6 +48,7 @@ library_sources = [ 'src/system/Thread.cpp', 'src/StretcherChannelData.cpp', 'src/StretcherImpl.cpp', + 'src/temporary.cpp', ] jni_sources = [ diff --git a/src/R3StretcherImpl.h b/src/R3StretcherImpl.h new file mode 100644 index 0000000..16d6e5d --- /dev/null +++ b/src/R3StretcherImpl.h @@ -0,0 +1,150 @@ +/* -*- 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_R3_STRETCHERIMPL_H +#define RUBBERBAND_R3_STRETCHERIMPL_H + +#include +#include + +#include "dsp/BinClassifier.h" +#include "dsp/FFT.h" + +namespace RubberBand +{ + +class R3StretcherImpl +{ +public: + R3StretcherImpl(int sampleRate, int channels); + ~R3StretcherImpl(); + + void reset(); + + void setTimeRatio(double ratio); + void setPitchScale(double scale); + + double getTimeRatio() const; + double getPitchScale() const; + +protected: + int m_sampleRate; + int m_channels; + + double m_timeRatio; + double m_pitchScale; + + struct FftBand { + int fftSize; + float f0; + float f1; + FftBand(int _s, float _f0, float _f1) : + fftSize(_s), f0(_f0), f1(_f1) { } + }; + + struct PhaseLockBand { + int p; + float beta; + float f0; + float f1; + PhaseLockBand(int _p, float _beta, float _f0, float _f1) : + p(_p), beta(_beta), f0(_f0), f1(_f1) { } + }; + + struct Range { + bool present; + float f0; + float f1; + Range() : present(false), f0(0.f), f1(0.f) { } + }; + + struct Guidance { + FftBand fftBands[3]; + PhaseLockBand phaseLockBands[5]; + Range kick; + Range lowPercussive; + Range phaseReset; + Range highPercussive; + 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 + float *mag; + float *phase; + int *nearestPeaks; + int *nearestTroughs; + float *prevOutMag; + float *prevOutPhase; + int *prevNearestPeaks; + + ChannelScaleData(int _fftSize) : + fftSize(_fftSize), bufSize(_fftSize/2 + 1), + mag(allocate_and_zero(size_t(bufSize))), + phase(allocate_and_zero(size_t(bufSize))), + nearestPeaks(allocate_and_zero(size_t(bufSize))), + nearestTroughs(allocate_and_zero(size_t(bufSize))), + prevOutMag(allocate_and_zero(size_t(bufSize))), + prevOutPhase(allocate_and_zero(size_t(bufSize))), + prevNearestPeaks(allocate_and_zero(size_t(bufSize))) { } + + ~ChannelScaleData() { + deallocate(mag); + deallocate(phase); + deallocate(nearestPeaks); + deallocate(nearestTroughs); + deallocate(prevOutMag); + deallocate(prevOutPhase); + deallocate(prevNearestPeaks); + } + + private: + ChannelScaleData(const ChannelScaleData &) =delete; + ChannelScaleData &operator=(const ChannelScaleData &) =delete; + }; + + struct ChannelData { + std::map> scales; + std::unique_ptr classifier; + BinSegmentation segmentation; + BinSegmentation prevSegmentation; + BinSegmentation nextSegmentation; + Guidance guidance; + }; + + std::map> m_ffts; + +}; + +} + +#endif diff --git a/src/StretcherImpl.cpp b/src/StretcherImpl.cpp index cfc16a8..97c43c8 100644 --- a/src/StretcherImpl.cpp +++ b/src/StretcherImpl.cpp @@ -43,8 +43,6 @@ #include #include -using namespace RubberBand; - using std::cerr; using std::endl; using std::vector; @@ -619,7 +617,7 @@ RubberBandStretcher::Impl::configure() for (set::const_iterator i = windowSizes.begin(); i != windowSizes.end(); ++i) { if (m_windows.find(*i) == m_windows.end()) { - m_windows[*i] = new Window(HanningWindow, *i); + m_windows[*i] = new Window(HannWindow, *i); } if (m_sincs.find(*i) == m_sincs.end()) { m_sincs[*i] = new SincWindow(*i, *i); @@ -768,7 +766,7 @@ RubberBandStretcher::Impl::reconfigure() if (m_windows.find(m_aWindowSize) == m_windows.end()) { std::cerr << "WARNING: reconfigure(): window allocation (size " << m_aWindowSize << ") required in RT mode" << std::endl; m_windows[m_aWindowSize] = new Window - (HanningWindow, m_aWindowSize); + (HannWindow, m_aWindowSize); m_sincs[m_aWindowSize] = new SincWindow (m_aWindowSize, m_aWindowSize); } @@ -776,7 +774,7 @@ RubberBandStretcher::Impl::reconfigure() if (m_windows.find(m_sWindowSize) == m_windows.end()) { std::cerr << "WARNING: reconfigure(): window allocation (size " << m_sWindowSize << ") required in RT mode" << std::endl; m_windows[m_sWindowSize] = new Window - (HanningWindow, m_sWindowSize); + (HannWindow, m_sWindowSize); m_sincs[m_sWindowSize] = new SincWindow (m_sWindowSize, m_sWindowSize); } diff --git a/src/StretcherImpl.h b/src/StretcherImpl.h index b75fc9e..6485e6e 100644 --- a/src/StretcherImpl.h +++ b/src/StretcherImpl.h @@ -40,8 +40,6 @@ #include #include -using namespace RubberBand; - namespace RubberBand { diff --git a/src/dsp/BinClassifier.h b/src/dsp/BinClassifier.h new file mode 100644 index 0000000..348d0d7 --- /dev/null +++ b/src/dsp/BinClassifier.h @@ -0,0 +1,159 @@ +/* -*- 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_CLASSIFIER_H +#define RUBBERBAND_BIN_CLASSIFIER_H + +#include "../system/Allocators.h" +#include "../dsp/MovingMedian.h" +#include "../base/RingBuffer.h" + +#include +#include + +namespace RubberBand { + +class BinClassifier { + + enum class Classification { + Harmonic = 0, + Percussive = 1, + Residual = 2, + Silent = 3 + }; + + struct Parameters { + int binCount; + int horizontalFilterLength; + int horizontalFilterLag; + int verticalFilterLength; + double harmonicThreshold; + double percussiveThreshold; + float silenceThreshold; + Parameters(int _binCount, int _horizontalFilterLength, + int _horizontalFilterLag, int _verticalFilterLength, + double _harmonicThreshold, double _percussiveThreshold, + float _silenceThreshold) : + binCount(_binCount), + horizontalFilterLength(_horizontalFilterLength), + horizontalFilterLag(_horizontalFilterLag), + verticalFilterLength(_verticalFilterLength), + harmonicThreshold(_harmonicThreshold), + percussiveThreshold(_percussiveThreshold), + silenceThreshold(_silenceThreshold) { } + }; + + BinClassifier(Parameters parameters) : + m_parameters(parameters), + m_vfQueue(parameters.horizontalFilterLag) + { + int n = m_parameters.binCount; + + for (int i = 0; i < n; ++i) { + m_hFilters.push_back(std::make_shared> + (m_parameters.horizontalFilterLength)); + } + + m_vFilter = std::make_unique> + (m_parameters.verticalFilterLength); + + m_hf = allocate_and_zero(n); + m_vf = allocate_and_zero(n); + + for (int i = 0; i < m_parameters.horizontalFilterLag; ++i) { + float *entry = allocate_and_zero(n); + m_vfQueue.write(&entry, 1); + } + } + + ~BinClassifier() + { + while (m_vfQueue.getReadSpace() > 0) { + float *entry = m_vfQueue.readOne(); + deallocate(entry); + } + + deallocate(m_hf); + deallocate(m_vf); + } + + void classify(const float *const mag, Classification *classification) { + const int n = m_parameters.binCount; + + for (int i = 0; i < n; ++i) { + 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(); + } + + if (m_parameters.horizontalFilterLag > 0) { + float *lagged = m_vfQueue.readOne(); + m_vfQueue.write(&m_vf, 1); + m_vf = lagged; + } + + double eps = 1.0e-7; + + for (int i = 0; i < n; ++i) { + Classification c; + if (mag[i] < m_parameters.silenceThreshold) { + c = Classification::Silent; + } else if (double(m_hf[i]) / (double(m_vf[i]) + eps) > + m_parameters.harmonicThreshold) { + c = Classification::Harmonic; + } else if (double(m_vf[i]) / (double(m_hf[i]) + eps) > + m_parameters.percussiveThreshold) { + c = Classification::Percussive; + } else { + c = Classification::Residual; + } + classification[i] = c; + } + } + +protected: + Parameters m_parameters; + std::vector>> m_hFilters; + std::unique_ptr> m_vFilter; + float *m_hf; + float *m_vf; + RingBuffer m_vfQueue; +}; + +} + +#endif diff --git a/src/dsp/MovingMedian.h b/src/dsp/MovingMedian.h index ba18390..4d6389c 100644 --- a/src/dsp/MovingMedian.h +++ b/src/dsp/MovingMedian.h @@ -102,6 +102,9 @@ private: v_move(index, index + 1, m_sortend - index); *m_sortend = T(0); } + + MovingMedian(const MovingMedian &) =delete; + MovingMedian &operator=(const MovingMedian &) =delete; }; } diff --git a/src/dsp/Window.h b/src/dsp/Window.h index c6c9b81..bfd947a 100644 --- a/src/dsp/Window.h +++ b/src/dsp/Window.h @@ -38,7 +38,7 @@ enum WindowType { RectangularWindow, BartlettWindow, HammingWindow, - HanningWindow, + HannWindow, BlackmanWindow, GaussianWindow, ParzenWindow, @@ -136,7 +136,7 @@ void Window::encache() cosinewin(m_cache, 0.54, 0.46, 0.0, 0.0); break; - case HanningWindow: + case HannWindow: cosinewin(m_cache, 0.50, 0.50, 0.0, 0.0); break; diff --git a/src/temporary.cpp b/src/temporary.cpp new file mode 100644 index 0000000..75cf924 --- /dev/null +++ b/src/temporary.cpp @@ -0,0 +1 @@ +#include "R3StretcherImpl.h"