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;