From eece70d89bf930a8e13e6288301c4c0e72021fac Mon Sep 17 00:00:00 2001 From: xenakios Date: Sat, 25 Nov 2017 21:25:46 +0200 Subject: [PATCH 01/10] Add parameters. Use plain old member variables for main volume and loop xfade length. GUI layout tweak. --- Source/PS_Source/StretchSource.cpp | 30 ++++++++++++++++++++++-------- Source/PS_Source/StretchSource.h | 9 +++++++-- Source/PluginEditor.cpp | 2 +- Source/PluginProcessor.cpp | 12 +++++++++--- Source/PluginProcessor.h | 2 +- 5 files changed, 40 insertions(+), 15 deletions(-) diff --git a/Source/PS_Source/StretchSource.cpp b/Source/PS_Source/StretchSource.cpp index 5f72163..04b0384 100644 --- a/Source/PS_Source/StretchSource.cpp +++ b/Source/PS_Source/StretchSource.cpp @@ -129,6 +129,20 @@ void StretchAudioSource::setAudioBufferAsInputSource(AudioBuffer* buf, in setPlayRange({ 0.0,1.0 }, true); } +void StretchAudioSource::setMainVolume(double decibels) +{ + std::lock_guard locker(m_mutex); + m_main_volume = jlimit(-144.0, 12.0, decibels); + ++m_param_change_count; +} + +void StretchAudioSource::setLoopXFadeLength(double lenseconds) +{ + std::lock_guard locker(m_mutex); + m_loopxfadelen = jlimit(0.0, 1.0, lenseconds); + ++m_param_change_count; +} + void StretchAudioSource::getNextAudioBlock(const AudioSourceChannelInfo & bufferToFill) { // for realtime play, this is assumed to be used with BufferingAudioSource, so mutex locking should not be too bad... @@ -145,7 +159,7 @@ void StretchAudioSource::getNextAudioBlock(const AudioSourceChannelInfo & buffer e->set_freezing(m_freezing); } - double maingain = Decibels::decibelsToGain((double)val_MainVolume.getValue()); + double maingain = Decibels::decibelsToGain(m_main_volume); if (m_vol_smoother.getTargetValue() != maingain) m_vol_smoother.setValue(maingain); FloatVectorOperations::disableDenormalisedNumberSupport(); @@ -158,7 +172,7 @@ void StretchAudioSource::getNextAudioBlock(const AudioSourceChannelInfo & buffer return; if (m_inputfile->info.nsamples == 0) return; - m_inputfile->setXFadeLenSeconds(val_XFadeLen.getValue()); + m_inputfile->setXFadeLenSeconds(m_loopxfadelen); double silencethreshold = Decibels::decibelsToGain(-70.0); bool tempfirst = true; @@ -633,8 +647,8 @@ void MultiStretchAudioSource::getNextAudioBlock(const AudioSourceChannelInfo & b m_blocksize = bufferToFill.numSamples; if (m_is_in_switch == false) { - getActiveStretchSource()->val_MainVolume.setValue(val_MainVolume.getValue()); - getActiveStretchSource()->val_XFadeLen.setValue(val_XFadeLen.getValue()); + getActiveStretchSource()->setMainVolume(val_MainVolume.getValue()); + getActiveStretchSource()->setLoopXFadeLength(val_XFadeLen.getValue()); getActiveStretchSource()->setFreezing(m_freezing); getActiveStretchSource()->getNextAudioBlock(bufferToFill); @@ -648,10 +662,10 @@ void MultiStretchAudioSource::getNextAudioBlock(const AudioSourceChannelInfo & b } AudioSourceChannelInfo ascinfo1(m_processbuffers[0]); AudioSourceChannelInfo ascinfo2(m_processbuffers[1]); - m_stretchsources[0]->val_MainVolume.setValue(val_MainVolume.getValue()); - m_stretchsources[1]->val_MainVolume.setValue(val_MainVolume.getValue()); - m_stretchsources[0]->val_XFadeLen.setValue(val_XFadeLen.getValue()); - m_stretchsources[1]->val_XFadeLen.setValue(val_XFadeLen.getValue()); + m_stretchsources[0]->setMainVolume(val_MainVolume.getValue()); + m_stretchsources[1]->setMainVolume(val_MainVolume.getValue()); + m_stretchsources[0]->setLoopXFadeLength(val_XFadeLen.getValue()); + m_stretchsources[1]->setLoopXFadeLength(val_XFadeLen.getValue()); m_stretchsources[0]->setFreezing(m_freezing); m_stretchsources[1]->setFreezing(m_freezing); m_stretchsources[1]->setFFTWindowingType(m_stretchsources[0]->getFFTWindowingType()); diff --git a/Source/PS_Source/StretchSource.h b/Source/PS_Source/StretchSource.h index 6fe9963..e44d5f3 100644 --- a/Source/PS_Source/StretchSource.h +++ b/Source/PS_Source/StretchSource.h @@ -82,8 +82,7 @@ public: void setFFTWindowingType(int windowtype); int getFFTWindowingType() { return m_fft_window_type; } std::pair,Range> getFileCachedRangesNormalized(); - Value val_MainVolume; - Value val_XFadeLen; + ValueTree getStateTree(); void setStateTree(ValueTree state); void setClippingEnabled(bool b) { m_clip_output = b; } @@ -91,6 +90,10 @@ public: void setLoopingEnabled(bool b); void setMaxLoops(int64_t numloops) { m_maxloops = numloops; } void setAudioBufferAsInputSource(AudioBuffer* buf, int sr, int len); + void setMainVolume(double decibels); + double getMainVolume() const { return m_main_volume; } + void setLoopXFadeLength(double lenseconds); + double getLoopXFadeLengtj() const { return m_loopxfadelen; } int m_param_change_count = 0; private: CircularBuffer m_stretchoutringbuf{ 1024 * 1024 }; @@ -107,6 +110,8 @@ private: double m_outsr = 44100.0; int m_process_fftsize = 0; int m_fft_window_type = -1; + double m_main_volume = 0.0; + double m_loopxfadelen = 0.0; ProcessParameters m_ppar; BinauralBeatsParameters m_bbpar; double m_playrate = 1.0; diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index 5ca5007..854a409 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -79,7 +79,7 @@ void PaulstretchpluginAudioProcessorEditor::resized() for (int i = 0; i < m_parcomps.size(); ++i) { - m_parcomps[i]->setBounds(1, 30 + i * 25, 598, 24); + m_parcomps[i]->setBounds(1, 30 + i * 25, getWidth()-2, 24); } int yoffs = m_parcomps.back()->getBottom() + 1; m_wavecomponent.setBounds(1, yoffs, getWidth()-2, getHeight()-1-yoffs); diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index c79cdd4..46747ab 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -12,8 +12,10 @@ #include "PluginEditor.h" #include +#ifdef WIN32 #undef min #undef max +#endif std::set g_activeprocessors; @@ -92,6 +94,8 @@ PaulstretchpluginAudioProcessor::PaulstretchpluginAudioProcessor() addParameter(new AudioParameterFloat("playrange_end0", "Sound end", 0.0f, 1.0f, 1.0f)); // 6 addParameter(new AudioParameterBool("freeze0", "Freeze", false)); // 7 addParameter(new AudioParameterFloat("spread0", "Frequency spread", 0.0f, 1.0f, 0.0f)); // 8 + addParameter(new AudioParameterFloat("compress0", "Compress", 0.0f, 1.0f, 0.0f)); // 9 + addParameter(new AudioParameterFloat("loopxfadelen0", "Loop xfade length", 0.0f, 1.0f, 0.0f)); // 10 } PaulstretchpluginAudioProcessor::~PaulstretchpluginAudioProcessor() @@ -145,7 +149,7 @@ bool PaulstretchpluginAudioProcessor::isMidiEffect() const double PaulstretchpluginAudioProcessor::getTailLengthSeconds() const { - return 0.0; + return (double)m_bufamounts[m_prebuffer_amount]/getSampleRate(); } int PaulstretchpluginAudioProcessor::getNumPrograms() @@ -306,14 +310,16 @@ void PaulstretchpluginAudioProcessor::processBlock (AudioSampleBuffer& buffer, M } jassert(m_buffering_source != nullptr); jassert(m_bufferingthread.isThreadRunning()); - m_stretch_source->val_MainVolume = (float)*getFloatParameter(0); + m_stretch_source->setMainVolume(*getFloatParameter(0)); m_stretch_source->setRate(*getFloatParameter(1)); - m_stretch_source->val_XFadeLen = 0.1; + setFFTSize(*getFloatParameter(2)); m_ppar.pitch_shift.cents = *getFloatParameter(3) * 100.0; m_ppar.freq_shift.Hz = *getFloatParameter(4); m_ppar.spread.enabled = *getFloatParameter(8) > 0.0f; m_ppar.spread.bandwidth = *getFloatParameter(8); + m_ppar.compressor.power = *getFloatParameter(9); + m_stretch_source->setLoopXFadeLength(*getFloatParameter(10)); double t0 = *getFloatParameter(5); double t1 = *getFloatParameter(6); if (t0 > t1) diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h index 34b77f6..25094d6 100644 --- a/Source/PluginProcessor.h +++ b/Source/PluginProcessor.h @@ -73,7 +73,7 @@ private: bool m_ready_to_play = false; AudioBuffer m_recbuffer; - double m_max_reclen = 5; + double m_max_reclen = 10.0; bool m_is_recording = false; int m_rec_pos = 0; void finishRecording(int lenrecorded); From da1fea6850501986d998e4561e250cc64d3fc3c4 Mon Sep 17 00:00:00 2001 From: xenakios Date: Tue, 28 Nov 2017 16:35:15 +0200 Subject: [PATCH 02/10] Check volume and loop xfade length in setters before entering mutex --- Source/PS_Source/StretchSource.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/PS_Source/StretchSource.cpp b/Source/PS_Source/StretchSource.cpp index 04b0384..c00b93c 100644 --- a/Source/PS_Source/StretchSource.cpp +++ b/Source/PS_Source/StretchSource.cpp @@ -131,6 +131,8 @@ void StretchAudioSource::setAudioBufferAsInputSource(AudioBuffer* buf, in void StretchAudioSource::setMainVolume(double decibels) { + if (decibels == m_main_volume) + return; std::lock_guard locker(m_mutex); m_main_volume = jlimit(-144.0, 12.0, decibels); ++m_param_change_count; @@ -138,6 +140,8 @@ void StretchAudioSource::setMainVolume(double decibels) void StretchAudioSource::setLoopXFadeLength(double lenseconds) { + if (lenseconds == m_loopxfadelen) + return; std::lock_guard locker(m_mutex); m_loopxfadelen = jlimit(0.0, 1.0, lenseconds); ++m_param_change_count; From 5f2435665818184bd89c1904a38f99459c31063c Mon Sep 17 00:00:00 2001 From: xenakios Date: Tue, 28 Nov 2017 20:46:04 +0200 Subject: [PATCH 03/10] Init FFT windowing type --- Source/PS_Source/StretchSource.cpp | 1 + Source/PluginProcessor.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/Source/PS_Source/StretchSource.cpp b/Source/PS_Source/StretchSource.cpp index c00b93c..0dcfe91 100644 --- a/Source/PS_Source/StretchSource.cpp +++ b/Source/PS_Source/StretchSource.cpp @@ -563,6 +563,7 @@ void StretchAudioSource::setPlayRange(Range playrange, bool isloop) m_inputfile->seek(m_playrange.getStart()); m_seekpos = m_playrange.getStart(); ++m_param_change_count; + } bool StretchAudioSource::isLoopEnabled() diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index 46747ab..7d184fd 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -84,6 +84,7 @@ PaulstretchpluginAudioProcessor::PaulstretchpluginAudioProcessor() m_ppar.freq_shift.enabled = true; m_stretch_source->setOnsetDetection(0.0); m_stretch_source->setLoopingEnabled(true); + m_stretch_source->setFFTWindowingType(1); addParameter(new AudioParameterFloat("mainvolume0", "Main volume", -24.0f, 12.0f, -3.0f)); // 0 addParameter(new AudioParameterFloat("stretchamount0", "Stretch amount", NormalisableRange(0.1f, 128.0f, 0.01f, 0.5),1.0f)); // 1 From e52241ba8677322272d1c57f1a2237842ed9036f Mon Sep 17 00:00:00 2001 From: xenakios Date: Tue, 28 Nov 2017 21:10:22 +0200 Subject: [PATCH 04/10] Avoid resetting active source play range when changing file. Update xfade cache when changing file. --- Source/PS_Source/Input/AInputS.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/PS_Source/Input/AInputS.h b/Source/PS_Source/Input/AInputS.h index bbbf174..ad347b0 100644 --- a/Source/PS_Source/Input/AInputS.h +++ b/Source/PS_Source/Input/AInputS.h @@ -74,7 +74,9 @@ public: std::lock_guard locker(m_mutex); m_using_memory_buffer = false; m_afreader = std::unique_ptr(reader); - m_currentsample = 0; + if (m_activerange.isEmpty()) + m_activerange = { 0.0,1.0 }; + m_currentsample = m_activerange.getStart()*info.nsamples; info.samplerate = (int)m_afreader->sampleRate; info.nchannels = m_afreader->numChannels; info.nsamples = m_afreader->lengthInSamples; @@ -83,6 +85,7 @@ public: m_readbuf.setSize(info.nchannels, m_readbuf.getNumSamples()); m_crossfadebuf.setSize(info.nchannels, m_crossfadebuf.getNumSamples()); } + updateXFadeCache(); m_readbuf.clear(); return true; } From 4a2cc8ff7154fcbb4d6e68102e32b7720dec2acb Mon Sep 17 00:00:00 2001 From: xenakios Date: Sun, 3 Dec 2017 18:39:47 +0200 Subject: [PATCH 05/10] Initial work to separate spectrum processing implementations into stand alone functions from the ProcessedStretch class, to allow visualizations etc without having to instantiate the full ProcessedStretch object --- Source/PS_Source/ProcessedStretch.cpp | 9 -- Source/PS_Source/ProcessedStretch.h | 148 ++++++++++++++++++++++++++ 2 files changed, 148 insertions(+), 9 deletions(-) diff --git a/Source/PS_Source/ProcessedStretch.cpp b/Source/PS_Source/ProcessedStretch.cpp index 13291ba..3a26779 100644 --- a/Source/PS_Source/ProcessedStretch.cpp +++ b/Source/PS_Source/ProcessedStretch.cpp @@ -174,15 +174,6 @@ void ProcessedStretch::process_spectrum(REALTYPE *freq) //void ProcessedStretch::process_output(REALTYPE *smps,int nsmps){ //}; - -REALTYPE profile(REALTYPE fi, REALTYPE bwi){ - REALTYPE x=fi/bwi; - x*=x; - if (x>14.71280603) return 0.0; - return exp(-x);///bwi; - -}; - void ProcessedStretch::do_harmonics(REALTYPE *freq1,REALTYPE *freq2){ REALTYPE freq=pars.harmonics.freq; REALTYPE bandwidth=pars.harmonics.bandwidth; diff --git a/Source/PS_Source/ProcessedStretch.h b/Source/PS_Source/ProcessedStretch.h index 056b194..3a12640 100644 --- a/Source/PS_Source/ProcessedStretch.h +++ b/Source/PS_Source/ProcessedStretch.h @@ -180,6 +180,154 @@ struct ProcessParameters } }; +inline REALTYPE profile(REALTYPE fi, REALTYPE bwi) { + REALTYPE x = fi / bwi; + x *= x; + if (x>14.71280603) return 0.0; + return exp(-x);///bwi; + +}; + +inline void spectrum_do_harmonics(ProcessParameters& pars, std::vector& tmpfreq1, int nfreq, double samplerate, REALTYPE *freq1, REALTYPE *freq2) { + REALTYPE freq = pars.harmonics.freq; + REALTYPE bandwidth = pars.harmonics.bandwidth; + int nharmonics = pars.harmonics.nharmonics; + + if (freq<10.0) freq = 10.0; + + REALTYPE *amp = tmpfreq1.data(); + for (int i = 0; i= samplerate / 2) break; + + bw_Hz = (pow(2.0f, bandwidth / 1200.0f) - 1.0f)*f; + bwi = bw_Hz / (2.0f*samplerate); + fi = f / samplerate; + + REALTYPE sum = 0.0f; + REALTYPE max = 0.0f; + for (int i = 1; imax) max = amp[i]; + }; + if (max<1e-8f) max = 1e-8f; + + for (int i = 1; i0) && (i2= nfreq) break; + freq2[i2] += freq1[i]; + }; + }; + if (_rap >= 1.0) {//up + _rap = 1.0f / _rap; + for (int i = 0; i& sumfreq, + std::vector& tmpfreq1, + REALTYPE *freq1, REALTYPE *freq2) { + spectrum_zero(nfreq,sumfreq.data()); + if (pars.octave.om2>1e-3) { + spectrum_do_pitch_shift(pars,nfreq, freq1, tmpfreq1.data(), 0.25); + spectrum_add(nfreq, sumfreq.data(), tmpfreq1.data(), pars.octave.om2); + }; + if (pars.octave.om1>1e-3) { + spectrum_do_pitch_shift(pars,nfreq, freq1, tmpfreq1.data(), 0.5); + spectrum_add(nfreq,sumfreq.data(), tmpfreq1.data(), pars.octave.om1); + }; + if (pars.octave.o0>1e-3) { + spectrum_add(nfreq,sumfreq.data(), freq1, pars.octave.o0); + }; + if (pars.octave.o1>1e-3) { + spectrum_do_pitch_shift(pars,nfreq, freq1, tmpfreq1.data(), 2.0); + spectrum_add(nfreq,sumfreq.data(), tmpfreq1.data(), pars.octave.o1); + }; + if (pars.octave.o15>1e-3) { + spectrum_do_pitch_shift(pars,nfreq, freq1, tmpfreq1.data(), 3.0); + spectrum_add(nfreq,sumfreq.data(), tmpfreq1.data(), pars.octave.o15); + }; + if (pars.octave.o2>1e-3) { + spectrum_do_pitch_shift(pars, nfreq, freq1, tmpfreq1.data(), 4.0); + spectrum_add(nfreq,sumfreq.data(), tmpfreq1.data(), pars.octave.o2); + }; + + REALTYPE sum = 0.01f + pars.octave.om2 + pars.octave.om1 + pars.octave.o0 + pars.octave.o1 + pars.octave.o15 + pars.octave.o2; + if (sum<0.5f) sum = 0.5f; + for (int i = 0; i= ilow) && (i Date: Sun, 3 Dec 2017 19:09:42 +0200 Subject: [PATCH 06/10] All/most spectral processing stages now in stand alone functions --- Source/PS_Source/ProcessedStretch.h | 103 ++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/Source/PS_Source/ProcessedStretch.h b/Source/PS_Source/ProcessedStretch.h index 3a12640..ea28951 100644 --- a/Source/PS_Source/ProcessedStretch.h +++ b/Source/PS_Source/ProcessedStretch.h @@ -188,6 +188,109 @@ inline REALTYPE profile(REALTYPE fi, REALTYPE bwi) { }; +inline void spectrum_spread(int nfreq, double samplerate, + std::vector& tmpfreq1, + REALTYPE *freq1, REALTYPE *freq2, REALTYPE spread_bandwidth) { + //convert to log spectrum + REALTYPE minfreq = 20.0f; + REALTYPE maxfreq = 0.5f*samplerate; + + REALTYPE log_minfreq = log(minfreq); + REALTYPE log_maxfreq = log(maxfreq); + + for (int i = 0; i= nfreq) x0 = nfreq - 1; + int x1 = x0 + 1; if (x1 >= nfreq) x1 = nfreq - 1; + REALTYPE xp = x - x0; + if (x0; i--) { + tmpfreq1[i] = tmpfreq1[i + 1] * a + tmpfreq1[i] * (1.0f - a); + }; + }; + + freq2[0] = 0; + REALTYPE log_maxfreq_d_minfreq = log(maxfreq / minfreq); + for (int i = 1; i0.0) && (x= nfreq) x0 = nfreq - 1; + int x1 = x0 + 1; if (x1 >= nfreq) x1 = nfreq - 1; + REALTYPE xp = x - x0; + y = tmpfreq1[x0] * (1.0f - xp) + tmpfreq1[x1] * xp; + }; + freq2[i] = y; + }; + + +}; + + +inline void spectrum_do_compressor(ProcessParameters& pars, int nfreq, REALTYPE *freq1, REALTYPE *freq2) { + REALTYPE rms = 0.0; + for (int i = 0; i& tmpfreq1, + REALTYPE *freq1, REALTYPE *freq2) { + spectrum_spread(nfreq, samplerate, tmpfreq1, freq1, tmpfreq1.data(), pars.tonal_vs_noise.bandwidth); + + if (pars.tonal_vs_noise.preserve >= 0.0) { + REALTYPE mul = (pow(10.0f, pars.tonal_vs_noise.preserve) - 1.0f); + for (int i = 0; i& tmpfreq1, int nfreq, double samplerate, REALTYPE *freq1, REALTYPE *freq2) { REALTYPE freq = pars.harmonics.freq; REALTYPE bandwidth = pars.harmonics.bandwidth; From 16868d5ccddd91965379dae6fb0db321a3e03fc8 Mon Sep 17 00:00:00 2001 From: xenakios Date: Sun, 3 Dec 2017 22:00:40 +0200 Subject: [PATCH 07/10] Allow FFT object to be constructed without inverse FFT. Added SpectralVisualizer component that does an extremely inefficient visualization of the spectral processing stages. --- Source/PS_Source/Stretch.cpp | 11 ++++--- Source/PS_Source/Stretch.h | 2 +- Source/PluginEditor.cpp | 64 ++++++++++++++++++++++++++++++++++-- Source/PluginEditor.h | 12 +++++++ 4 files changed, 82 insertions(+), 7 deletions(-) diff --git a/Source/PS_Source/Stretch.cpp b/Source/PS_Source/Stretch.cpp index 6d929a2..df2bcef 100644 --- a/Source/PS_Source/Stretch.cpp +++ b/Source/PS_Source/Stretch.cpp @@ -20,7 +20,7 @@ #include #include -FFT::FFT(int nsamples_) +FFT::FFT(int nsamples_, bool no_inverse) { nsamples=nsamples_; if (nsamples%2!=0) { @@ -43,13 +43,15 @@ FFT::FFT(int nsamples_) { //fftwf_plan_with_nthreads(2); planfftw=fftwf_plan_r2r_1d(nsamples,data.data(),data.data(),FFTW_R2HC,FFTW_MEASURE); - planifftw=fftwf_plan_r2r_1d(nsamples,data.data(),data.data(),FFTW_HC2R,FFTW_MEASURE); + if (no_inverse == false) + planifftw=fftwf_plan_r2r_1d(nsamples,data.data(),data.data(),FFTW_HC2R,FFTW_MEASURE); } else { //fftwf_plan_with_nthreads(2); planfftw=fftwf_plan_r2r_1d(nsamples,data.data(),data.data(),FFTW_R2HC,FFTW_ESTIMATE); //fftwf_plan_with_nthreads(2); - planifftw=fftwf_plan_r2r_1d(nsamples,data.data(),data.data(),FFTW_HC2R,FFTW_ESTIMATE); + if (no_inverse == false) + planifftw=fftwf_plan_r2r_1d(nsamples,data.data(),data.data(),FFTW_HC2R,FFTW_ESTIMATE); } //double t1 = Time::getMillisecondCounterHiRes(); //Logger::writeToLog("Creating FFTW3 plans took "+String(t1-t0)+ "ms"); @@ -64,7 +66,8 @@ FFT::FFT(int nsamples_) FFT::~FFT() { fftwf_destroy_plan(planfftw); - fftwf_destroy_plan(planifftw); + if (planifftw!=nullptr) + fftwf_destroy_plan(planifftw); }; void FFT::smp2freq() diff --git a/Source/PS_Source/Stretch.h b/Source/PS_Source/Stretch.h index ec3747f..4ac90b9 100644 --- a/Source/PS_Source/Stretch.h +++ b/Source/PS_Source/Stretch.h @@ -122,7 +122,7 @@ enum FFTWindow{W_RECTANGULAR,W_HAMMING,W_HANN,W_BLACKMAN,W_BLACKMAN_HARRIS}; class FFT {//FFT class that considers phases as random public: - FFT(int nsamples_);//samples must be even + FFT(int nsamples_, bool no_inverse=false);//samples must be even ~FFT(); void smp2freq();//input is smp, output is freq (phases are discarded) void freq2smp();//input is freq,output is smp (phases are random) diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index 854a409..4d5b1fd 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -42,7 +42,7 @@ PaulstretchpluginAudioProcessorEditor::PaulstretchpluginAudioProcessorEditor (Pa addAndMakeVisible(&m_rec_enable); m_rec_enable.setButtonText("Capture"); attachCallback(m_rec_enable, [this]() { processor.setRecordingEnabled(m_rec_enable.getToggleState()); }); - + addAndMakeVisible(&m_specvis); setSize (700, 30+pars.size()*25+200); m_wavecomponent.TimeSelectionChangedCallback = [this](Range range, int which) { @@ -56,6 +56,7 @@ PaulstretchpluginAudioProcessorEditor::PaulstretchpluginAudioProcessorEditor (Pa m_wavecomponent.ShowFileCacheRange = true; startTimer(1, 100); startTimer(2, 1000); + startTimer(3, 500); m_wavecomponent.startTimer(100); } @@ -82,7 +83,8 @@ void PaulstretchpluginAudioProcessorEditor::resized() m_parcomps[i]->setBounds(1, 30 + i * 25, getWidth()-2, 24); } int yoffs = m_parcomps.back()->getBottom() + 1; - m_wavecomponent.setBounds(1, yoffs, getWidth()-2, getHeight()-1-yoffs); + //m_wavecomponent.setBounds(1, yoffs, getWidth()-2, getHeight()-1-yoffs); + m_specvis.setBounds(1, yoffs, getWidth() - 2, getHeight() - 1 - yoffs); } void PaulstretchpluginAudioProcessorEditor::timerCallback(int id) @@ -108,6 +110,12 @@ void PaulstretchpluginAudioProcessorEditor::timerCallback(int id) m_wavecomponent.setAudioFile(processor.getAudioFile()); } m_wavecomponent.setTimeSelection(processor.getTimeSelection()); + + } + if (id == 3) + { + m_specvis.setState(processor.getStretchSource()->getProcessParameters(), processor.getStretchSource()->getFFTSize() / 2, + processor.getSampleRate()); } } @@ -431,3 +439,55 @@ int WaveformComponent::getTimeSelectionEdge(int x, int y) return 0; } +SpectralVisualizer::SpectralVisualizer() +{ + m_img = Image(Image::RGB, 500, 200, true); +} + +void SpectralVisualizer::setState(ProcessParameters & pars, int nfreqs, double samplerate) +{ + m_img = Image(Image::RGB, getWidth(), getHeight(), true); + std::vector insamples(nfreqs*2); + std::vector freqs1(nfreqs*2); + std::vector freqs2(nfreqs*2); + std::vector freqs3(nfreqs*2); + double hz = 440.0; + int numharmonics = 40; + double scaler = 1.0 / numharmonics; + for (int i = 0; i < nfreqs; ++i) + { + for (int j = 0; j < numharmonics; ++j) + { + double oscgain = 1.0 - (1.0 / numharmonics)*j; + insamples[i] += scaler * oscgain * sin(2 * 3.141592653 / samplerate * i* (hz+hz*j)); + } + } + FFT fft(nfreqs*2); + for (int i = 0; i < nfreqs; ++i) + { + fft.smp[i] = insamples[i]; + } + fft.applywindow(W_HAMMING); + fft.smp2freq(); + double ratio = pow(2.0f, pars.pitch_shift.cents / 1200.0f); + spectrum_do_pitch_shift(pars, nfreqs, fft.freq.data(), freqs2.data(), ratio); + spectrum_do_freq_shift(pars, nfreqs, samplerate, freqs2.data(), freqs1.data()); + spectrum_do_compressor(pars, nfreqs, freqs1.data(), freqs2.data()); + spectrum_spread(nfreqs, samplerate, freqs3, freqs2.data(), freqs1.data(), pars.spread.bandwidth); + Graphics g(m_img); + g.setColour(Colours::white); + for (int i = 0; i < nfreqs; ++i) + { + double binfreq = (samplerate / 2 / nfreqs)*i; + double xcor = jmap(binfreq, 0.0, samplerate / 2.0, 0.0, getWidth()); + double ycor = getHeight()- jmap(freqs1[i], 0.0, nfreqs/64, 0.0, getHeight()); + ycor = jlimit(0.0, getHeight(), ycor); + g.drawLine(xcor, getHeight(), xcor, ycor, 1.0); + } + repaint(); +} + +void SpectralVisualizer::paint(Graphics & g) +{ + g.drawImage(m_img, 0, 0, getWidth(), getHeight(), 0, 0, m_img.getWidth(), m_img.getHeight()); +} diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h index f6c300f..8c07a4f 100644 --- a/Source/PluginEditor.h +++ b/Source/PluginEditor.h @@ -15,6 +15,17 @@ #include #include +class SpectralVisualizer : public Component +{ +public: + SpectralVisualizer(); + void setState(ProcessParameters& pars, int nfreqs, double samplerate); + void paint(Graphics& g) override; + +private: + Image m_img; +}; + inline void attachCallback(Button& button, std::function callback) { struct ButtonCallback : public Button::Listener, @@ -240,6 +251,7 @@ public: private: PaulstretchpluginAudioProcessor& processor; std::vector> m_parcomps; + SpectralVisualizer m_specvis; ToggleButton m_rec_enable; TextButton m_import_button; Label m_info_label; From ccf7ba303f7c0eff298c28e078b0ed075f1a75ea Mon Sep 17 00:00:00 2001 From: xenakios Date: Sun, 3 Dec 2017 22:51:57 +0200 Subject: [PATCH 08/10] SpectralVisualizer optimizations, create objects only when needed etc --- Source/PluginEditor.cpp | 57 +++++++++++++++++++++++++++-------------- Source/PluginEditor.h | 4 +++ 2 files changed, 42 insertions(+), 19 deletions(-) diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index 4d5b1fd..4588856 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -56,7 +56,7 @@ PaulstretchpluginAudioProcessorEditor::PaulstretchpluginAudioProcessorEditor (Pa m_wavecomponent.ShowFileCacheRange = true; startTimer(1, 100); startTimer(2, 1000); - startTimer(3, 500); + startTimer(3, 200); m_wavecomponent.startTimer(100); } @@ -446,48 +446,67 @@ SpectralVisualizer::SpectralVisualizer() void SpectralVisualizer::setState(ProcessParameters & pars, int nfreqs, double samplerate) { - m_img = Image(Image::RGB, getWidth(), getHeight(), true); - std::vector insamples(nfreqs*2); - std::vector freqs1(nfreqs*2); - std::vector freqs2(nfreqs*2); - std::vector freqs3(nfreqs*2); + double t0 = Time::getMillisecondCounterHiRes(); double hz = 440.0; int numharmonics = 40; double scaler = 1.0 / numharmonics; - for (int i = 0; i < nfreqs; ++i) + if (m_img.getWidth()!=getWidth() || m_img.getHeight()!=getHeight()) + m_img = Image(Image::RGB, getWidth(), getHeight(), true); + if (m_nfreqs == 0 || nfreqs != m_nfreqs) { - for (int j = 0; j < numharmonics; ++j) + m_nfreqs = nfreqs; + m_insamples = std::vector(nfreqs * 2); + m_freqs1 = std::vector(nfreqs); + m_freqs2 = std::vector(nfreqs); + m_freqs3 = std::vector(nfreqs); + m_fft = std::make_unique(nfreqs*2); + std::fill(m_insamples.begin(), m_insamples.end(), 0.0f); + for (int i = 0; i < nfreqs; ++i) { - double oscgain = 1.0 - (1.0 / numharmonics)*j; - insamples[i] += scaler * oscgain * sin(2 * 3.141592653 / samplerate * i* (hz+hz*j)); + for (int j = 0; j < numharmonics; ++j) + { + double oscgain = 1.0 - (1.0 / numharmonics)*j; + m_insamples[i] += scaler * oscgain * sin(2 * 3.141592653 / samplerate * i* (hz + hz * j)); + } } } - FFT fft(nfreqs*2); + + //std::fill(m_freqs1.begin(), m_freqs1.end(), 0.0f); + //std::fill(m_freqs2.begin(), m_freqs2.end(), 0.0f); + //std::fill(m_freqs3.begin(), m_freqs3.end(), 0.0f); + //std::fill(m_fft->freq.begin(), m_fft->freq.end(), 0.0f); + + for (int i = 0; i < nfreqs; ++i) { - fft.smp[i] = insamples[i]; + m_fft->smp[i] = m_insamples[i]; } - fft.applywindow(W_HAMMING); - fft.smp2freq(); + m_fft->applywindow(W_HAMMING); + m_fft->smp2freq(); double ratio = pow(2.0f, pars.pitch_shift.cents / 1200.0f); - spectrum_do_pitch_shift(pars, nfreqs, fft.freq.data(), freqs2.data(), ratio); - spectrum_do_freq_shift(pars, nfreqs, samplerate, freqs2.data(), freqs1.data()); - spectrum_do_compressor(pars, nfreqs, freqs1.data(), freqs2.data()); - spectrum_spread(nfreqs, samplerate, freqs3, freqs2.data(), freqs1.data(), pars.spread.bandwidth); + spectrum_do_pitch_shift(pars, nfreqs, m_fft->freq.data(), m_freqs2.data(), ratio); + spectrum_do_freq_shift(pars, nfreqs, samplerate, m_freqs2.data(), m_freqs1.data()); + spectrum_do_compressor(pars, nfreqs, m_freqs1.data(), m_freqs2.data()); + spectrum_spread(nfreqs, samplerate, m_freqs3, m_freqs2.data(), m_freqs1.data(), pars.spread.bandwidth); Graphics g(m_img); + g.fillAll(Colours::black); g.setColour(Colours::white); for (int i = 0; i < nfreqs; ++i) { double binfreq = (samplerate / 2 / nfreqs)*i; double xcor = jmap(binfreq, 0.0, samplerate / 2.0, 0.0, getWidth()); - double ycor = getHeight()- jmap(freqs1[i], 0.0, nfreqs/64, 0.0, getHeight()); + double ycor = getHeight()- jmap(m_freqs1[i], 0.0, nfreqs/64, 0.0, getHeight()); ycor = jlimit(0.0, getHeight(), ycor); g.drawLine(xcor, getHeight(), xcor, ycor, 1.0); } + double t1 = Time::getMillisecondCounterHiRes(); + m_elapsed = t1 - t0; repaint(); } void SpectralVisualizer::paint(Graphics & g) { g.drawImage(m_img, 0, 0, getWidth(), getHeight(), 0, 0, m_img.getWidth(), m_img.getHeight()); + g.setColour(Colours::yellow); + g.drawText(String(m_elapsed, 1)+" ms", 1, 1, getWidth(), 30, Justification::topLeft); } diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h index 8c07a4f..420573e 100644 --- a/Source/PluginEditor.h +++ b/Source/PluginEditor.h @@ -24,6 +24,10 @@ public: private: Image m_img; + std::vector m_insamples,m_freqs1, m_freqs2, m_freqs3; + std::unique_ptr m_fft; + int m_nfreqs = 0; + double m_elapsed = 0.0; }; inline void attachCallback(Button& button, std::function callback) From 75be9947141e2eee4a13b9ef2767e5f1f62ae0a7 Mon Sep 17 00:00:00 2001 From: xenakios Date: Sun, 3 Dec 2017 23:28:54 +0200 Subject: [PATCH 09/10] Constness --- Source/PS_Source/ProcessedStretch.h | 14 +++++++------- Source/PS_Source/StretchSource.cpp | 2 +- Source/PS_Source/StretchSource.h | 2 +- Source/PluginEditor.cpp | 2 +- Source/PluginEditor.h | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Source/PS_Source/ProcessedStretch.h b/Source/PS_Source/ProcessedStretch.h index ea28951..1ed4360 100644 --- a/Source/PS_Source/ProcessedStretch.h +++ b/Source/PS_Source/ProcessedStretch.h @@ -247,7 +247,7 @@ inline void spectrum_spread(int nfreq, double samplerate, }; -inline void spectrum_do_compressor(ProcessParameters& pars, int nfreq, REALTYPE *freq1, REALTYPE *freq2) { +inline void spectrum_do_compressor(const ProcessParameters& pars, int nfreq, REALTYPE *freq1, REALTYPE *freq2) { REALTYPE rms = 0.0; for (int i = 0; i& tmpfreq1, REALTYPE *freq1, REALTYPE *freq2) { spectrum_spread(nfreq, samplerate, tmpfreq1, freq1, tmpfreq1.data(), pars.tonal_vs_noise.bandwidth); @@ -291,7 +291,7 @@ inline void spectrum_do_tonal_vs_noise(ProcessParameters& pars, int nfreq, doubl }; -inline void spectrum_do_harmonics(ProcessParameters& pars, std::vector& tmpfreq1, int nfreq, double samplerate, REALTYPE *freq1, REALTYPE *freq2) { +inline void spectrum_do_harmonics(const ProcessParameters& pars, std::vector& tmpfreq1, int nfreq, double samplerate, REALTYPE *freq1, REALTYPE *freq2) { REALTYPE freq = pars.harmonics.freq; REALTYPE bandwidth = pars.harmonics.bandwidth; int nharmonics = pars.harmonics.nharmonics; @@ -347,7 +347,7 @@ inline void spectrum_zero(int nfreq,REALTYPE *freq1) { for (int i = 0; i& sumfreq, std::vector& tmpfreq1, REALTYPE *freq1, REALTYPE *freq2) { @@ -408,7 +408,7 @@ inline void spectrum_do_octave(ProcessParameters& pars, int nfreq, double sample for (int i = 0; i Date: Sun, 3 Dec 2017 23:47:28 +0200 Subject: [PATCH 10/10] Add harmonics processing parameters. Add harmonics processing visualization etc --- Source/PS_Source/ProcessedStretch.cpp | 4 ++-- Source/PS_Source/ProcessedStretch.h | 6 ++++++ Source/PS_Source/Stretch.h | 2 +- Source/PluginEditor.cpp | 5 ++++- Source/PluginProcessor.cpp | 5 +++++ 5 files changed, 18 insertions(+), 4 deletions(-) diff --git a/Source/PS_Source/ProcessedStretch.cpp b/Source/PS_Source/ProcessedStretch.cpp index 3a26779..e4f4d4c 100644 --- a/Source/PS_Source/ProcessedStretch.cpp +++ b/Source/PS_Source/ProcessedStretch.cpp @@ -104,9 +104,9 @@ void ProcessedStretch::process_spectrum(REALTYPE *freq) { for (auto& e : m_spectrum_processes) { - copy(freq, infreq.data()); + spectrum_copy(nfreq, freq, infreq.data()); if (e == 0 && pars.harmonics.enabled) - do_harmonics(infreq.data(), freq); + spectrum_do_harmonics(pars, tmpfreq1, nfreq, samplerate, infreq.data(), freq); if (e == 1 && pars.tonal_vs_noise.enabled) do_tonal_vs_noise(infreq.data(), freq); if (e == 2 && pars.freq_shift.enabled) diff --git a/Source/PS_Source/ProcessedStretch.h b/Source/PS_Source/ProcessedStretch.h index 1ed4360..6269bbd 100644 --- a/Source/PS_Source/ProcessedStretch.h +++ b/Source/PS_Source/ProcessedStretch.h @@ -188,6 +188,12 @@ inline REALTYPE profile(REALTYPE fi, REALTYPE bwi) { }; +inline void spectrum_copy(int nfreq, REALTYPE* freq1, REALTYPE* freq2) +{ + for (int i = 0; i& tmpfreq1, REALTYPE *freq1, REALTYPE *freq2, REALTYPE spread_bandwidth) { diff --git a/Source/PS_Source/Stretch.h b/Source/PS_Source/Stretch.h index 4ac90b9..80e87fe 100644 --- a/Source/PS_Source/Stretch.h +++ b/Source/PS_Source/Stretch.h @@ -192,7 +192,7 @@ class Stretch virtual void process_spectrum(REALTYPE *){}; virtual REALTYPE get_stretch_multiplier(REALTYPE pos_percents); - REALTYPE samplerate; + REALTYPE samplerate=0.0f; private: diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index 520092d..69f4ecd 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -488,6 +488,9 @@ void SpectralVisualizer::setState(const ProcessParameters & pars, int nfreqs, do spectrum_do_freq_shift(pars, nfreqs, samplerate, m_freqs2.data(), m_freqs1.data()); spectrum_do_compressor(pars, nfreqs, m_freqs1.data(), m_freqs2.data()); spectrum_spread(nfreqs, samplerate, m_freqs3, m_freqs2.data(), m_freqs1.data(), pars.spread.bandwidth); + if (pars.harmonics.enabled) + spectrum_do_harmonics(pars, m_freqs3, nfreqs, samplerate, m_freqs1.data(), m_freqs2.data()); + else spectrum_copy(nfreqs, m_freqs1.data(), m_freqs2.data()); Graphics g(m_img); g.fillAll(Colours::black); g.setColour(Colours::white); @@ -495,7 +498,7 @@ void SpectralVisualizer::setState(const ProcessParameters & pars, int nfreqs, do { double binfreq = (samplerate / 2 / nfreqs)*i; double xcor = jmap(binfreq, 0.0, samplerate / 2.0, 0.0, getWidth()); - double ycor = getHeight()- jmap(m_freqs1[i], 0.0, nfreqs/64, 0.0, getHeight()); + double ycor = getHeight()- jmap(m_freqs2[i], 0.0, nfreqs/128, 0.0, getHeight()); ycor = jlimit(0.0, getHeight(), ycor); g.drawLine(xcor, getHeight(), xcor, ycor, 1.0); } diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index 7d184fd..06357b6 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -97,6 +97,8 @@ PaulstretchpluginAudioProcessor::PaulstretchpluginAudioProcessor() addParameter(new AudioParameterFloat("spread0", "Frequency spread", 0.0f, 1.0f, 0.0f)); // 8 addParameter(new AudioParameterFloat("compress0", "Compress", 0.0f, 1.0f, 0.0f)); // 9 addParameter(new AudioParameterFloat("loopxfadelen0", "Loop xfade length", 0.0f, 1.0f, 0.0f)); // 10 + addParameter(new AudioParameterFloat("numharmonics0", "Num harmonics", 0.0f, 100.0f, 0.0f)); // 11 + addParameter(new AudioParameterFloat("harmonicsfreq0", "Harmonics base freq", 1.0f, 5000.0f, 100.0f)); // 12 } PaulstretchpluginAudioProcessor::~PaulstretchpluginAudioProcessor() @@ -320,6 +322,9 @@ void PaulstretchpluginAudioProcessor::processBlock (AudioSampleBuffer& buffer, M m_ppar.spread.enabled = *getFloatParameter(8) > 0.0f; m_ppar.spread.bandwidth = *getFloatParameter(8); m_ppar.compressor.power = *getFloatParameter(9); + m_ppar.harmonics.enabled = *getFloatParameter(11)>=1.0; + m_ppar.harmonics.nharmonics = *getFloatParameter(11); + m_ppar.harmonics.freq = *getFloatParameter(12); m_stretch_source->setLoopXFadeLength(*getFloatParameter(10)); double t0 = *getFloatParameter(5); double t1 = *getFloatParameter(6);