diff audio/TimeStretchWrapper.h @ 738:48001ed9143b audio-source-refactor

Introduce TimeStretchWrapper; some work towards making the AudioCallbackPlaySource not actually try to be an ApplicationPlaybackSource itself but only return one that is constructed from wrappers that it controls the lifespan of
author Chris Cannam
date Wed, 18 Mar 2020 12:51:41 +0000
parents
children 846970dbef17
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audio/TimeStretchWrapper.h	Wed Mar 18 12:51:41 2020 +0000
@@ -0,0 +1,108 @@
+/* -*- 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.
+*/
+
+#ifndef SV_TIME_STRETCH_WRAPPER_H
+#define SV_TIME_STRETCH_WRAPPER_H
+
+#include "bqaudioio/ApplicationPlaybackSource.h"
+
+#include "base/BaseTypes.h"
+
+#include <vector>
+#include <mutex>
+
+namespace RubberBand {
+    class RubberBandStretcher;
+}
+
+/**
+ * A breakfastquay::ApplicationPlaybackSource wrapper that implements
+ * time-stretching using Rubber Band. Note that the stretcher is
+ * bypassed entirely when a ratio of 1.0 is set; this means it's
+ * (almost) free to use one of these wrappers normally, but it also
+ * means you can't switch from 1.0 to another ratio (or back again)
+ * without some audible artifacts.
+ *
+ * This is real-time safe while the ratio is fixed, and may perform
+ * reallocations when the ratio changes.
+ */
+class TimeStretchWrapper : public breakfastquay::ApplicationPlaybackSource
+{
+public:
+    /**
+     * Create a wrapper around the given ApplicationPlaybackSource,
+     * implementing another ApplicationPlaybackSource interface that
+     * draws from the same source data but with a time-stretcher
+     * optionally applied.
+     *
+     * The wrapper does not take ownership of the wrapped
+     * ApplicationPlaybackSource, whose lifespan must exceed that of
+     * this object.    
+     */
+    TimeStretchWrapper(ApplicationPlaybackSource *source);
+    ~TimeStretchWrapper();
+
+    /**
+     * Set a time stretch factor, i.e. playback speed, where 1.0 is
+     * normal speed
+     */
+    void setTimeStretchRatio(double ratio);
+
+    /**
+     * Clear stretcher buffers.
+     */
+    void reset();
+
+    // These functions are passed through to the wrapped
+    // ApplicationPlaybackSource
+    
+    std::string getClientName() const override;
+    int getApplicationSampleRate() const override;
+    int getApplicationChannelCount() const override;
+
+    void setSystemPlaybackBlockSize(int) override;
+    void setSystemPlaybackSampleRate(int) override;
+    void setSystemPlaybackChannelCount(int) override;
+    void setSystemPlaybackLatency(int) override;
+
+    void setOutputLevels(float peakLeft, float peakRight) override;
+    void audioProcessingOverload() override;
+
+    /** 
+     * Request some samples from the wrapped
+     * ApplicationPlaybackSource, time-stretch if appropriate, and
+     * return them to the target
+     */
+    int getSourceSamples(float *const *samples, int nchannels, int nframes)
+        override;
+
+private:
+    ApplicationPlaybackSource *m_source;
+    RubberBand::RubberBandStretcher *m_stretcher;
+    double m_timeRatio;
+    std::vector<std::vector<float>> m_inputs;
+    std::mutex m_mutex;
+    sv_frame_t m_stretcherInputSize;
+    int m_channelCount;
+    sv_samplerate_t m_sampleRate;
+
+    void checkStretcher(); // call without m_mutex held
+    
+    TimeStretchWrapper(const TimeStretchWrapper &)=delete;
+    TimeStretchWrapper &operator=(const TimeStretchWrapper &)=delete;
+};
+
+#endif
+
+