#pragma once #include "buffer.h" #include "randomaccessaudioreadable.h" #include "rendercontext.h" #include "ringbuffercache.h" #include "statefuldsp.h" #include "timing/warp.h" #include #include // adapt static to dynamic polymorphism, to select between stretcher // implementations struct BungeeStretcherBase { BungeeStretcherBase(int preroll, int blockDelay) : preroll(preroll), blockDelay(blockDelay) {}; virtual ~BungeeStretcherBase() {}; virtual Bungee::InputChunk specifyGrain(const Bungee::Request &request) = 0; virtual void analyseGrain(const float *data, intptr_t channelStride) = 0; virtual void synthesiseGrain(Bungee::OutputChunk &outputChunk) = 0; const int preroll; const int blockDelay; }; class TimestretchReader : public StatefulDSP { enum class State { Reset, ContinuousPlayback }; const std::shared_ptr underlyingBuffer; const int underlyingSampleRate; const int playbackSampleRate; const int synthesisHopSize; const int grainSize; State state; time_units::Beats currentPosition; time_units::Beats lastLocalPosition; time_units::Seconds lastAnalysisPosition; double resamplingSynthesisHopSize; double pitchFreqCoef; BufferF32 excessOutput; int excessOutputOffset; int excessOutputCount; BufferF32 underlyingChunk; float transposition; std::shared_ptr> warpMap; std::unique_ptr stretcher; void reset(const RenderContext *renderContext, time_units::Beats localPosition); void synthesize(const RenderContext *renderContext, time_units::Beats localPosition, bool reset); void calculatePitchCoefAndHopSize(); public: TimestretchReader(std::shared_ptr underlyingBuffer, std::shared_ptr> warpMap, int playbackSampleRate, bool useProAlgorithm); TimestretchReader(int channelCount, int underlyingSampleRate, int playbackSampleRate, bool useProAlgorithm); ~TimestretchReader(); void setUnderlying(std::shared_ptr underlyingBuffer, std::shared_ptr> warpMap); // returns true on reset bool readSegment(const RenderContext *renderContext, time_units::Beats localPosition, float incomingTransposition, BufferF32 output); time_units::Seconds getLastAnalysisPosition() const; };