paulxstretch/Source/PS_Source/ProcessedStretch.h

545 lines
14 KiB
C++

/*
Copyright (C) 2011 Nasca Octavian Paul
Author: Nasca Octavian Paul
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License (version 2) for more details.
You should have received a copy of the GNU General Public License (version 2)
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#pragma once
#include "Stretch.h"
#include <array>
#include "../jcdp_envelope.h"
struct ProcessParameters
{
ProcessParameters()
{
pitch_shift.cents=0;
ratiomix.ratios = { 0.25,0.5,1.0,2.0,3.0,4.0,1.5,1.0/1.5 };
octave.om2=octave.om1=octave.o1=octave.o15=octave.o2=0.0f;
octave.o0=1.0f;
freq_shift.Hz=0;
compressor.power=0.0f;
filter.stop=false;
filter.low=0.0f;
filter.high=22000.0f;
filter.hdamp=0.0f;
harmonics.freq=440.0f;
harmonics.bandwidth=25.0f;
harmonics.nharmonics=10;
harmonics.gauss=false;
spread.bandwidth=0.3f;
tonal_vs_noise.preserve=0.5f;
tonal_vs_noise.bandwidth=0.9f;
};
~ProcessParameters(){
};
struct{
int cents;
}pitch_shift;
struct{
REALTYPE om2,om1,o0,o1,o15,o2;
}octave;
struct
{
std::array<double, 8> ratios;
std::array<double, 8> ratiolevels;
} ratiomix;
struct{
int Hz;
}freq_shift;
struct{
REALTYPE power;
}compressor;
struct{
REALTYPE low,high;
REALTYPE hdamp;
bool stop;
}filter;
struct{
REALTYPE freq;
REALTYPE bandwidth;
int nharmonics;
bool gauss;
}harmonics;
struct{
REALTYPE bandwidth;
}spread;
struct{
REALTYPE preserve;
REALTYPE bandwidth;
}tonal_vs_noise;
//FreeEdit free_filter;
//FreeEdit stretch_multiplier;
/*
auto getMembers() const
{
return std::make_tuple(pitch_shift.enabled,
pitch_shift.cents,
octave.enabled,
octave.o0,
octave.o1,
octave.o15,
octave.o2,
octave.om1,
octave.om2,
spread.enabled,
spread.bandwidth,
tonal_vs_noise.enabled,
tonal_vs_noise.bandwidth,
tonal_vs_noise.preserve,
freq_shift.enabled,
freq_shift.Hz,
compressor.enabled,
compressor.power,
harmonics.bandwidth,
harmonics.enabled,
harmonics.freq,
harmonics.gauss,
harmonics.nharmonics,
filter.enabled,
filter.hdamp,
filter.high,
filter.low,
filter.stop);
}
bool operator == (const ProcessParameters& other) const
{
return getMembers() == other.getMembers();
}
*/
bool operator == (const ProcessParameters& other) const noexcept
{
return pitch_shift.cents == other.pitch_shift.cents &&
octave.o0 == other.octave.o0 &&
octave.o1 == other.octave.o1 &&
octave.o15 == other.octave.o15 &&
octave.o2 == other.octave.o2 &&
octave.om1 == other.octave.om1 &&
octave.om2 == other.octave.om2 &&
spread.bandwidth == other.spread.bandwidth &&
tonal_vs_noise.bandwidth == other.tonal_vs_noise.bandwidth &&
tonal_vs_noise.preserve == other.tonal_vs_noise.preserve &&
freq_shift.Hz == other.freq_shift.Hz &&
compressor.power == other.compressor.power &&
harmonics.bandwidth == other.harmonics.bandwidth &&
harmonics.freq == other.harmonics.freq &&
harmonics.gauss == other.harmonics.gauss &&
harmonics.nharmonics == other.harmonics.nharmonics &&
filter.hdamp == other.filter.hdamp &&
filter.high == other.filter.high &&
filter.low == other.filter.low &&
filter.stop == other.filter.stop &&
ratiomix.ratiolevels == other.ratiomix.ratiolevels &&
ratiomix.ratios == other.ratiomix.ratios;
}
};
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_copy(int nfreq, REALTYPE* freq1, REALTYPE* freq2)
{
for (int i = 0; i<nfreq; i++) freq2[i] = freq1[i];
};
inline void spectrum_spread(int nfreq, double samplerate,
std::vector<REALTYPE>& 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; i++) {
REALTYPE freqx = i / (REALTYPE)nfreq;
REALTYPE x = exp(log_minfreq + freqx * (log_maxfreq - log_minfreq)) / maxfreq * nfreq;
REALTYPE y = 0.0f;
int x0 = (int)floor(x); if (x0 >= nfreq) x0 = nfreq - 1;
int x1 = x0 + 1; if (x1 >= nfreq) x1 = nfreq - 1;
REALTYPE xp = x - x0;
if (x<nfreq) {
y = freq1[x0] * (1.0f - xp) + freq1[x1] * xp;
};
tmpfreq1[i] = y;
};
//increase the bandwidth of each harmonic (by smoothing the log spectrum)
int n = 2;
REALTYPE bandwidth = spread_bandwidth;
REALTYPE a = 1.0f - pow(2.0f, -bandwidth * bandwidth*10.0f);
a = pow(a, 8192.0f / nfreq * n);
for (int k = 0; k<n; k++) {
tmpfreq1[0] = 0.0f;
for (int i = 1; i<nfreq; i++) {
tmpfreq1[i] = tmpfreq1[i - 1] * a + tmpfreq1[i] * (1.0f - a);
};
tmpfreq1[nfreq - 1] = 0.0f;
for (int i = nfreq - 2; i>0; 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; i<nfreq; i++) {
REALTYPE freqx = i / (REALTYPE)nfreq;
REALTYPE x = log((freqx*maxfreq) / minfreq) / log_maxfreq_d_minfreq * nfreq;
REALTYPE y = 0.0;
if ((x>0.0) && (x<nfreq)) {
int x0 = (int)floor(x); if (x0 >= 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(const ProcessParameters& pars, int nfreq, REALTYPE *freq1, REALTYPE *freq2) {
REALTYPE rms = 0.0;
for (int i = 0; i<nfreq; i++) rms += freq1[i] * freq1[i];
rms = sqrt(rms / nfreq)*0.1f;
if (rms<1e-3f) rms = 1e-3f;
REALTYPE _rap = pow(rms, -pars.compressor.power);
for (int i = 0; i<nfreq; i++) freq2[i] = freq1[i] * _rap;
};
inline void spectrum_do_tonal_vs_noise(const ProcessParameters& pars, int nfreq, double samplerate,
std::vector<REALTYPE>& 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<nfreq; i++) {
REALTYPE x = freq1[i];
REALTYPE smooth_x = tmpfreq1[i] + 1e-6f;
REALTYPE result = 0.0f;
result = x - smooth_x * mul;
if (result<0.0f) result = 0.0f;
freq2[i] = result;
};
}
else {
REALTYPE mul = (pow(5.0f, 1.0f + pars.tonal_vs_noise.preserve) - 1.0f);
for (int i = 0; i<nfreq; i++) {
REALTYPE x = freq1[i];
REALTYPE smooth_x = tmpfreq1[i] + 1e-6f;
REALTYPE result = 0.0f;
result = x - smooth_x * mul + 0.1f*mul;
if (result<0.0f) result = x;
else result = 0.0f;
freq2[i] = result;
};
};
};
inline void spectrum_do_harmonics(const ProcessParameters& pars, std::vector<REALTYPE>& 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<nfreq; i++) amp[i] = 0.0;
for (int nh = 1; nh <= nharmonics; nh++) {//for each harmonic
REALTYPE bw_Hz;//bandwidth of the current harmonic measured in Hz
REALTYPE bwi;
REALTYPE fi;
REALTYPE f = nh * freq;
if (f >= 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; i<nfreq; i++) {//todo: optimize here
REALTYPE hprofile;
hprofile = profile((i / (REALTYPE)nfreq*0.5f) - fi, bwi);
amp[i] += hprofile;
if (max<hprofile) max = hprofile;
sum += hprofile;
};
};
REALTYPE max = 0.0;
for (int i = 1; i<nfreq; i++) {
if (amp[i]>max) max = amp[i];
};
if (max<1e-8f) max = 1e-8f;
for (int i = 1; i<nfreq; i++) {
//REALTYPE c,s;
REALTYPE a = amp[i] / max;
if (!pars.harmonics.gauss) a = (a<0.368f ? 0.0f : 1.0f);
freq2[i] = freq1[i] * a;
};
};
inline void spectrum_add(int nfreq, REALTYPE *freq2, REALTYPE *freq1, REALTYPE a) {
for (int i = 0; i<nfreq; i++) freq2[i] += freq1[i] * a;
};
inline void spectrum_zero(int nfreq,REALTYPE *freq1) {
for (int i = 0; i<nfreq; i++) freq1[i] = 0.0;
};
inline void spectrum_do_freq_shift(const ProcessParameters& pars, int nfreq, double samplerate,
REALTYPE *freq1, REALTYPE *freq2) {
spectrum_zero(nfreq, freq2);
int ifreq = (int)(pars.freq_shift.Hz / (samplerate*0.5)*nfreq);
for (int i = 0; i<nfreq; i++) {
int i2 = ifreq + i;
if ((i2>0) && (i2<nfreq)) freq2[i2] = freq1[i];
};
};
inline void spectrum_do_pitch_shift(const ProcessParameters& pars, int nfreq, REALTYPE *freq1, REALTYPE *freq2, REALTYPE _rap) {
spectrum_zero(nfreq,freq2);
if (_rap<1.0) {//down
for (int i = 0; i<nfreq; i++) {
int i2 = (int)(i*_rap);
if (i2 >= nfreq) break;
freq2[i2] += freq1[i];
};
};
if (_rap >= 1.0) {//up
_rap = 1.0f / _rap;
for (int i = 0; i<nfreq; i++) {
freq2[i] = freq1[(int)(i*_rap)];
};
};
};
inline void spectrum_do_octave(const ProcessParameters& pars, int nfreq, double /*samplerate*/,
std::vector<REALTYPE>& sumfreq,
std::vector<REALTYPE>& 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<nfreq; i++) freq2[i] = sumfreq[i] / sum;
};
inline void spectrum_do_ratiomix(const ProcessParameters& pars, int nfreq, double /*samplerate*/,
std::vector<REALTYPE>& sumfreq,
std::vector<REALTYPE>& tmpfreq1,
REALTYPE *freq1, REALTYPE *freq2)
{
spectrum_zero(nfreq, sumfreq.data());
double ratiolevelsum = 0.01;
for (int i = 0; i < pars.ratiomix.ratios.size(); ++i)
{
double ratiolevel = pars.ratiomix.ratiolevels[i];
double ratio = pars.ratiomix.ratios[i];
ratiolevelsum += ratiolevel;
if (ratiolevel > 1e-3 && ratio > 0.0)
{
spectrum_do_pitch_shift(pars, nfreq, freq1, tmpfreq1.data(), ratio);
spectrum_add(nfreq, sumfreq.data(), tmpfreq1.data(), ratiolevel);
}
}
if (ratiolevelsum<0.5f)
ratiolevelsum = 0.5f;
for (int i = 0; i<nfreq; i++)
freq2[i] = sumfreq[i] / ratiolevelsum;
};
inline void spectrum_do_filter(const ProcessParameters& pars, int nfreq, double samplerate, REALTYPE *freq1, REALTYPE *freq2) {
REALTYPE low = 0, high = 0;
if (pars.filter.low<pars.filter.high) {//sort the low/high freqs
low = pars.filter.low;
high = pars.filter.high;
}
else {
high = pars.filter.low;
low = pars.filter.high;
};
int ilow = (int)(low / samplerate * nfreq*2.0f);
int ihigh = (int)(high / samplerate * nfreq*2.0f);
REALTYPE dmp = 1.0;
REALTYPE dmprap = 1.0f - pow(pars.filter.hdamp*0.5f, 4.0f);
for (int i = 0; i<nfreq; i++) {
REALTYPE a = 0.0f;
if ((i >= ilow) && (i<ihigh)) a = 1.0f;
if (pars.filter.stop) a = 1.0f - a;
freq2[i] = freq1[i] * a*dmp;
dmp *= dmprap + 1e-8f;
};
};
inline void spectrum_do_free_filter(shared_envelope& env, int nfreq, double samplerate,
REALTYPE *freq1, REALTYPE *freq2)
{
jassert(env != nullptr);
for (int i = 0; i<nfreq; i++)
{
double binhz = (samplerate / 2.0) / nfreq * i;
if (binhz >= 30.0)
{
double norm = 0.150542*log(0.0333333*binhz);
double db = jmap<double>(env->getTransformedValue(norm), 0.0, 1.0, -48.0, 12.0);
freq2[i] = freq1[i] * Decibels::decibelsToGain(db);
}
else
freq2[i] = freq1[i];
};
};
enum SpectrumProcessType
{
SPT_Harmonics = 0,
SPT_TonalVsNoise,
SPT_FreqShift,
SPT_PitchShift,
SPT_RatioMix,
SPT_Spread,
SPT_Filter,
SPT_FreeFilter,
SPT_Compressor,
SPT_Unknown = 1000
};
class SpectrumProcess
{
public:
SpectrumProcess() {}
SpectrumProcess(SpectrumProcessType index, AudioParameterBool* enabled) : m_index(index), m_enabled(enabled) {}
SpectrumProcessType m_index = SPT_Unknown;
AudioParameterBool* m_enabled = nullptr;
};
// Special function to swap the modules. We don't want to mess up the AudioParameterBool pointers,
// just swap the boolean states...
inline void swapSpectrumProcesses(SpectrumProcess& a, SpectrumProcess& b)
{
std::swap(a, b);
return;
bool aenab = *b.m_enabled;
bool benab = *a.m_enabled;
std::swap(a.m_index, b.m_index);
*a.m_enabled = aenab;
*b.m_enabled = benab;
}
class ProcessedStretch final : public Stretch
{
public:
//stereo_mode: 0=mono,1=left,2=right
ProcessedStretch(REALTYPE rap_,int in_bufsize_,FFTWindow w=W_HAMMING,bool bypass_=false,REALTYPE samplerate_=44100.0f,int stereo_mode=0);
~ProcessedStretch();
void set_parameters(ProcessParameters *ppar);
void setFreeFilterEnvelope(shared_envelope env);
std::vector<SpectrumProcess> m_spectrum_processes;
void setBufferSize(int sz) override;
private:
REALTYPE get_stretch_multiplier(REALTYPE pos_percents) override;
// void process_output(REALTYPE *smps,int nsmps);
void process_spectrum(REALTYPE *freq) override;
shared_envelope m_free_filter_envelope;
void copy(REALTYPE* freq1, REALTYPE* freq2);
void add(REALTYPE *freq2,REALTYPE *freq1,REALTYPE a=1.0);
void mul(REALTYPE *freq1,REALTYPE a);
void zero(REALTYPE *freq1);
void update_free_filter();
int nfreq=0;
std::vector<REALTYPE> m_free_filter_freqs;
ProcessParameters pars;
std::vector<REALTYPE> m_infreq,m_sumfreq,m_tmpfreq1,m_tmpfreq2;
//REALTYPE *fbfreq;
};