Chris@43: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@43: Chris@43: /* Chris@43: Sonic Visualiser Chris@43: An audio file viewer and annotation editor. Chris@43: Centre for Digital Music, Queen Mary, University of London. Chris@43: This file copyright 2006 Chris Cannam and QMUL. Chris@43: Chris@43: This program is free software; you can redistribute it and/or Chris@43: modify it under the terms of the GNU General Public License as Chris@43: published by the Free Software Foundation; either version 2 of the Chris@43: License, or (at your option) any later version. See the file Chris@43: COPYING included with this distribution for more information. Chris@43: */ Chris@43: Chris@43: #ifndef _PHASE_VOCODER_TIME_STRETCHER_H_ Chris@43: #define _PHASE_VOCODER_TIME_STRETCHER_H_ Chris@43: Chris@43: #include "base/Window.h" Chris@43: #include "base/RingBuffer.h" Chris@43: Chris@43: #include "data/fft/FFTapi.h" Chris@43: Chris@43: #include Chris@43: Chris@43: /** Chris@43: * A time stretcher that alters the performance speed of audio, Chris@43: * preserving pitch. Chris@43: * Chris@43: * This is based on the straightforward phase vocoder with phase Chris@43: * unwrapping (as in e.g. the DAFX book pp275-), with optional Chris@43: * percussive transient detection to avoid smearing percussive notes Chris@43: * and resynchronise phases, and adding a stream API for real-time Chris@43: * use. Principles and methods from Chris Duxbury, AES 2002 and 2004 Chris@43: * thesis; Emmanuel Ravelli, DAFX 2005; Dan Barry, ISSC 2005 on Chris@43: * percussion detection; code by Chris Cannam. Chris@43: */ Chris@43: Chris@43: class PhaseVocoderTimeStretcher Chris@43: { Chris@43: public: Chris@43: PhaseVocoderTimeStretcher(size_t sampleRate, Chris@43: size_t channels, Chris@43: float ratio, Chris@43: bool sharpen, Chris@43: size_t maxOutputBlockSize); Chris@43: virtual ~PhaseVocoderTimeStretcher(); Chris@43: Chris@43: /** Chris@43: * Return the number of samples that would need to be added via Chris@43: * putInput in order to provoke the time stretcher into doing some Chris@43: * time stretching and making more output samples available. Chris@43: * This will be an estimate, if transient sharpening is on; the Chris@43: * caller may need to do the put/get/test cycle more than once. Chris@43: */ Chris@43: size_t getRequiredInputSamples() const; Chris@43: Chris@43: /** Chris@43: * Put (and possibly process) a given number of input samples. Chris@43: * Number should usually equal the value returned from Chris@43: * getRequiredInputSamples(). Chris@43: */ Chris@43: void putInput(float **input, size_t samples); Chris@43: Chris@43: /** Chris@43: * Get the number of processed samples ready for reading. Chris@43: */ Chris@43: size_t getAvailableOutputSamples() const; Chris@43: Chris@43: /** Chris@43: * Get some processed samples. Chris@43: */ Chris@43: void getOutput(float **output, size_t samples); Chris@43: Chris@43: //!!! and reset? Chris@43: Chris@43: /** Chris@43: * Change the time stretch ratio. Chris@43: */ Chris@43: void setRatio(float ratio); Chris@43: Chris@43: /** Chris@43: * Get the hop size for input. Chris@43: */ Chris@43: size_t getInputIncrement() const { return m_n1; } Chris@43: Chris@43: /** Chris@43: * Get the hop size for output. Chris@43: */ Chris@43: size_t getOutputIncrement() const { return m_n2; } Chris@43: Chris@43: /** Chris@43: * Get the window size for FFT processing. Chris@43: */ Chris@43: size_t getWindowSize() const { return m_wlen; } Chris@43: Chris@43: /** Chris@43: * Get the stretch ratio. Chris@43: */ Chris@43: float getRatio() const { return float(m_n2) / float(m_n1); } Chris@43: Chris@43: /** Chris@43: * Return whether this time stretcher will attempt to sharpen transients. Chris@43: */ Chris@43: bool getSharpening() const { return m_sharpen; } Chris@43: Chris@43: /** Chris@43: * Return the number of channels for this time stretcher. Chris@43: */ Chris@43: size_t getChannelCount() const { return m_channels; } Chris@43: Chris@43: /** Chris@43: * Get the latency added by the time stretcher, in sample frames. Chris@43: * This will be exact if transient sharpening is off, or approximate Chris@43: * if it is on. Chris@43: */ Chris@43: size_t getProcessingLatency() const; Chris@43: Chris@43: protected: Chris@43: /** Chris@43: * Process a single phase vocoder frame from "in" into Chris@43: * m_freq[channel]. Chris@43: */ Chris@43: void analyseBlock(size_t channel, float *in); // into m_freq[channel] Chris@43: Chris@43: /** Chris@43: * Examine m_freq[0..m_channels-1] and return whether a percussive Chris@43: * transient is found. Chris@43: */ Chris@43: bool isTransient(); Chris@43: Chris@43: /** Chris@43: * Resynthesise from m_freq[channel] adding in to "out", Chris@43: * adjusting phases on the basis of a prior step size of lastStep. Chris@43: * Also add the window shape in to the modulation array (if Chris@43: * present) -- for use in ensuring the output has the correct Chris@43: * magnitude afterwards. Chris@43: */ Chris@43: void synthesiseBlock(size_t channel, float *out, float *modulation, Chris@43: size_t lastStep); Chris@43: Chris@43: void initialise(); Chris@43: void calculateParameters(); Chris@43: void cleanup(); Chris@43: Chris@43: bool shouldSharpen() { Chris@43: return m_sharpen && (m_ratio > 0.25); Chris@43: } Chris@43: Chris@43: size_t m_sampleRate; Chris@43: size_t m_channels; Chris@43: size_t m_maxOutputBlockSize; Chris@43: float m_ratio; Chris@43: bool m_sharpen; Chris@43: size_t m_n1; Chris@43: size_t m_n2; Chris@43: size_t m_wlen; Chris@43: Window *m_analysisWindow; Chris@43: Window *m_synthesisWindow; Chris@43: Chris@43: int m_totalCount; Chris@43: int m_transientCount; Chris@43: int m_n2sum; Chris@43: Chris@43: float **m_prevPhase; Chris@43: float **m_prevAdjustedPhase; Chris@43: Chris@43: float *m_prevTransientMag; Chris@43: int m_prevTransientScore; Chris@43: int m_transientThreshold; Chris@43: bool m_prevTransient; Chris@43: Chris@43: float *m_tempbuf; Chris@43: float **m_time; Chris@43: fftf_complex **m_freq; Chris@43: fftf_plan *m_plan; Chris@43: fftf_plan *m_iplan; Chris@43: Chris@43: RingBuffer **m_inbuf; Chris@43: RingBuffer **m_outbuf; Chris@43: float **m_mashbuf; Chris@43: float *m_modulationbuf; Chris@43: Chris@43: QMutex *m_mutex; Chris@43: }; Chris@43: Chris@43: #endif