Initial git commit
This commit is contained in:
commit
d7ca59b90a
116
Source/PS_Source/BinauralBeats.cpp
Normal file
116
Source/PS_Source/BinauralBeats.cpp
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
Copyright (C) 2008-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
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include "BinauralBeats.h"
|
||||
/*
|
||||
void BinauralBeatsParameters::add2XML(XMLwrapper *xml){
|
||||
xml->addpar("stereo_mode",(int) stereo_mode);
|
||||
xml->addparreal("mono",mono);
|
||||
|
||||
xml->beginbranch("FREE_EDIT");
|
||||
free_edit.add2XML(xml);
|
||||
xml->endbranch();
|
||||
};
|
||||
void BinauralBeatsParameters::getfromXML(XMLwrapper *xml){
|
||||
stereo_mode=(BB_STEREO_MODE)xml->getpar("stereo_mode",(int) stereo_mode,0,2);
|
||||
mono=xml->getparreal("mono",mono);
|
||||
|
||||
if (xml->enterbranch("FREE_EDIT")){
|
||||
free_edit.getfromXML(xml);
|
||||
xml->exitbranch();
|
||||
};
|
||||
};
|
||||
*/
|
||||
//coefficients of allpass filters are found by Olli Niemitalo
|
||||
|
||||
const REALTYPE Hilbert::coefl[]={0.6923877778065f, 0.9360654322959f, 0.9882295226860f, 0.9987488452737f};
|
||||
const REALTYPE Hilbert::coefr[]={0.4021921162426f, 0.8561710882420f, 0.9722909545651f, 0.9952884791278f};
|
||||
|
||||
BinauralBeats::BinauralBeats(int samplerate_){
|
||||
samplerate=samplerate_;
|
||||
hilbert_t=0.0f;
|
||||
};
|
||||
|
||||
|
||||
void BinauralBeats::process(REALTYPE *smpsl,REALTYPE *smpsr,int nsmps,REALTYPE pos_percents){
|
||||
// for (int i=0;i<nsmps;i++) smpsl[i]=smpsr[i]=sin(30.0*M_PI*i*2.0/nsmps)*0.4;
|
||||
|
||||
//printf(" enabled %d\n",pars.free_edit.get_enabled());
|
||||
if (pars.free_edit.get_enabled()){
|
||||
float mono=pars.mono*0.5f;
|
||||
for (int i=0;i<nsmps;i++){
|
||||
REALTYPE inl=smpsl[i];
|
||||
REALTYPE inr=smpsr[i];
|
||||
REALTYPE outl=inl*(1.0f-mono)+inr*mono;
|
||||
REALTYPE outr=inr*(1.0f-mono)+inl*mono;
|
||||
|
||||
smpsl[i]=outl;
|
||||
smpsr[i]=outr;
|
||||
};
|
||||
|
||||
REALTYPE freq=pars.free_edit.get_value(pos_percents);
|
||||
|
||||
|
||||
// freq=300;//test
|
||||
|
||||
freq*=0.5;//half down for left, half up for right
|
||||
for (int i=0;i<nsmps;i++){
|
||||
hilbert_t=fmod(hilbert_t+freq/samplerate,1.0f);
|
||||
REALTYPE h1=0.0f,h2=0.0f;
|
||||
hl.process(smpsl[i],h1,h2);
|
||||
|
||||
REALTYPE x=hilbert_t*2.0f*(float)M_PI;
|
||||
REALTYPE m1=h1*cos(x);
|
||||
REALTYPE m2=h2*sin(x);
|
||||
REALTYPE outl1=m1+m2;
|
||||
REALTYPE outl2=m1-m2;
|
||||
|
||||
|
||||
h1=0,h2=0;
|
||||
hr.process(smpsr[i],h1,h2);
|
||||
|
||||
m1=h1*cos(x);
|
||||
m2=h2*sin(x);
|
||||
REALTYPE outr1=m1-m2;
|
||||
REALTYPE outr2=m1+m2;
|
||||
|
||||
switch(pars.stereo_mode){
|
||||
case SM_LEFT_RIGHT:
|
||||
smpsl[i]=outl2;
|
||||
smpsr[i]=outr2;
|
||||
break;
|
||||
case SM_RIGHT_LEFT:
|
||||
smpsl[i]=outl1;
|
||||
smpsr[i]=outr1;
|
||||
break;
|
||||
case SM_SYMMETRIC:
|
||||
smpsl[i]=(outl1+outr1)*0.5f;
|
||||
smpsr[i]=(outl2+outr2)*0.5f;
|
||||
break;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
112
Source/PS_Source/BinauralBeats.h
Normal file
112
Source/PS_Source/BinauralBeats.h
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
|
||||
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 "FreeEdit.h"
|
||||
|
||||
#include "globals.h"
|
||||
|
||||
#include "../JuceLibraryCode/JuceHeader.h"
|
||||
|
||||
class AP{//allpass
|
||||
public:
|
||||
AP(REALTYPE a_=0.5){
|
||||
in1=in2=out1=out2=0.0;
|
||||
set(a_);
|
||||
};
|
||||
REALTYPE process(REALTYPE in){
|
||||
REALTYPE out=a*(in+out2)-in2;
|
||||
in2=in1;in1=in;
|
||||
out2=out1;out1=out;
|
||||
|
||||
return out;
|
||||
};
|
||||
REALTYPE set(REALTYPE a_){
|
||||
a=a_*a_;
|
||||
return a;
|
||||
};
|
||||
private:
|
||||
REALTYPE in1,in2,out1,out2;
|
||||
REALTYPE a;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class Hilbert{
|
||||
public:
|
||||
Hilbert(){
|
||||
for (int i=0;i<N;i++){
|
||||
apl[i].set(coefl[i]);
|
||||
apr[i].set(coefr[i]);
|
||||
};
|
||||
oldl=0.0;
|
||||
};
|
||||
void process(REALTYPE in, REALTYPE &out1, REALTYPE &out2){
|
||||
out1=oldl;
|
||||
out2=in;
|
||||
|
||||
for (int i=0;i<N;i++){
|
||||
out1=apl[i].process(out1);
|
||||
out2=apr[i].process(out2);
|
||||
};
|
||||
|
||||
|
||||
oldl=in;
|
||||
};
|
||||
|
||||
private:
|
||||
static const int N=4;
|
||||
static const REALTYPE coefl[];
|
||||
static const REALTYPE coefr[];
|
||||
AP apl[N],apr[N];
|
||||
REALTYPE oldl;
|
||||
};
|
||||
|
||||
enum BB_STEREO_MODE{
|
||||
SM_LEFT_RIGHT=0,
|
||||
SM_RIGHT_LEFT=1,
|
||||
SM_SYMMETRIC=2
|
||||
};
|
||||
|
||||
struct BinauralBeatsParameters{
|
||||
BinauralBeatsParameters(){
|
||||
stereo_mode=SM_LEFT_RIGHT;
|
||||
mono=0.5;
|
||||
};
|
||||
|
||||
BB_STEREO_MODE stereo_mode;
|
||||
float mono;
|
||||
FreeEdit free_edit;
|
||||
//void add2XML(XMLwrapper *xml);
|
||||
//void getfromXML(XMLwrapper *xml);
|
||||
};
|
||||
|
||||
class BinauralBeats{
|
||||
public:
|
||||
BinauralBeats(int samplerate_);
|
||||
void process(REALTYPE *smpsl,REALTYPE *smpsr,int nsmps,REALTYPE pos_percents);
|
||||
BinauralBeatsParameters pars;
|
||||
private:
|
||||
int samplerate;
|
||||
REALTYPE hilbert_t;
|
||||
Hilbert hl,hr;
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(BinauralBeats)
|
||||
};
|
||||
|
||||
|
236
Source/PS_Source/FreeEdit.cpp
Normal file
236
Source/PS_Source/FreeEdit.cpp
Normal file
@ -0,0 +1,236 @@
|
||||
/*
|
||||
Copyright (C) 2009 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
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include "FreeEdit.h"
|
||||
#include <vector>
|
||||
#ifdef HAVE_PS_FREE_EDIT
|
||||
FreeEdit::FreeEdit(){
|
||||
enabled=false;
|
||||
smooth=0.0;
|
||||
interp_mode=LINEAR;
|
||||
npos=FREE_EDIT_MAX_POINTS;
|
||||
pos=new FreeEditPos[npos];
|
||||
for (int i=0;i<npos;i++){
|
||||
pos[i].x=pos[i].y=0;
|
||||
pos[i].enabled=false;
|
||||
};
|
||||
pos[0].x=0;
|
||||
pos[0].y=0.5;
|
||||
pos[0].enabled=true;
|
||||
pos[1].x=1;
|
||||
pos[1].y=0.5;
|
||||
pos[1].enabled=true;
|
||||
|
||||
curve.data=NULL;
|
||||
curve.size=0;
|
||||
};
|
||||
|
||||
void FreeEdit::deep_copy_from(const FreeEdit &other){
|
||||
enabled=other.enabled;
|
||||
smooth=other.smooth;
|
||||
interp_mode=other.interp_mode;
|
||||
npos=other.npos;
|
||||
pos=new FreeEditPos[npos];
|
||||
for (int i=0;i<npos;i++){
|
||||
pos[i].x=other.pos[i].x;
|
||||
pos[i].y=other.pos[i].y;
|
||||
pos[i].enabled=other.pos[i].enabled;
|
||||
};
|
||||
curve.size=other.curve.size;
|
||||
if (other.curve.data&&other.curve.size){
|
||||
curve.data=new REALTYPE[curve.size];
|
||||
for (int i=0;i<curve.size;i++) curve.data[i]=other.curve.data[i];
|
||||
}else curve.data=NULL;
|
||||
extreme_x=other.extreme_x;
|
||||
extreme_y=other.extreme_y;
|
||||
};
|
||||
FreeEdit::FreeEdit (const FreeEdit &other){
|
||||
deep_copy_from(other);
|
||||
};
|
||||
const FreeEdit &FreeEdit::operator=(const FreeEdit &other){
|
||||
if (this == &other) return *this;
|
||||
deep_copy_from(other);
|
||||
return *this;
|
||||
};
|
||||
void FreeEdit::get_curve(int datasize,REALTYPE *data,bool real_values){
|
||||
int npos_used=0;
|
||||
for (int i=0;i<npos;i++) if (is_enabled(i)) npos_used++;
|
||||
if (!npos_used){
|
||||
for (int i=0;i<datasize;i++) data[i]=(real_values?extreme_y.get_min():0.0f);
|
||||
return;
|
||||
};
|
||||
|
||||
//get the enabled points
|
||||
std::vector<REALTYPE> posx(npos_used);
|
||||
std::vector<REALTYPE> posy(npos_used);
|
||||
int k=0;
|
||||
for (int i=0;i<npos;i++){
|
||||
if (is_enabled(i)){
|
||||
posx[k]=get_posx(i);
|
||||
posy[k]=get_posy(i);
|
||||
k++;
|
||||
};
|
||||
};
|
||||
|
||||
//sort the points
|
||||
for (int j=0;j<npos_used-1;j++){
|
||||
for (int i=j+1;i<npos_used;i++){
|
||||
if (posx[i]<posx[j]){
|
||||
swap(posx[i],posx[j]);
|
||||
swap(posy[i],posy[j]);
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
//generate the curve
|
||||
int p1=0,p2=1;
|
||||
for (int i=0;i<datasize;i++){
|
||||
REALTYPE x=(REALTYPE)i/(REALTYPE)datasize;
|
||||
while ((x>posx[p2])&&(p2<npos_used)){
|
||||
p1=p2;
|
||||
p2++;
|
||||
};
|
||||
REALTYPE px1=posx[p1];
|
||||
REALTYPE px2=posx[p2];
|
||||
REALTYPE diffx=px2-px1;
|
||||
REALTYPE x0=0;
|
||||
if (diffx>1e-5) x0=(x-px1)/diffx;
|
||||
if (interp_mode==COSINE) x0=(1.0f-cos(x0*(float)M_PI))*0.5f;
|
||||
REALTYPE y=y=posy[p1]*(1.0f-x0)+posy[p2]*x0;
|
||||
data[i]=y;
|
||||
};
|
||||
|
||||
|
||||
//smooth the curve
|
||||
if (smooth>0.01){
|
||||
const int max_times=4;
|
||||
REALTYPE a=exp(log(0.25f)/(smooth*smooth*datasize*0.25f));
|
||||
if ((a<=0.0f)||(a>=1.0f)) return;
|
||||
a=pow(a,max_times);
|
||||
for (k=0;k<max_times;k++){
|
||||
for (int i=1;i<datasize;i++) data[i]=data[i]*(1.0f-a)+data[i-1]*a;
|
||||
for (int i=datasize-2;i>=0;i--) data[i]=data[i]*(1.0f-a)+data[i+1]*a;
|
||||
};
|
||||
};
|
||||
|
||||
if (real_values){
|
||||
for (int i=0;i<datasize;i++) data[i]=extreme_y.coord_to_real_value(data[i]);
|
||||
if (extreme_y.get_scale()==FE_DB){
|
||||
for (int i=0;i<datasize;i++) data[i]=(float)dB2rap(data[i]);
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
void FreeEdit::update_curve(int size){
|
||||
if (curve.data) delete []curve.data;
|
||||
if (size<2) size=2;
|
||||
curve.size=size;
|
||||
curve.data=new REALTYPE[size];
|
||||
|
||||
get_curve(curve.size,curve.data,true);
|
||||
|
||||
// for(int i=0;i<size;i++) printf("_%d %g\n",i,curve.data[i]);
|
||||
};
|
||||
|
||||
REALTYPE FreeEdit::get_value(REALTYPE x){
|
||||
if (!curve.data) {
|
||||
return 0.0;// update_curve();
|
||||
};
|
||||
if (extreme_x.get_scale()==FE_LOG){
|
||||
if (x<=0.0f) x=1e-9f;
|
||||
};
|
||||
|
||||
// printf("%g\n",curve.data[1]);
|
||||
|
||||
x=extreme_x.real_value_to_coord(x);
|
||||
if (x<0) x=0.0;
|
||||
else if (x>1.0) x=1.0;
|
||||
REALTYPE rx=x*curve.size;
|
||||
REALTYPE rxh=floor(rx);
|
||||
int k=(int)rxh;
|
||||
REALTYPE rxl=rx-rxh;
|
||||
if (k<0) k=0;
|
||||
if (k>(curve.size-1)) k=curve.size-1;
|
||||
int k1=k+1; if (k1>(curve.size-1)) k1=curve.size-1;
|
||||
return curve.data[k]*(1.0f-rxl)+curve.data[k1]*rxl;
|
||||
};
|
||||
/*
|
||||
void FreeEdit::add2XML(XMLwrapper *xml){
|
||||
xml->addparbool("enabled",enabled);
|
||||
xml->addparreal("smooth",smooth);
|
||||
xml->addpar("interp_mode",interp_mode);
|
||||
xml->beginbranch("POINTS");
|
||||
for (int i=0;i<FREE_EDIT_MAX_POINTS;i++){
|
||||
if (!pos[i].enabled) continue;
|
||||
xml->beginbranch("POINT",i);
|
||||
xml->addparbool("enabled",pos[i].enabled);
|
||||
xml->addparreal("x",pos[i].x);
|
||||
xml->addparreal("y",pos[i].y);
|
||||
|
||||
xml->endbranch();
|
||||
};
|
||||
xml->endbranch();
|
||||
|
||||
xml->beginbranch("EXTREME_X");
|
||||
extreme_x.add2XML(xml);
|
||||
xml->endbranch();
|
||||
|
||||
xml->beginbranch("EXTREME_Y");
|
||||
extreme_y.add2XML(xml);
|
||||
xml->endbranch();
|
||||
};
|
||||
|
||||
|
||||
void FreeEdit::getfromXML(XMLwrapper *xml){
|
||||
enabled=xml->getparbool("enabled",enabled);
|
||||
smooth=xml->getparreal("smooth",smooth);
|
||||
interp_mode=(INTERP_MODE)xml->getpar("interp_mode",interp_mode,0,1);
|
||||
|
||||
|
||||
|
||||
if (xml->enterbranch("POINTS")){
|
||||
for (int i=0;i<FREE_EDIT_MAX_POINTS;i++){
|
||||
if (xml->enterbranch("POINT",i)){
|
||||
pos[i].enabled=xml->getparbool("enabled",pos[i].enabled);
|
||||
pos[i].x=xml->getparreal("x",pos[i].x);
|
||||
pos[i].y=xml->getparreal("y",pos[i].y);
|
||||
|
||||
xml->exitbranch();
|
||||
};
|
||||
};
|
||||
xml->exitbranch();
|
||||
};
|
||||
|
||||
if (xml->enterbranch("EXTREME_X")){
|
||||
extreme_x.getfromXML(xml);
|
||||
xml->exitbranch();
|
||||
};
|
||||
|
||||
if (xml->enterbranch("EXTREME_Y")){
|
||||
extreme_y.getfromXML(xml);
|
||||
xml->exitbranch();
|
||||
};
|
||||
|
||||
update_curve();
|
||||
};
|
||||
*/
|
||||
|
||||
#endif
|
252
Source/PS_Source/FreeEdit.h
Normal file
252
Source/PS_Source/FreeEdit.h
Normal file
@ -0,0 +1,252 @@
|
||||
/*
|
||||
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
|
||||
#define HAVE_PS_FREE_EDIT
|
||||
#ifdef HAVE_PS_FREE_EDIT
|
||||
#define FREE_EDIT_MAX_POINTS 50
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "globals.h"
|
||||
//#include "XMLwrapper.h"
|
||||
|
||||
#define LOG_2 0.693147181
|
||||
#define LOG_10 2.302585093
|
||||
|
||||
#define dB2rap(dB) ((exp((dB)*LOG_10/20.0)))
|
||||
#define rap2dB(rap) ((20*log(rap)/LOG_10))
|
||||
|
||||
struct FreeEditPos{
|
||||
REALTYPE x,y;
|
||||
bool enabled;
|
||||
};
|
||||
enum FREE_EDIT_EXTREME_SCALE{
|
||||
FE_LINEAR=0,
|
||||
FE_LOG=1,
|
||||
FE_DB=2
|
||||
};
|
||||
class FreeEditExtremes{
|
||||
public:
|
||||
FreeEditExtremes(){
|
||||
init();
|
||||
};
|
||||
void init(REALTYPE min_=0.0,REALTYPE max_=1.0,FREE_EDIT_EXTREME_SCALE scale_=FE_LINEAR,bool lock_min_max_=false,bool lock_scale_=false){
|
||||
min=min_;
|
||||
max=max_;
|
||||
scale=scale_;
|
||||
lock_min_max=lock_min_max_;
|
||||
lock_scale=lock_scale_;
|
||||
correct_values();
|
||||
};
|
||||
|
||||
//converting functions
|
||||
inline REALTYPE coord_to_real_value(REALTYPE coord){//coord = 0.0 .. 1.0
|
||||
REALTYPE result;
|
||||
switch(scale){
|
||||
case FE_LOG://log
|
||||
result=exp(log(min)+coord*(log(max)-log(min)));
|
||||
return result;
|
||||
default://linear or dB
|
||||
result=min+coord*(max-min);
|
||||
return result;
|
||||
};
|
||||
};
|
||||
|
||||
inline REALTYPE real_value_to_coord(REALTYPE val){//coord = 0.0 .. 1.0
|
||||
switch(scale){
|
||||
case FE_LOG://log
|
||||
{
|
||||
REALTYPE coord=log(val/min)/log(max/min);
|
||||
clamp1(coord);
|
||||
return coord;
|
||||
};
|
||||
default://linear
|
||||
{
|
||||
REALTYPE diff=max-min;
|
||||
REALTYPE coord;
|
||||
if (fabs(diff)>0.0001) coord=(val-min)/diff;
|
||||
else coord=min;
|
||||
clamp1(coord);
|
||||
return coord;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
//max and min functions
|
||||
void set_min(REALTYPE val){
|
||||
if (lock_min_max) return;
|
||||
min=val;
|
||||
correct_values();
|
||||
};
|
||||
REALTYPE get_min(){
|
||||
return min;
|
||||
};
|
||||
void set_max(REALTYPE val){
|
||||
if (lock_min_max) return;
|
||||
max=val;
|
||||
correct_values();
|
||||
};
|
||||
REALTYPE get_max(){
|
||||
return max;
|
||||
};
|
||||
//scale functions
|
||||
FREE_EDIT_EXTREME_SCALE get_scale(){
|
||||
return scale;
|
||||
};
|
||||
void set_scale(FREE_EDIT_EXTREME_SCALE val){
|
||||
if (lock_scale) return;
|
||||
scale=val;
|
||||
};
|
||||
/*
|
||||
void add2XML(XMLwrapper *xml){
|
||||
xml->addparreal("min",min);
|
||||
xml->addparreal("max",max);
|
||||
};
|
||||
void getfromXML(XMLwrapper *xml){
|
||||
set_min(xml->getparreal("min",min));
|
||||
set_max(xml->getparreal("max",max));
|
||||
};
|
||||
*/
|
||||
private:
|
||||
inline REALTYPE clamp1(REALTYPE m){
|
||||
if (m<0.0) return 0.0;
|
||||
if (m>1.0) return 1.0;
|
||||
return m;
|
||||
};
|
||||
void correct_values(){
|
||||
if (scale!=FE_LOG) return;
|
||||
if (min<1e-8) min=1e-8f;
|
||||
if (max<1e-8) max=1e-8f;
|
||||
};
|
||||
bool lock_min_max,lock_scale;
|
||||
REALTYPE min,max;
|
||||
FREE_EDIT_EXTREME_SCALE scale;
|
||||
};
|
||||
class FreeEdit{
|
||||
public:
|
||||
enum INTERP_MODE{
|
||||
LINEAR=0,
|
||||
COSINE=1
|
||||
};
|
||||
FreeEdit();
|
||||
FreeEdit (const FreeEdit &other);
|
||||
const FreeEdit &operator=(const FreeEdit &other);
|
||||
void deep_copy_from(const FreeEdit &other);
|
||||
|
||||
//void add2XML(XMLwrapper *xml);
|
||||
//void getfromXML(XMLwrapper *xml);
|
||||
|
||||
//Enabled functions
|
||||
bool get_enabled(){
|
||||
return enabled;
|
||||
};
|
||||
void set_enabled(bool val){
|
||||
enabled=val;
|
||||
};
|
||||
|
||||
inline int get_npoints(){
|
||||
return npos;
|
||||
};
|
||||
|
||||
//manipulation functions
|
||||
inline bool is_enabled(int n){
|
||||
if ((n<0)||(n>=npos)) return false;
|
||||
return pos[n].enabled;
|
||||
};
|
||||
inline void set_enabled(int n,bool enabled_){
|
||||
if ((n<0)||(n>=npos)) return;
|
||||
pos[n].enabled=enabled_;
|
||||
};
|
||||
|
||||
|
||||
inline REALTYPE get_posx(int n){
|
||||
if ((n<0)||(n>=npos)) return 0.0;
|
||||
return pos[n].x;
|
||||
};
|
||||
inline REALTYPE get_posy(int n){
|
||||
if ((n<0)||(n>=npos)) return 0.0;
|
||||
return pos[n].y;
|
||||
};
|
||||
inline void set_posx(int n,REALTYPE x){
|
||||
if ((n<2)||(n>=npos)) return;//don't allow to set the x position of the first two positions
|
||||
pos[n].x=clamp1(x);
|
||||
};
|
||||
inline void set_posy(int n,REALTYPE y){
|
||||
if ((n<0)||(n>=npos)) return;
|
||||
pos[n].y=clamp1(y);
|
||||
};
|
||||
|
||||
void set_all_values(REALTYPE val){
|
||||
for (int i=0;i<npos;i++){
|
||||
if (pos[i].enabled) pos[i].y=extreme_y.real_value_to_coord(val);
|
||||
}
|
||||
};
|
||||
|
||||
//interpolation mode
|
||||
INTERP_MODE get_interp_mode(){
|
||||
return interp_mode;
|
||||
};
|
||||
void set_interp_mode(INTERP_MODE interp_mode_){
|
||||
interp_mode=interp_mode_;
|
||||
};
|
||||
|
||||
//smooth
|
||||
REALTYPE get_smooth(){
|
||||
return smooth;
|
||||
};
|
||||
void set_smooth(REALTYPE smooth_){
|
||||
smooth=clamp1(smooth_);;
|
||||
};
|
||||
|
||||
//getting the curve
|
||||
void get_curve(int datasize,REALTYPE *data,bool real_values);
|
||||
|
||||
~FreeEdit(){
|
||||
delete []pos;
|
||||
};
|
||||
|
||||
//making/updating the curve
|
||||
void update_curve(int size=16384);
|
||||
REALTYPE get_value(REALTYPE x);
|
||||
|
||||
//extremes
|
||||
FreeEditExtremes extreme_x,extreme_y;
|
||||
|
||||
struct{
|
||||
REALTYPE *data;
|
||||
int size;
|
||||
}curve;
|
||||
private:
|
||||
inline REALTYPE clamp1(REALTYPE m){
|
||||
if (m<0.0) return 0.0;
|
||||
if (m>1.0) return 1.0;
|
||||
return m;
|
||||
};
|
||||
inline void swap(REALTYPE &m1,REALTYPE &m2){
|
||||
REALTYPE tmp=m1;
|
||||
m1=m2;
|
||||
m2=tmp;
|
||||
};
|
||||
FreeEditPos *pos;
|
||||
int npos;
|
||||
REALTYPE smooth;
|
||||
INTERP_MODE interp_mode;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
#endif
|
260
Source/PS_Source/Input/AInputS.h
Normal file
260
Source/PS_Source/Input/AInputS.h
Normal file
@ -0,0 +1,260 @@
|
||||
/*
|
||||
Copyright (C) 2006-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 <string>
|
||||
|
||||
#include "../JuceLibraryCode/JuceHeader.h"
|
||||
|
||||
//#include <audiofile.h>
|
||||
#include "InputS.h"
|
||||
|
||||
inline double ramp(int64_t pos, int64_t totallen, int64_t rampinlen, int64_t rampoutlen)
|
||||
{
|
||||
if (totallen < rampinlen + rampoutlen)
|
||||
return 1.0;
|
||||
if (pos < rampinlen)
|
||||
return 1.0 / rampinlen*pos;
|
||||
if (pos >= totallen - rampoutlen)
|
||||
return 1.0 - 1.0 / rampoutlen*(pos - totallen + rampoutlen);
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
class AInputS final : public InputS
|
||||
{
|
||||
public:
|
||||
AInputS(AudioFormatManager* mana) : m_manager(mana)
|
||||
{
|
||||
m_readbuf.setSize(2, 65536*2);
|
||||
m_readbuf.clear();
|
||||
m_crossfadebuf.setSize(2, 44100);
|
||||
m_crossfadebuf.clear();
|
||||
PlayRangeEndCallback=[](AInputS*){};
|
||||
}
|
||||
~AInputS() {}
|
||||
|
||||
bool openAudioFile(File file) override
|
||||
{
|
||||
m_silenceoutputted = 0;
|
||||
AudioFormatReader* reader = m_manager->createReaderFor(file);
|
||||
if (reader)
|
||||
{
|
||||
m_afreader = std::unique_ptr<AudioFormatReader>(reader);
|
||||
m_currentsample = 0;
|
||||
info.samplerate = (int)m_afreader->sampleRate;
|
||||
info.nchannels = m_afreader->numChannels;
|
||||
info.nsamples = m_afreader->lengthInSamples;
|
||||
if (m_readbuf.getNumChannels() < info.nchannels)
|
||||
{
|
||||
m_readbuf.setSize(info.nchannels, m_readbuf.getNumSamples());
|
||||
m_crossfadebuf.setSize(info.nchannels, m_crossfadebuf.getNumSamples());
|
||||
}
|
||||
m_readbuf.clear();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void close() override
|
||||
{
|
||||
m_afreader = nullptr;
|
||||
m_currentsample = 0;
|
||||
info.nchannels = 0;
|
||||
info.nsamples = 0;
|
||||
info.samplerate = 0;
|
||||
}
|
||||
int readNextBlock(AudioBuffer<float>& abuf, int nsmps, int numchans) override
|
||||
{
|
||||
if (m_afreader == nullptr)
|
||||
return 0;
|
||||
int inchans = m_afreader->numChannels;
|
||||
int64_t subsect_t0 = (int64_t)(m_activerange.getStart()*info.nsamples);
|
||||
int64_t subsect_t1 = (int64_t)(m_activerange.getEnd()*info.nsamples);
|
||||
int64_t subsectlen = subsect_t1 - subsect_t0;
|
||||
int xfadelen = m_xfadelen;
|
||||
if (xfadelen >= subsectlen)
|
||||
xfadelen = int(subsectlen - 2);
|
||||
auto getSampleLambda=[this](int64_t pos, int ch)
|
||||
{
|
||||
if (m_cached_file_range.contains(pos))
|
||||
return m_readbuf.getSample(ch, int(pos - m_cached_file_range.getStart()));
|
||||
else
|
||||
{
|
||||
Range<int64_t> activerange((int64_t)(m_activerange.getStart()*info.nsamples),
|
||||
(int64_t)(m_activerange.getEnd()*info.nsamples+1));
|
||||
Range<int64_t> possiblerange(pos, pos + m_readbuf.getNumSamples() + 0);
|
||||
m_cached_file_range = activerange.getIntersectionWith(possiblerange);
|
||||
m_afreader->read(&m_readbuf, 0, (int)m_cached_file_range.getLength(), pos, true, true);
|
||||
return m_readbuf.getSample(ch, int(pos - m_cached_file_range.getStart()));
|
||||
}
|
||||
};
|
||||
auto getCrossFadedSampleLambda=[this,&getSampleLambda](int64_t playpos, int chan, int64_t subt0, int64_t subt1, int xfadelen)
|
||||
{
|
||||
if (m_loop_enabled == false && playpos >= subt1)
|
||||
return 0.0f;
|
||||
if (playpos >= subt0 && playpos <= subt1 - xfadelen)
|
||||
return getSampleLambda(playpos, chan);
|
||||
if (playpos > (subt1 - xfadelen) && playpos<subt1)
|
||||
{
|
||||
int64_t fadeindex = playpos - subt1 + xfadelen;
|
||||
double fadeoutgain = 1.0 - (1.0 / (xfadelen - 0))*fadeindex;
|
||||
float s0 = (float)(getSampleLambda(playpos, chan)*fadeoutgain);
|
||||
double fadeingain = (1.0 / (xfadelen - 0))*fadeindex;
|
||||
int64_t playpos2 = playpos - subt1 + xfadelen;
|
||||
jassert(playpos2>=0 && playpos2<=xfadelen);
|
||||
float s1 = (float)(m_crossfadebuf.getSample(chan, (int)playpos2)*fadeingain);
|
||||
return s0 + s1;
|
||||
}
|
||||
++m_silenceoutputted;
|
||||
return 0.0f;
|
||||
};
|
||||
float** smps = abuf.getArrayOfWritePointers();
|
||||
int readinc = 1;
|
||||
if (m_reverseplay)
|
||||
readinc = -1;
|
||||
for (int i = 0; i < nsmps; ++i)
|
||||
{
|
||||
|
||||
if (m_afreader->numChannels == 1 && numchans > 0)
|
||||
{
|
||||
float sig = getCrossFadedSampleLambda(m_currentsample, 0, subsect_t0, subsect_t1, xfadelen);
|
||||
for (int j = 0; j < numchans; ++j)
|
||||
{
|
||||
smps[j][i] = sig;
|
||||
}
|
||||
}
|
||||
else if (m_afreader->numChannels > 1 && numchans > 1)
|
||||
{
|
||||
for (int j = 0; j < numchans; ++j)
|
||||
{
|
||||
int inchantouse = j % inchans;
|
||||
smps[j][i] = getCrossFadedSampleLambda(m_currentsample, inchantouse, subsect_t0, subsect_t1,xfadelen);
|
||||
}
|
||||
|
||||
}
|
||||
m_currentsample += readinc;
|
||||
if (m_loop_enabled == true)
|
||||
{
|
||||
if (m_reverseplay == false && m_currentsample >= subsect_t1)
|
||||
{
|
||||
m_currentsample = subsect_t0+xfadelen;
|
||||
++m_loopcount;
|
||||
}
|
||||
else if (m_reverseplay == true && m_currentsample < subsect_t0)
|
||||
{
|
||||
m_currentsample = subsect_t1 - 1;
|
||||
}
|
||||
} else
|
||||
{
|
||||
if (m_reverseplay == false && m_currentsample == subsect_t1)
|
||||
PlayRangeEndCallback(this);
|
||||
else if (m_reverseplay == true && m_currentsample == subsect_t0)
|
||||
PlayRangeEndCallback(this);
|
||||
}
|
||||
}
|
||||
|
||||
return nsmps;
|
||||
}
|
||||
void seek(double pos) override //0=start,1.0=end
|
||||
{
|
||||
if (m_afreader==nullptr)
|
||||
return;
|
||||
m_loopcount = 0;
|
||||
m_silenceoutputted = 0;
|
||||
m_cache_misses = 0;
|
||||
m_currentsample = (int64_t)(pos*m_afreader->lengthInSamples);
|
||||
m_currentsample = jlimit<int64_t>(0, m_afreader->lengthInSamples, m_currentsample);
|
||||
//Logger::writeToLog("Seeking to " + String(m_currentsample));
|
||||
//if (m_cached_file_range.contains(info.currentsample)==false)
|
||||
m_cached_file_range = Range<int64_t>();
|
||||
updateXFadeCache();
|
||||
//m_cached_crossfade_range = Range<int64_t>();
|
||||
}
|
||||
std::pair<Range<double>,Range<double>> getCachedRangesNormalized()
|
||||
{
|
||||
if (m_afreader == nullptr)
|
||||
return {};
|
||||
return { { jmap<double>((double)m_cached_file_range.getStart(),0,(double)info.nsamples,0.0,1.0),
|
||||
jmap<double>((double)m_cached_file_range.getEnd(), 0, (double)info.nsamples, 0.0, 1.0) },
|
||||
{ jmap<double>((double)m_cached_crossfade_range.getStart(),0,(double)info.nsamples,0.0,1.0),
|
||||
jmap<double>((double)m_cached_crossfade_range.getEnd(), 0, (double)info.nsamples, 0.0, 1.0) } };
|
||||
}
|
||||
int getNumCacheMisses() { return m_cache_misses; }
|
||||
void updateXFadeCache()
|
||||
{
|
||||
if (m_xfadelen>m_crossfadebuf.getNumSamples())
|
||||
m_crossfadebuf.setSize(info.nchannels,m_xfadelen);
|
||||
m_afreader->read(&m_crossfadebuf, 0, m_xfadelen, (int64_t)(m_activerange.getStart()*info.nsamples), true, true);
|
||||
m_cached_crossfade_range =
|
||||
Range<int64_t>((int64_t)(m_activerange.getStart()*info.nsamples),(int64_t)(m_activerange.getStart()*info.nsamples+m_xfadelen));
|
||||
}
|
||||
void setActiveRange(Range<double> rng) override
|
||||
{
|
||||
if (rng.getEnd() < rng.getStart())
|
||||
rng = { 0.0,1.0 };
|
||||
if (rng.isEmpty())
|
||||
rng = { 0.0,1.0 };
|
||||
m_activerange = rng;
|
||||
m_loopcount = 0;
|
||||
updateXFadeCache();
|
||||
}
|
||||
void setLoopEnabled(bool b) override
|
||||
{
|
||||
m_loop_enabled = b;
|
||||
m_loopcount = 0;
|
||||
updateXFadeCache();
|
||||
}
|
||||
void setXFadeLenSeconds(double len)
|
||||
{
|
||||
if (info.samplerate==0)
|
||||
return;
|
||||
len = jlimit(0.0,1.0,len);
|
||||
int temp = (int)(len*info.samplerate);
|
||||
if (m_xfadelen!=temp)
|
||||
{
|
||||
m_xfadelen = temp;
|
||||
updateXFadeCache();
|
||||
}
|
||||
}
|
||||
Range<int64_t> getActiveRangeFrames()
|
||||
{
|
||||
if (info.nsamples == 0)
|
||||
return Range<int64_t>();
|
||||
return Range<int64_t>((int64_t)(m_activerange.getStart()*info.nsamples), (int64_t)(m_activerange.getEnd()*info.nsamples));
|
||||
}
|
||||
void setReversePlay(bool b)
|
||||
{
|
||||
m_reverseplay = b;
|
||||
}
|
||||
bool isReversed() { return m_reverseplay; }
|
||||
int64_t getLoopCount() { return m_loopcount; }
|
||||
private:
|
||||
std::function<void(AInputS*)> PlayRangeEndCallback;
|
||||
std::unique_ptr<AudioFormatReader> m_afreader;
|
||||
AudioBuffer<float> m_readbuf;
|
||||
AudioBuffer<float> m_crossfadebuf;
|
||||
Range<int64_t> m_cached_file_range;
|
||||
Range<int64_t> m_cached_crossfade_range;
|
||||
int m_cache_misses = 0;
|
||||
int m_fade_in = 512;
|
||||
int m_fade_out = 512;
|
||||
int m_xfadelen = 0;
|
||||
bool m_reverseplay = false;
|
||||
int64_t m_loopcount = 0;
|
||||
AudioFormatManager* m_manager = nullptr;
|
||||
};
|
91
Source/PS_Source/Input/InputS.h
Normal file
91
Source/PS_Source/Input/InputS.h
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
Copyright (C) 2006-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 "../globals.h"
|
||||
|
||||
#include "../JuceLibraryCode/JuceHeader.h"
|
||||
|
||||
class InputS
|
||||
{
|
||||
public:
|
||||
InputS()
|
||||
{
|
||||
skipbufsize=1024;
|
||||
skipbuf.setSize(4, skipbufsize);
|
||||
};
|
||||
|
||||
virtual ~InputS()
|
||||
{
|
||||
|
||||
};
|
||||
virtual bool openAudioFile(File file)=0;
|
||||
virtual void close()=0;
|
||||
|
||||
virtual int readNextBlock(AudioBuffer<float>& abuf, int smps, int numchans)=0;
|
||||
void skip(int nsmps)
|
||||
{
|
||||
while ((nsmps>0)&&(m_silenceoutputted<1))
|
||||
{
|
||||
int readsize=(nsmps<skipbufsize)?nsmps:skipbufsize;
|
||||
if (skipbuf.getNumSamples() < readsize)
|
||||
skipbuf.setSize(info.nchannels, readsize);
|
||||
readNextBlock(skipbuf,readsize,info.nchannels);
|
||||
nsmps-=readsize;
|
||||
};
|
||||
};
|
||||
virtual void seek(double pos)=0;//0=start,1.0=end
|
||||
|
||||
struct {
|
||||
int64_t nsamples=0;
|
||||
int nchannels=0;
|
||||
int samplerate=0;
|
||||
|
||||
} info;
|
||||
|
||||
|
||||
int getSilenceOutputtedAfterActiveRange()
|
||||
{
|
||||
return m_silenceoutputted;
|
||||
}
|
||||
Range<double> getActiveRange() { return m_activerange; }
|
||||
double getLengthSeconds()
|
||||
{
|
||||
if (info.nsamples == 0 || info.samplerate==0)
|
||||
return 0.0;
|
||||
return (double)info.nsamples / info.samplerate;
|
||||
}
|
||||
virtual void setActiveRange(Range<double> rng) = 0;
|
||||
bool isLooping() { return m_loop_enabled; }
|
||||
virtual void setLoopEnabled(bool b) = 0;
|
||||
int64_t getCurrentPosition() { return m_currentsample; }
|
||||
virtual bool hasEnded()
|
||||
{
|
||||
return m_currentsample >= info.nsamples*m_activerange.getEnd();
|
||||
}
|
||||
protected:
|
||||
int64_t m_currentsample = 0;
|
||||
int m_silenceoutputted = 0;
|
||||
bool m_loop_enabled = false;
|
||||
Range<double> m_activerange{ 0.0,1.0 };
|
||||
private:
|
||||
int skipbufsize;
|
||||
AudioBuffer<float> skipbuf;
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(InputS)
|
||||
};
|
20
Source/PS_Source/Mutex.h
Normal file
20
Source/PS_Source/Mutex.h
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
Copyright (C) 2006-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 <mutex>
|
||||
using Mutex = std::mutex;
|
698
Source/PS_Source/PaulStretchControl.cpp
Normal file
698
Source/PS_Source/PaulStretchControl.cpp
Normal file
@ -0,0 +1,698 @@
|
||||
/*
|
||||
Copyright (C) 2006-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
|
||||
*/
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include "globals.h"
|
||||
#include "PaulStretchControl.h"
|
||||
#include "../WDL/resample.h"
|
||||
#include <thread>
|
||||
|
||||
#ifdef WIN32
|
||||
#undef min
|
||||
#undef max
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
|
||||
extern std::unique_ptr<PropertiesFile> g_propsfile;
|
||||
|
||||
Control::Control(AudioFormatManager* afm) : m_afm(afm), m_bufferingthread("stretchbufferingthread")
|
||||
{
|
||||
m_stretch_source = std::make_unique<MultiStretchAudioSource>(2,m_afm);
|
||||
|
||||
|
||||
wavinfo.samplerate=44100;
|
||||
wavinfo.nsamples=0;
|
||||
|
||||
process.bufsize=16384;
|
||||
process.stretch=4.0;
|
||||
process.onset_detection_sensitivity=0.0;
|
||||
|
||||
seek_pos=0.0;
|
||||
window_type=W_HANN;
|
||||
|
||||
volume=1.0;
|
||||
|
||||
|
||||
gui_sliders.fftsize_s=0.5;
|
||||
gui_sliders.stretch_s=0.5;
|
||||
gui_sliders.mode_s=0;
|
||||
|
||||
|
||||
|
||||
///#warning test
|
||||
/// process.transient.enable=true;
|
||||
};
|
||||
|
||||
Control::~Control()
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
|
||||
void Control::setStretchAmount(double rate)
|
||||
{
|
||||
m_stretch_source->setRate(rate);
|
||||
}
|
||||
|
||||
void Control::setFFTSize(double size)
|
||||
{
|
||||
if (m_prebuffer_amount == 5)
|
||||
m_fft_size_to_use = pow(2, 7.0 + size * 14.5);
|
||||
else m_fft_size_to_use = pow(2, 7.0 + size * 10.0); // chicken out from allowing huge FFT sizes if not enough prebuffering
|
||||
int optim = optimizebufsize(m_fft_size_to_use);
|
||||
m_fft_size_to_use = optim;
|
||||
m_stretch_source->setFFTSize(optim);
|
||||
//Logger::writeToLog(String(m_fft_size_to_use));
|
||||
}
|
||||
|
||||
void Control::setOnsetDetection(double x)
|
||||
{
|
||||
m_stretch_source->setOnsetDetection(x);
|
||||
}
|
||||
|
||||
void Control::set_input_file(File file, std::function<void(String)> cb)
|
||||
{
|
||||
auto task = [this,file,cb]()
|
||||
{
|
||||
|
||||
auto ai = unique_from_raw(m_afm->createReaderFor(file));
|
||||
|
||||
if (ai!=nullptr)
|
||||
{
|
||||
if (ai->numChannels > 32)
|
||||
{
|
||||
MessageManager::callAsync([cb,file](){ cb("Too many channels in file "+file.getFullPathName()); });
|
||||
return;
|
||||
}
|
||||
if (ai->bitsPerSample>32)
|
||||
{
|
||||
MessageManager::callAsync([cb,file](){ cb("Too high bit depth in file "+file.getFullPathName()); });
|
||||
return;
|
||||
}
|
||||
wavinfo.filename = file.getFullPathName();
|
||||
wavinfo.samplerate = ai->sampleRate;
|
||||
wavinfo.nsamples = ai->lengthInSamples;
|
||||
m_stretch_source->setAudioFile(file);
|
||||
MessageManager::callAsync([cb,file](){ cb(String()); });
|
||||
|
||||
/// if (process.transient.enable) {
|
||||
/// pre_analyse_whole_audio(ai);
|
||||
/// };
|
||||
|
||||
|
||||
} else
|
||||
{
|
||||
wavinfo.filename="";
|
||||
wavinfo.samplerate=0;
|
||||
wavinfo.nsamples=0;
|
||||
MessageManager::callAsync([cb,file](){ cb("Could not open file "+file.getFullPathName()); });
|
||||
|
||||
};
|
||||
};
|
||||
std::thread th(task);
|
||||
th.detach();
|
||||
};
|
||||
|
||||
String Control::get_input_filename(){
|
||||
return wavinfo.filename;
|
||||
};
|
||||
|
||||
String Control::get_stretch_info(Range<double> playrange)
|
||||
{
|
||||
double prate = m_stretch_source->getRate();
|
||||
double realduration = m_stretch_source->getOutputDurationSecondsForRange(playrange, m_fft_size_to_use);
|
||||
int64_t durintseconds = realduration;
|
||||
int64_t durintminutes = realduration / 60.0;
|
||||
int64_t durinthours = realduration / 3600.0;
|
||||
int64_t durintdays = realduration / (3600 * 24.0);
|
||||
String timestring;
|
||||
if (durintminutes < 1)
|
||||
timestring = String(realduration,3) +" seconds";
|
||||
if (durintminutes >= 1 && durinthours < 1)
|
||||
timestring = String(durintminutes) + " mins " + String(durintseconds % 60) + " secs";
|
||||
if (durinthours >= 1 && durintdays < 1)
|
||||
timestring = String(durinthours) + " hours " + String(durintminutes % 60) + " mins " + String(durintseconds % 60) + " secs";
|
||||
if (durintdays >= 1)
|
||||
timestring = String(durintdays) + " days " + String(durinthours % 24) + " hours " +
|
||||
String(durintminutes % 60) + " mins ";
|
||||
double fftwindowlenseconds = (double)m_fft_size_to_use / wavinfo.samplerate;
|
||||
double fftwindowfreqresol = (double)wavinfo.samplerate / 2 / m_fft_size_to_use;
|
||||
return "[Stretch " + String(prate, 1) + "x " + timestring + "] [FFT window "+String(fftwindowlenseconds,3)+" secs, frequency resolution "+
|
||||
String(fftwindowfreqresol,3)+" Hz]";
|
||||
#ifdef OLDSTRETCHINFO
|
||||
const int size=200;
|
||||
char tmp[size];tmp[size-1]=0;
|
||||
|
||||
if (wavinfo.nsamples==0) return "Stretch: ";
|
||||
|
||||
|
||||
double realduration = m_stretch_source->getOutputDuration();
|
||||
|
||||
if (realduration>(365.25*86400.0*1.0e12)){//more than 1 trillion years
|
||||
double duration=(realduration/(365.25*86400.0*1.0e12));//my
|
||||
snprintf(tmp,size,"Stretch: %.7gx (%g trillion years)",process.stretch,duration);
|
||||
return tmp;
|
||||
};
|
||||
if (realduration>(365.25*86400.0*1.0e9)){//more than 1 billion years
|
||||
double duration=(realduration/(365.25*86400.0*1.0e9));//my
|
||||
snprintf(tmp,size,"Stretch: %.7gx (%g billion years)",process.stretch,duration);
|
||||
return tmp;
|
||||
};
|
||||
if (realduration>(365.25*86400.0*1.0e6)){//more than 1 million years
|
||||
double duration=(realduration/(365.25*86400.0*1.0e6));//my
|
||||
snprintf(tmp,size,"Stretch: %.7gx (%g million years)",process.stretch,duration);
|
||||
return tmp;
|
||||
};
|
||||
if (realduration>(365.25*86400.0*2000.0)){//more than two millenniums
|
||||
int duration=(int)(realduration/(365.25*86400.0));//years
|
||||
int years=duration%1000;
|
||||
int milleniums=duration/1000;
|
||||
|
||||
char stryears[size];stryears[0]=0;
|
||||
if (years!=0){
|
||||
if (years==1) snprintf(stryears,size," 1 year");
|
||||
else snprintf(stryears,size," %d years",years);
|
||||
};
|
||||
snprintf(tmp,size,"Stretch: %.7gx (%d milleniums%s)",process.stretch,milleniums,stryears);
|
||||
return tmp;
|
||||
};
|
||||
if (realduration>(365.25*86400.0)){//more than 1 year
|
||||
int duration=(int) (realduration/3600.0);//hours
|
||||
int hours=duration%24;
|
||||
int days=(duration/24)%365;
|
||||
int years=duration/(365*24);
|
||||
|
||||
char stryears[size];stryears[0]=0;
|
||||
if (years==1) snprintf(stryears,size,"1 year ");
|
||||
else snprintf(stryears,size,"%d years ",years);
|
||||
|
||||
char strdays[size];strdays[0]=0;
|
||||
if (days>0){
|
||||
if (days==1) snprintf(strdays,size,"1 day");
|
||||
else snprintf(strdays,size,"%d days",days);
|
||||
};
|
||||
if (years>=10) hours=0;
|
||||
char strhours[size];strhours[0]=0;
|
||||
if (hours>0){
|
||||
snprintf(strhours,size," %d h",hours);
|
||||
};
|
||||
|
||||
snprintf(tmp,size,"Stretch: %.7gx (%s%s%s)",process.stretch,stryears,strdays,strhours);
|
||||
return tmp;
|
||||
}else{//less than 1 year
|
||||
int duration=(int)(realduration);//seconds
|
||||
|
||||
char strdays[size];strdays[0]=0;
|
||||
int days=duration/86400;
|
||||
if (days>0){
|
||||
if (days==1) snprintf(strdays,size,"1 day ");
|
||||
else snprintf(strdays,size,"%d days ",duration/86400);
|
||||
};
|
||||
REALTYPE stretch=process.stretch;
|
||||
if (stretch>=1.0){
|
||||
stretch=((int) (stretch*100.0))*0.01;
|
||||
};
|
||||
snprintf(tmp,size,"Stretch: %.7gx (%s%.2d:%.2d:%.2d)",
|
||||
stretch,strdays,(duration/3600)%24,(duration/60)%60,duration%60);
|
||||
return tmp;
|
||||
};
|
||||
return "";
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
string Control::get_fftresolution_info(){
|
||||
string resolution="Resolution: ";
|
||||
if (wavinfo.nsamples==0) return resolution;
|
||||
|
||||
//todo: unctime and uncfreq are correct computed? Need to check later.
|
||||
REALTYPE unctime=process.bufsize/(REALTYPE)wavinfo.samplerate*sqrt(2.0);
|
||||
REALTYPE uncfreq=1.0/unctime*sqrt(2.0);
|
||||
char tmp[100];
|
||||
snprintf(tmp,100,"%.5g seconds",unctime);resolution+=tmp;
|
||||
snprintf(tmp,100," (%.5g Hz)",uncfreq);resolution+=tmp;
|
||||
return resolution;
|
||||
}
|
||||
*/
|
||||
|
||||
double Control::getPreBufferingPercent()
|
||||
{
|
||||
if (m_buffering_source == nullptr)
|
||||
return 0.0;
|
||||
return m_buffering_source->getPercentReady();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Control::startplay(bool /*bypass*/, bool /*realtime*/, Range<double> playrange, int numoutchans, String& err)
|
||||
{
|
||||
m_stretch_source->setPlayRange(playrange, m_stretch_source->isLoopingEnabled());
|
||||
|
||||
int bufamt = m_bufamounts[m_prebuffer_amount];
|
||||
|
||||
if (m_buffering_source != nullptr && numoutchans != m_buffering_source->getNumberOfChannels())
|
||||
m_recreate_buffering_source = true;
|
||||
if (m_recreate_buffering_source == true)
|
||||
{
|
||||
m_buffering_source = std::make_unique<MyBufferingAudioSource>(m_stretch_source.get(),
|
||||
m_bufferingthread, false, bufamt, numoutchans, false);
|
||||
m_recreate_buffering_source = false;
|
||||
}
|
||||
|
||||
m_stretch_source->setNumOutChannels(numoutchans);
|
||||
m_stretch_source->setFFTSize(m_fft_size_to_use);
|
||||
update_process_parameters();
|
||||
m_last_outpos_pos = 0.0;
|
||||
m_last_in_pos = playrange.getStart()*m_stretch_source->getInfileLengthSeconds();
|
||||
|
||||
|
||||
|
||||
// sleep(100);
|
||||
// update_process_parameters();
|
||||
};
|
||||
void Control::stopplay()
|
||||
{
|
||||
//m_adm->removeAudioCallback(&m_test_callback);
|
||||
|
||||
};
|
||||
|
||||
void Control::set_seek_pos(REALTYPE x)
|
||||
{
|
||||
seek_pos=x;
|
||||
m_stretch_source->seekPercent(x);
|
||||
};
|
||||
|
||||
REALTYPE Control::get_seek_pos()
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double Control::getLivePlayPosition()
|
||||
{
|
||||
#ifndef USEOLDPLAYCURSOR
|
||||
double outpos = m_audiocallback.m_outpos;
|
||||
double rate = 1.0 / m_stretch_source->getRate();
|
||||
double outputdiff = (outpos - m_last_outpos_pos);
|
||||
double fftlenseconds = (double)m_stretch_source->getFFTSize() / 44100.0;
|
||||
//Logger::writeToLog("output diff " + String(outputdiff)+" fft len "+String(fftlenseconds));
|
||||
//jassert(outputdiff >= 0.0 && outputdiff<0.5);
|
||||
jassert(rate > 0.0);
|
||||
double inlenseconds = m_stretch_source->getInfileLengthSeconds();
|
||||
if (inlenseconds < 0.0001)
|
||||
return 0.0;
|
||||
double inposseconds = m_stretch_source->getInfilePositionSeconds();
|
||||
double playposseconds = m_last_in_pos + (outputdiff*rate) - fftlenseconds;
|
||||
if (outputdiff*rate >= fftlenseconds || outputdiff < 0.0001)
|
||||
{
|
||||
//Logger::writeToLog("juuh "+String(inposseconds));
|
||||
m_last_in_pos = inposseconds;
|
||||
m_last_outpos_pos = outpos;
|
||||
return 1.0 / inlenseconds*(m_last_in_pos-fftlenseconds-getPreBufferAmountSeconds()*rate);
|
||||
}
|
||||
//Logger::writeToLog("jaah " + String(inposseconds));
|
||||
return 1.0 / inlenseconds*(playposseconds-getPreBufferAmountSeconds()*rate);
|
||||
#else
|
||||
return m_stretch_source->getInfilePositionPercent();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Control::playing()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void Control::set_stretch_controls(double stretch_s,int mode,double fftsize_s,double onset_detection_sensitivity)
|
||||
{
|
||||
gui_sliders.stretch_s=stretch_s;
|
||||
gui_sliders.mode_s=mode;
|
||||
gui_sliders.fftsize_s=fftsize_s;
|
||||
|
||||
double stretch=1.0;
|
||||
switch(mode){
|
||||
case 0:
|
||||
stretch_s=pow(stretch_s,1.2);
|
||||
stretch=pow(10.0,stretch_s*4.0);
|
||||
break;
|
||||
case 1:
|
||||
stretch_s=pow(stretch_s,1.5);
|
||||
stretch=pow(10.0,stretch_s*18.0);
|
||||
break;
|
||||
case 2:
|
||||
stretch=1.0/pow(10.0,stretch_s*2.0);
|
||||
break;
|
||||
};
|
||||
|
||||
|
||||
fftsize_s=pow(fftsize_s,1.5);
|
||||
int bufsize=(int)(pow(2.0,fftsize_s*12.0)*512.0);
|
||||
|
||||
bufsize=optimizebufsize(bufsize);
|
||||
|
||||
process.stretch=stretch;
|
||||
process.bufsize=bufsize;
|
||||
process.onset_detection_sensitivity=onset_detection_sensitivity;
|
||||
|
||||
};
|
||||
|
||||
double Control::get_stretch_control(double stretch,int mode)
|
||||
{
|
||||
double result=1.0;
|
||||
switch(mode){
|
||||
case 0:
|
||||
if (stretch<1.0) return -1;
|
||||
stretch=(log(stretch)/log(10))*0.25;
|
||||
result=pow(stretch,1.0/1.2);
|
||||
break;
|
||||
case 1:
|
||||
if (stretch<1.0) return -1;
|
||||
stretch=(log(stretch)/log(10))/18.0;
|
||||
result=pow(stretch,1.0/1.5);
|
||||
break;
|
||||
case 2:
|
||||
if (stretch>1.0) return -1;
|
||||
result=2.0/(log(stretch)/log(10));
|
||||
break;
|
||||
};
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
void Control::update_player_stretch()
|
||||
{
|
||||
return;
|
||||
//player->setrap(process.stretch);
|
||||
//player->set_onset_detection_sensitivity(process.onset_detection_sensitivity);
|
||||
};
|
||||
|
||||
|
||||
int abs_val(int x){
|
||||
if (x<0) return -x;
|
||||
else return x;
|
||||
};
|
||||
|
||||
|
||||
void Control::setPreBufferAmount(int x)
|
||||
{
|
||||
int temp = jlimit(0, 5, x);
|
||||
if (temp != m_prebuffer_amount)
|
||||
{
|
||||
m_prebuffer_amount = temp;
|
||||
m_recreate_buffering_source = true;
|
||||
}
|
||||
}
|
||||
|
||||
double Control::getPreBufferAmountSeconds()
|
||||
{
|
||||
return 1.0;
|
||||
|
||||
}
|
||||
|
||||
void Control::setAudioVisualizer(AudioVisualiserComponent * comp)
|
||||
{
|
||||
//m_audiocallback.m_aviscomponent = comp;
|
||||
}
|
||||
|
||||
void Control::setOutputAudioFileToRecord(File f)
|
||||
{
|
||||
m_audio_out_file = f;
|
||||
}
|
||||
|
||||
int Control::get_optimized_updown(int n,bool up){
|
||||
int orig_n=n;
|
||||
while(true){
|
||||
n=orig_n;
|
||||
|
||||
while (!(n%11)) n/=11;
|
||||
while (!(n%7)) n/=7;
|
||||
|
||||
while (!(n%5)) n/=5;
|
||||
while (!(n%3)) n/=3;
|
||||
while (!(n%2)) n/=2;
|
||||
if (n<2) break;
|
||||
if (up) orig_n++;
|
||||
else orig_n--;
|
||||
if (orig_n<4) return 4;
|
||||
};
|
||||
return orig_n;
|
||||
};
|
||||
int Control::optimizebufsize(int n){
|
||||
int n1=get_optimized_updown(n,false);
|
||||
int n2=get_optimized_updown(n,true);
|
||||
if ((n-n1)<(n2-n)) return n1;
|
||||
else return n2;
|
||||
};
|
||||
|
||||
|
||||
|
||||
void Control::set_window_type(FFTWindow window){
|
||||
window_type=window;
|
||||
//if (player) player->set_window_type(window);
|
||||
};
|
||||
|
||||
RenderInfoRef Control::Render2(RenderParameters renpars)
|
||||
{
|
||||
RenderInfoRef rinfo = std::make_shared<RenderInfo>();
|
||||
auto bbparcopy = bbpar;
|
||||
auto processcopy = process;
|
||||
auto windowtypecopy = window_type;
|
||||
auto pparcopy = ppar;
|
||||
//auto volcopy = volume;
|
||||
auto ratecopy = m_stretch_source->getRate();
|
||||
auto fftsize = m_fft_size_to_use;
|
||||
AudioFormatManager* afm = m_afm;
|
||||
auto render_task = [rinfo,renpars,
|
||||
bbparcopy, processcopy, windowtypecopy, pparcopy, fftsize,ratecopy,afm]()mutable->void
|
||||
{
|
||||
|
||||
|
||||
double t0 = Time::getMillisecondCounterHiRes();
|
||||
if (renpars.pos2 < renpars.pos1)
|
||||
std::swap(renpars.pos1, renpars.pos2);
|
||||
if (renpars.pos2-renpars.pos1<0.0001)
|
||||
renpars.pos2+=0.0001;
|
||||
auto ai = std::make_unique<AInputS>(afm);
|
||||
if (!ai->openAudioFile(renpars.inaudio))
|
||||
{
|
||||
rinfo->m_txt = "Error: Could not open audio file (or file format not recognized) :" + renpars.inaudio.getFileName();
|
||||
return;
|
||||
}
|
||||
if (renpars.sampleRate == 0)
|
||||
renpars.sampleRate = ai->info.samplerate;
|
||||
WavAudioFormat audioformat;
|
||||
renpars.outaudio = renpars.outaudio.getNonexistentSibling();
|
||||
auto outstream = renpars.outaudio.createOutputStream();
|
||||
int wavbits = 16;
|
||||
if (renpars.wavformat == 1)
|
||||
wavbits = 24;
|
||||
if (renpars.wavformat == 2)
|
||||
wavbits = 32;
|
||||
renpars.numoutchans = jlimit(2, g_maxnumoutchans, renpars.numoutchans);
|
||||
StringPairArray metadata;
|
||||
metadata.set(WavAudioFormat::bwavOriginator,"PaulStretch3");
|
||||
/*
|
||||
metadata.set("NumCuePoints", "2");
|
||||
metadata.set("Cue0Offset", "44100");
|
||||
metadata.set("Cue0Identifier", "0");
|
||||
metadata.set("Cue1Offset", "88200");
|
||||
metadata.set("Cue1Identifier", "1");
|
||||
*/
|
||||
AudioFormatWriter* writer = audioformat.createWriterFor(outstream, renpars.sampleRate, renpars.numoutchans, wavbits,
|
||||
metadata, 0);
|
||||
if (writer == nullptr)
|
||||
{
|
||||
delete outstream;
|
||||
rinfo->m_txt = "Could not create output file";
|
||||
return;
|
||||
}
|
||||
|
||||
auto stretchsource = std::make_unique<StretchAudioSource>(renpars.numoutchans,afm);
|
||||
if (wavbits == 2)
|
||||
stretchsource->setClippingEnabled(renpars.clipFloatOutput);
|
||||
else stretchsource->setClippingEnabled(true);
|
||||
stretchsource->setAudioFile(renpars.inaudio);
|
||||
stretchsource->setPlayRange({renpars.pos1,renpars.pos2}, renpars.numLoops>0);
|
||||
stretchsource->setRate(ratecopy);
|
||||
stretchsource->val_MainVolume.setValue(renpars.voldb);
|
||||
stretchsource->setNumOutChannels(renpars.numoutchans);
|
||||
|
||||
stretchsource->setProcessParameters(&pparcopy);
|
||||
stretchsource->setFFTSize(fftsize);
|
||||
int bufsize = 4096;
|
||||
AudioBuffer<float> procbuf(renpars.numoutchans,bufsize);
|
||||
AudioSourceChannelInfo asinfo(procbuf);
|
||||
stretchsource->prepareToPlay(bufsize, renpars.sampleRate);
|
||||
double render_time_limit = renpars.maxrenderlen;
|
||||
int64_t outputsamplecount = 0;
|
||||
int64_t outputlen = ai->info.samplerate*stretchsource->getOutputDurationSecondsForRange({ renpars.pos1,renpars.pos2 }, fftsize);
|
||||
rinfo->m_progress_percent = 0.01;
|
||||
stretchsource->setMaxLoops(renpars.numLoops);
|
||||
while(outputsamplecount<render_time_limit*renpars.sampleRate)
|
||||
{
|
||||
if (rinfo->m_cancel == true)
|
||||
{
|
||||
rinfo->m_txt = "Cancelled";
|
||||
break;
|
||||
}
|
||||
stretchsource->getNextAudioBlock(asinfo);
|
||||
writer->writeFromAudioSampleBuffer(procbuf, 0, bufsize);
|
||||
outputsamplecount +=bufsize;
|
||||
rinfo->m_progress_percent = 1.0 / outputlen*outputsamplecount;
|
||||
if (stretchsource->hasReachedEnd())
|
||||
{
|
||||
Logger::writeToLog("StretchSource has reached end");
|
||||
break;
|
||||
}
|
||||
if (outputsamplecount>=render_time_limit*ai->info.samplerate)
|
||||
{
|
||||
rinfo->m_txt = "Render stopped at time limit";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
delete writer;
|
||||
double t1 = Time::getMillisecondCounterHiRes();
|
||||
if (rinfo->m_cancel == false)
|
||||
{
|
||||
rinfo->m_elapsed_time = t1 - t0;
|
||||
if (rinfo->m_txt.isEmpty())
|
||||
rinfo->m_txt = "Done in "+String((t1-t0)/1000.0,1)+" seconds";
|
||||
}
|
||||
else rinfo->m_txt = "Cancelled";
|
||||
};
|
||||
std::thread th([rinfo,render_task, renpars]()mutable
|
||||
{
|
||||
render_task();
|
||||
MessageManager::callAsync([rinfo, renpars]()
|
||||
{
|
||||
renpars.completion_callback(rinfo);
|
||||
});
|
||||
});
|
||||
th.detach();
|
||||
return rinfo;
|
||||
}
|
||||
|
||||
void Control::setPrebufferThreadPriority(int v)
|
||||
{
|
||||
m_prebufthreadprior = jlimit(4,6,v);
|
||||
}
|
||||
|
||||
string Control::getfftsizestr(int fftsize){
|
||||
const int size=100;
|
||||
char tmp[size];tmp[size-1]=0;
|
||||
if (fftsize<1024.0) snprintf(tmp,size-1,"%d",fftsize);
|
||||
else if (fftsize<(1024.0*1024.0)) snprintf(tmp,size-1,"%.4gK",fftsize/1024.0);
|
||||
else if (fftsize<(1024.0*1024.0*1024.0)) snprintf(tmp,size-1,"%.4gM",fftsize/(1024.0*1024.0));
|
||||
else snprintf(tmp,size-1,"%.7gG",fftsize/(1024.0*1024.0*1024.0));
|
||||
return tmp;
|
||||
};
|
||||
|
||||
void Control::update_process_parameters()
|
||||
{
|
||||
m_stretch_source->setProcessParameters(&ppar);
|
||||
//if (player)
|
||||
// player->set_process_parameters(&ppar,&bbpar);
|
||||
};
|
||||
|
||||
AudioCallback::AudioCallback() : AudioIODeviceCallback(),
|
||||
m_writethread("audio_out_record_thread")
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void AudioCallback::audioDeviceAboutToStart(AudioIODevice * device)
|
||||
{
|
||||
m_debugcount = 0;
|
||||
m_outpos = 0.0;
|
||||
m_outsr = device->getCurrentSampleRate();
|
||||
if (m_aviscomponent)
|
||||
m_aviscomponent->setNumChannels(m_numoutchans);
|
||||
if (m_bufferingsource)
|
||||
{
|
||||
if (m_prebufferthread->isThreadRunning()==false)
|
||||
m_prebufferthread->startThread(g_propsfile->getIntValue("prebufthreadpriority",5));
|
||||
int bufsize = std::max(512, device->getCurrentBufferSizeSamples());
|
||||
//Logger::writeToLog("Using buffer size " + String(bufsize));
|
||||
m_bufferingsource->prepareToPlay(bufsize, m_outsr);
|
||||
}
|
||||
m_playing = true;
|
||||
//Logger::writeToLog("hw samplerate " + String(m_outsr));
|
||||
}
|
||||
|
||||
void AudioCallback::audioDeviceStopped()
|
||||
{
|
||||
m_writer = nullptr;
|
||||
if (m_writethread.isThreadRunning() == true)
|
||||
{
|
||||
if (m_writethread.stopThread(1000) == false)
|
||||
{
|
||||
Logger::writeToLog("OUCH, live output recording thread didn't stop cleanly!");
|
||||
}
|
||||
}
|
||||
if (m_bufferingsource)
|
||||
{
|
||||
m_bufferingsource->releaseResources();
|
||||
if (m_prebufferthread->isThreadRunning() == true)
|
||||
{
|
||||
if (m_prebufferthread->stopThread(1000) == false)
|
||||
Logger::writeToLog("OUCH, prebuffering thread did not stop cleanly!");
|
||||
}
|
||||
}
|
||||
m_playing = false;
|
||||
}
|
||||
|
||||
void AudioCallback::audioDeviceIOCallback(const float ** /*inputChannelData*/, int, float ** outputChannelData, int numOutputChannels, int numSamples)
|
||||
{
|
||||
if (m_bufferingsource == nullptr)
|
||||
return;
|
||||
AudioBuffer<float> buf(outputChannelData, numOutputChannels, numSamples);
|
||||
AudioSourceChannelInfo ainfo(buf);
|
||||
m_bufferingsource->getNextAudioBlock(ainfo);
|
||||
if (m_aviscomponent && m_aviscomponent->isVisible())
|
||||
{
|
||||
m_aviscomponent->pushBuffer((const float**)outputChannelData, m_numoutchans, numSamples);
|
||||
}
|
||||
if (m_writer && m_is_recording == true)
|
||||
{
|
||||
m_writer->write((const float**)outputChannelData, numSamples);
|
||||
}
|
||||
m_outpos += (double)numSamples / m_outsr;
|
||||
}
|
||||
|
||||
String AudioCallback::startRecording(File outfile)
|
||||
{
|
||||
WavAudioFormat wavformat;
|
||||
auto outstream = outfile.createOutputStream();
|
||||
if (outstream == nullptr)
|
||||
return "Could not create output stream";
|
||||
auto writer = wavformat.createWriterFor(outstream, m_outsr, m_numoutchans, 32, StringPairArray(), 0);
|
||||
if (writer != nullptr)
|
||||
{
|
||||
if (m_writethread.isThreadRunning()==false)
|
||||
m_writethread.startThread();
|
||||
m_writer = std::make_unique<AudioFormatWriter::ThreadedWriter>(writer, m_writethread, 65536);
|
||||
m_is_recording = true;
|
||||
return String();
|
||||
}
|
||||
return "Could not create audio writer";
|
||||
}
|
||||
|
||||
void AudioCallback::setNumOutchans(int numchans)
|
||||
{
|
||||
m_numoutchans = jlimit(2, g_maxnumoutchans, numchans);
|
||||
}
|
180
Source/PS_Source/PaulStretchControl.h
Normal file
180
Source/PS_Source/PaulStretchControl.h
Normal file
@ -0,0 +1,180 @@
|
||||
/*
|
||||
Copyright (C) 2006-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 "globals.h"
|
||||
#include "Input/AInputS.h"
|
||||
#include "ProcessedStretch.h"
|
||||
#include "BinauralBeats.h"
|
||||
#include "StretchSource.h"
|
||||
#include "../JuceLibraryCode/JuceHeader.h"
|
||||
#include "../ps3_BufferingAudioSource.h"
|
||||
|
||||
class AudioCallback : public AudioIODeviceCallback
|
||||
{
|
||||
public:
|
||||
AudioCallback();
|
||||
void audioDeviceAboutToStart(AudioIODevice* device) override;
|
||||
void audioDeviceStopped() override;
|
||||
void audioDeviceIOCallback (const float** inputChannelData,
|
||||
int numInputChannels,
|
||||
float** outputChannelData,
|
||||
int numOutputChannels,
|
||||
int numSamples) override;
|
||||
String startRecording(File outfile);
|
||||
|
||||
std::atomic<bool> m_is_recording{ false };
|
||||
AudioVisualiserComponent* m_aviscomponent = nullptr;
|
||||
std::unique_ptr<AudioFormatWriter::ThreadedWriter> m_writer;
|
||||
TimeSliceThread m_writethread;
|
||||
TimeSliceThread* m_prebufferthread = nullptr;
|
||||
MyBufferingAudioSource* m_bufferingsource = nullptr;
|
||||
MultiStretchAudioSource* m_sas = nullptr;
|
||||
std::atomic<bool> m_playing{false};
|
||||
double m_outpos = 0.0;
|
||||
void setNumOutchans(int numchans);
|
||||
private:
|
||||
std::mutex m_mutex;
|
||||
int m_debugcount = 0;
|
||||
int m_outsr = 44100;
|
||||
int m_numoutchans = 2;
|
||||
};
|
||||
|
||||
class RenderInfo
|
||||
{
|
||||
public:
|
||||
double m_progress_percent = 0.0;
|
||||
double m_elapsed_time = 0.0;
|
||||
std::atomic<bool> m_cancel{ false };
|
||||
String m_txt;
|
||||
};
|
||||
|
||||
using RenderInfoRef = std::shared_ptr<RenderInfo>;
|
||||
|
||||
class RenderParameters
|
||||
{
|
||||
public:
|
||||
File inaudio;
|
||||
File outaudio;
|
||||
double pos1 = 0.0;
|
||||
double pos2 = 1.0;
|
||||
int64_t numLoops = 0;
|
||||
int wavformat = 2;
|
||||
int sampleRate = 0;
|
||||
int numoutchans = 2;
|
||||
double minrenderlen = 1.0;
|
||||
double maxrenderlen = 1000000.0;
|
||||
double voldb = 0.0;
|
||||
bool clipFloatOutput = true;
|
||||
std::function<void(RenderInfoRef)> completion_callback;
|
||||
};
|
||||
|
||||
class Control
|
||||
{
|
||||
public:
|
||||
Control(AudioFormatManager* afm);
|
||||
~Control();
|
||||
void startplay(bool bypass, bool realtime, Range<double> playrange, int numoutchans, String& err);
|
||||
void stopplay();
|
||||
void set_seek_pos(REALTYPE x);
|
||||
REALTYPE get_seek_pos();
|
||||
double getLivePlayPosition();
|
||||
bool playing();
|
||||
|
||||
void setStretchAmount(double rate);
|
||||
void setFFTSize(double size);
|
||||
int getFFTSize() { return m_fft_size_to_use; }
|
||||
void setOnsetDetection(double x);
|
||||
MultiStretchAudioSource* getStretchAudioSource() { return m_stretch_source.get(); }
|
||||
void set_input_file(File filename, std::function<void(String)> cb);//return non empty String if the file cannot be opened
|
||||
String get_input_filename();
|
||||
String get_stretch_info(Range<double> playrange);
|
||||
double getPreBufferingPercent();
|
||||
double get_stretch()
|
||||
{
|
||||
return process.stretch;
|
||||
};
|
||||
double get_onset_detection_sensitivity()
|
||||
{
|
||||
return process.onset_detection_sensitivity;
|
||||
};
|
||||
|
||||
void set_stretch_controls(double stretch_s,int mode,double fftsize_s,double onset_detection_sensitivity);//*_s sunt de la 0.0 la 1.0
|
||||
double get_stretch_control(double stretch,int mode);
|
||||
void update_player_stretch();
|
||||
|
||||
void set_window_type(FFTWindow window);
|
||||
/// void pre_analyse_whole_audio(InputS *ai);
|
||||
RenderInfoRef Render2(RenderParameters renpars);
|
||||
|
||||
ProcessParameters ppar;
|
||||
BinauralBeatsParameters bbpar;
|
||||
|
||||
void update_process_parameters();//pt. player
|
||||
struct{
|
||||
double fftsize_s,stretch_s;
|
||||
int mode_s;
|
||||
}gui_sliders;
|
||||
FFTWindow window_type;
|
||||
|
||||
void setPreBufferAmount(int x);
|
||||
int getPreBufferAmount() { return m_prebuffer_amount; }
|
||||
double getPreBufferAmountSeconds();
|
||||
bool isResampling()
|
||||
{
|
||||
return m_stretch_source->isResampling();
|
||||
}
|
||||
void setAudioVisualizer(AudioVisualiserComponent* comp);
|
||||
void setOutputAudioFileToRecord(File f);
|
||||
|
||||
|
||||
void setPrebufferThreadPriority(int v);
|
||||
int getPrebufferThreadPriority() { return m_prebufthreadprior; }
|
||||
private:
|
||||
REALTYPE volume;
|
||||
int m_prebufthreadprior = 5;
|
||||
int get_optimized_updown(int n,bool up);
|
||||
int optimizebufsize(int bufsize);
|
||||
std::string getfftsizestr(int fftsize);
|
||||
|
||||
struct control_process_t{
|
||||
int bufsize;
|
||||
double stretch;
|
||||
double onset_detection_sensitivity;
|
||||
}process;
|
||||
|
||||
struct control_infileinfo_t {
|
||||
int samplerate=0;
|
||||
int64_t nsamples=0;
|
||||
String filename;
|
||||
|
||||
}wavinfo;//input
|
||||
REALTYPE seek_pos;
|
||||
AudioFormatManager* m_afm = nullptr;
|
||||
TimeSliceThread m_bufferingthread;
|
||||
std::unique_ptr<MultiStretchAudioSource> m_stretch_source;
|
||||
std::unique_ptr<MyBufferingAudioSource> m_buffering_source;
|
||||
int m_prebuffer_amount = 1;
|
||||
bool m_recreate_buffering_source = true;
|
||||
File m_audio_out_file;
|
||||
int m_fft_size_to_use = 1024;
|
||||
double m_last_outpos_pos = 0.0;
|
||||
double m_last_in_pos = 0.0;
|
||||
std::vector<int> m_bufamounts{ 4096,8192,16384,32768,65536,262144 };
|
||||
};
|
457
Source/PS_Source/Player.cpp
Normal file
457
Source/PS_Source/Player.cpp
Normal file
@ -0,0 +1,457 @@
|
||||
/*
|
||||
Copyright (C) 2006-2009 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
|
||||
*/
|
||||
|
||||
#ifdef PLAYERCLASSSTILLPRESENT
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
//#include <unistd.h>
|
||||
#include <math.h>
|
||||
#include "Player.h"
|
||||
#include "globals.h"
|
||||
|
||||
extern std::unique_ptr<AudioFormatManager> g_audioformatmanager;
|
||||
|
||||
static int player_count=0;
|
||||
|
||||
Player::Player() : Thread("psplayerthread"){
|
||||
player_count++;
|
||||
if (player_count>1) {
|
||||
printf("Error: Player class multiples instances.\n");
|
||||
exit(1);
|
||||
};
|
||||
|
||||
newtask.mode=TASK_NONE;
|
||||
newtask.startpos=0.0;
|
||||
newtask.rap=1.0;
|
||||
newtask.fftsize=4096;
|
||||
|
||||
task=newtask;
|
||||
mode=MODE_STOP;
|
||||
|
||||
outbuf.n=0;
|
||||
|
||||
outbuf.size=0;
|
||||
outbuf.computek=0;
|
||||
outbuf.outk=0;
|
||||
outbuf.outpos=0;
|
||||
outbuf.nfresh=0;
|
||||
|
||||
|
||||
info.position=0;
|
||||
freeze_mode=false;
|
||||
bypass_mode=false;
|
||||
first_in_buf=true;
|
||||
window_type=W_HANN;
|
||||
|
||||
|
||||
paused=false;
|
||||
|
||||
info.playing=false;
|
||||
info.samplerate=44100;
|
||||
info.eof=true;
|
||||
volume=1.0;
|
||||
onset_detection_sensitivity=0.0;
|
||||
};
|
||||
|
||||
Player::~Player(){
|
||||
player_count--;
|
||||
|
||||
stop();
|
||||
};
|
||||
|
||||
Player::ModeType Player::getmode(){
|
||||
return mode;
|
||||
};
|
||||
|
||||
void Player::startplay(String filename, REALTYPE startpos,REALTYPE rap, int fftsize,bool bypass,
|
||||
ProcessParameters *ppar,BinauralBeatsParameters *bbpar, double loop_start, double loop_end, int numoutchans)
|
||||
{
|
||||
info.playing=false;
|
||||
info.eof=false;
|
||||
bypass_mode=bypass;
|
||||
if (bypass) freeze_mode=false;
|
||||
paused=false;
|
||||
taskmutex.lock();
|
||||
newtask.mode=TASK_START;
|
||||
newtask.filename=filename;
|
||||
newtask.startpos=startpos;
|
||||
newtask.m_num_outchans = numoutchans;
|
||||
newtask.rap=rap;
|
||||
newtask.fftsize=fftsize;
|
||||
newtask.bypass=bypass;
|
||||
newtask.ppar=ppar;
|
||||
newtask.bbpar=bbpar;
|
||||
newtask.m_loop_start = loop_start;
|
||||
newtask.m_loop_end = loop_end;
|
||||
taskmutex.unlock();
|
||||
};
|
||||
|
||||
void Player::stop(){
|
||||
//pun 0 la outbuf
|
||||
info.playing=false;
|
||||
/* taskmutex.lock();
|
||||
newtask.mode=TASK_STOP;
|
||||
taskmutex.unlock();*/
|
||||
};
|
||||
void Player::pause(){
|
||||
paused=!paused;
|
||||
};
|
||||
|
||||
void Player::seek(REALTYPE pos){
|
||||
taskmutex.lock();
|
||||
newtask.mode=TASK_SEEK;
|
||||
newtask.startpos=pos;
|
||||
taskmutex.unlock();
|
||||
};
|
||||
void Player::freeze(){
|
||||
freeze_mode=!freeze_mode;
|
||||
if (bypass_mode) freeze_mode=false;
|
||||
};
|
||||
|
||||
void Player::setrap(REALTYPE newrap){
|
||||
taskmutex.lock();
|
||||
newtask.mode=TASK_RAP;
|
||||
newtask.rap=newrap;
|
||||
taskmutex.unlock();
|
||||
};
|
||||
|
||||
void Player::set_process_parameters(ProcessParameters *ppar,BinauralBeatsParameters *bbpar){
|
||||
taskmutex.lock();
|
||||
newtask.mode=TASK_PARAMETERS;
|
||||
newtask.ppar=ppar;
|
||||
newtask.bbpar=bbpar;
|
||||
taskmutex.unlock();
|
||||
};
|
||||
|
||||
|
||||
void Player::set_window_type(FFTWindow window){
|
||||
window_type=window;
|
||||
};
|
||||
|
||||
void Player::set_volume(REALTYPE vol){
|
||||
volume=vol;
|
||||
};
|
||||
void Player::set_onset_detection_sensitivity(REALTYPE onset){
|
||||
onset_detection_sensitivity=onset;
|
||||
};
|
||||
|
||||
void Player::getaudiobuffer(int nsamples, float *out)
|
||||
{
|
||||
int numoutchans = task.m_num_outchans;
|
||||
if (mode==MODE_PREPARING){
|
||||
for (int i=0;i<nsamples*numoutchans;i++)
|
||||
{
|
||||
out[i]=0.0;
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (paused){
|
||||
for (int i=0;i<nsamples*numoutchans;i++)
|
||||
{
|
||||
out[i]=0.0;
|
||||
};
|
||||
return;
|
||||
};
|
||||
|
||||
bufmutex.lock();
|
||||
if ((outbuf.n==0)||(outbuf.nfresh==0)){
|
||||
bufmutex.unlock();
|
||||
for (int i=0;i<nsamples*numoutchans;i++)
|
||||
{
|
||||
out[i]=0.0;
|
||||
};
|
||||
return;
|
||||
};
|
||||
int k=outbuf.outk,pos=outbuf.outpos;
|
||||
|
||||
// printf("%d in_pos=%g\n",info.eof,outbuf.in_position[k]);
|
||||
if (info.eof) mode=MODE_STOP;
|
||||
else info.position=outbuf.in_position[k];
|
||||
for (int i=0;i<nsamples;i++)
|
||||
{
|
||||
for (int j=0;j<numoutchans;++j)
|
||||
out[i*numoutchans+j]=jlimit(-1.0f,1.0f, outbuf.channeldatas[j][k][pos]*volume);
|
||||
pos++;
|
||||
if (pos>=outbuf.size)
|
||||
{
|
||||
pos=0;
|
||||
k++;
|
||||
if (k>=outbuf.n) k=0;
|
||||
|
||||
outbuf.nfresh--;
|
||||
//printf("%d %d\n",k,outbuf.nfresh);
|
||||
|
||||
if (outbuf.nfresh<0){//Underflow
|
||||
outbuf.nfresh=0;
|
||||
for (int j=i;j<nsamples;j++)
|
||||
{
|
||||
for (int ch=0;ch<numoutchans;++ch)
|
||||
out[j*numoutchans+ch]=0.0;
|
||||
};
|
||||
break;
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
outbuf.outk=k;
|
||||
outbuf.outpos=pos;
|
||||
bufmutex.unlock();
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
void Player::run(){
|
||||
while(1)
|
||||
{
|
||||
if (threadShouldExit())
|
||||
break;
|
||||
newtaskcheck();
|
||||
|
||||
if (mode==MODE_STOP) sleep(10);
|
||||
if ((mode==MODE_PLAY)||(mode==MODE_PREPARING)){
|
||||
computesamples();
|
||||
};
|
||||
task.mode=TASK_NONE;
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
void Player::newtaskcheck(){
|
||||
TaskMode newmode=TASK_NONE;
|
||||
taskmutex.lock();
|
||||
if (task.mode!=newtask.mode) {
|
||||
newmode=newtask.mode;
|
||||
task=newtask;
|
||||
};
|
||||
newtask.mode=TASK_NONE;
|
||||
taskmutex.unlock();
|
||||
|
||||
if (newmode==TASK_START){
|
||||
if (current_filename!=task.filename)
|
||||
{
|
||||
current_filename=task.filename;
|
||||
//task.startpos=0.0;
|
||||
};
|
||||
ai = std::make_unique<AInputS>(g_audioformatmanager.get());
|
||||
if (ai->openAudioFile(task.filename))
|
||||
{
|
||||
info.samplerate=ai->info.samplerate;
|
||||
mode=MODE_PREPARING;
|
||||
ai->seek(task.startpos);
|
||||
if (task.m_loop_start>=0.0 && task.m_loop_end>task.m_loop_start)
|
||||
{
|
||||
ai->m_loop_start = task.m_loop_start;
|
||||
ai->m_loop_end = task.m_loop_end;
|
||||
ai->m_loop_enabled = true;
|
||||
} else
|
||||
{
|
||||
ai->m_loop_enabled = false;
|
||||
}
|
||||
bufmutex.lock();
|
||||
m_stretchers.resize(task.m_num_outchans);
|
||||
for (int i=0;i<m_stretchers.size();++i)
|
||||
{
|
||||
m_stretchers[i]=std::make_shared<ProcessedStretch>(task.rap,task.fftsize,window_type,task.bypass,ai->info.samplerate,i+1);
|
||||
m_stretchers[i]->set_parameters(task.ppar);
|
||||
}
|
||||
binaural_beats=std::make_unique<BinauralBeats>(ai->info.samplerate);
|
||||
|
||||
if (binaural_beats)
|
||||
binaural_beats->pars=*(task.bbpar);
|
||||
|
||||
inbufsize=m_stretchers[0]->get_max_bufsize();
|
||||
|
||||
inbuf.m_inbuf.resize(task.m_num_outchans);
|
||||
for (int i=0;i<inbuf.m_inbuf.size();++i)
|
||||
inbuf.m_inbuf[i].resize(inbufsize);
|
||||
for (int i=0;i<inbufsize;i++)
|
||||
for (int j=0;j<inbuf.m_inbuf.size();++j)
|
||||
inbuf.m_inbuf[j][i]=0.0;
|
||||
|
||||
|
||||
inbuf_i.resize(inbufsize*task.m_num_outchans);
|
||||
for (int i=0;i<inbufsize*task.m_num_outchans;i++)
|
||||
{
|
||||
inbuf_i[i]=0;
|
||||
};
|
||||
first_in_buf=true;
|
||||
|
||||
outbuf.size=m_stretchers[0]->get_bufsize();
|
||||
|
||||
int min_samples=ai->info.samplerate*m_prebufferamount;
|
||||
int hwbufsize = 1024;
|
||||
int n=2*hwbufsize/outbuf.size;
|
||||
if (n<3) n=3;//min 3 buffers
|
||||
if (n<(min_samples/outbuf.size)) n=(min_samples/outbuf.size);//the internal buffers sums "min_samples" amount
|
||||
outbuf.n=n;
|
||||
outbuf.nfresh=0;
|
||||
outbuf.channeldatas.resize(task.m_num_outchans);
|
||||
for (int i=0;i<task.m_num_outchans;++i)
|
||||
outbuf.channeldatas[i].resize(outbuf.n);
|
||||
outbuf.computek=0;
|
||||
outbuf.outk=0;
|
||||
outbuf.outpos=0;
|
||||
outbuf.in_position.resize(outbuf.n);
|
||||
for (int j=0;j<outbuf.n;j++){
|
||||
for (int k=0;k<task.m_num_outchans;++k)
|
||||
{
|
||||
outbuf.channeldatas[k][j].resize(outbuf.size);
|
||||
for (int i=0;i<outbuf.size;i++)
|
||||
outbuf.channeldatas[k][j][i]=0.0;
|
||||
}
|
||||
|
||||
outbuf.in_position[j]=0.0;
|
||||
};
|
||||
|
||||
bufmutex.unlock();
|
||||
|
||||
};
|
||||
};
|
||||
if (newmode==TASK_SEEK){
|
||||
if (ai) ai->seek(task.startpos);
|
||||
first_in_buf=true;
|
||||
};
|
||||
if (newmode==TASK_RAP)
|
||||
{
|
||||
for (int i=0;i<m_stretchers.size();++i)
|
||||
m_stretchers[i]->set_rap(task.rap);
|
||||
|
||||
};
|
||||
if (newmode==TASK_PARAMETERS){
|
||||
for (int i=0;i<m_stretchers.size();++i)
|
||||
m_stretchers[i]->set_parameters(task.ppar);
|
||||
if (binaural_beats) binaural_beats->pars=*(task.bbpar);
|
||||
};
|
||||
};
|
||||
|
||||
void Player::computesamples(){
|
||||
bufmutex.lock();
|
||||
|
||||
bool exitnow=(outbuf.n==0);
|
||||
if (outbuf.nfresh>=(outbuf.n-1)) exitnow=true;//buffers are full
|
||||
|
||||
bufmutex.unlock();
|
||||
if (exitnow) {
|
||||
if (mode==MODE_PREPARING) {
|
||||
info.playing=true;
|
||||
mode=MODE_PLAY;
|
||||
};
|
||||
sleep(10);
|
||||
return;
|
||||
};
|
||||
int outchs = task.m_num_outchans;
|
||||
bool eof=false;
|
||||
if (!ai)
|
||||
eof=true;
|
||||
else if (ai->eof) eof=true;
|
||||
if (eof)
|
||||
{
|
||||
|
||||
for (int i=0;i<inbufsize*outchs;i++)
|
||||
{
|
||||
inbuf_i[i] = 0.0;
|
||||
};
|
||||
outbuf.nfresh++;
|
||||
bufmutex.lock();
|
||||
for (int i=0;i<outbuf.size;i++)
|
||||
{
|
||||
for (int j=0;j<outchs;++j)
|
||||
outbuf.channeldatas[j][outbuf.computek][i]=0.0;
|
||||
};
|
||||
outbuf.computek++;
|
||||
if (outbuf.computek>=outbuf.n){
|
||||
outbuf.computek=0;
|
||||
};
|
||||
bufmutex.unlock();
|
||||
info.eof=true;
|
||||
return;
|
||||
};
|
||||
|
||||
bool result=true;
|
||||
float in_pos_100=(REALTYPE) ai->info.currentsample/(REALTYPE)ai->info.nsamples*100.0;
|
||||
info.liveposition = in_pos_100/100.0;
|
||||
int readsize=m_stretchers[0]->get_nsamples(in_pos_100);
|
||||
|
||||
|
||||
if (first_in_buf)
|
||||
readsize=m_stretchers[0]->get_nsamples_for_fill();
|
||||
else if (freeze_mode) readsize=0;
|
||||
if (readsize)
|
||||
result=(ai->read(readsize,outchs,inbuf_i.data())==(readsize));
|
||||
if (result)
|
||||
{
|
||||
float in_pos=(REALTYPE) ai->info.currentsample/(REALTYPE)ai->info.nsamples;
|
||||
|
||||
if (ai->eof) in_pos=0.0;
|
||||
|
||||
for (int i=0;i<readsize;i++)
|
||||
{
|
||||
for (int j=0;j<inbuf.m_inbuf.size();++j)
|
||||
inbuf.m_inbuf[j][i]=inbuf_i[i*outchs+j];
|
||||
|
||||
};
|
||||
REALTYPE max_onset = std::numeric_limits<REALTYPE>::min();
|
||||
for (int i=0;i<m_stretchers.size();++i)
|
||||
{
|
||||
m_stretchers[i]->window_type=window_type;
|
||||
REALTYPE s_onset=onset_detection_sensitivity;
|
||||
m_stretchers[i]->set_onset_detection_sensitivity(s_onset);
|
||||
bool freezing=freeze_mode&&(!first_in_buf);
|
||||
m_stretchers[i]->set_freezing(freezing);
|
||||
|
||||
REALTYPE onset_amt=m_stretchers[i]->process(inbuf.m_inbuf[i].data(),readsize);
|
||||
max_onset = std::max(onset_amt,max_onset);
|
||||
}
|
||||
for (int i=0;i<m_stretchers.size();++i)
|
||||
m_stretchers[i]->here_is_onset(max_onset);
|
||||
binaural_beats->process(m_stretchers[0]->out_buf.data(),m_stretchers[1]->out_buf.data(),
|
||||
m_stretchers[0]->get_bufsize(),in_pos_100);
|
||||
// stretchl->process_output(stretchl->out_buf,stretchl->out_bufsize);
|
||||
// stretchr->process_output(stretchr->out_buf,stretchr->out_bufsize);
|
||||
int nskip=m_stretchers[0]->get_skip_nsamples();
|
||||
if (nskip>0) ai->skip(nskip);
|
||||
|
||||
|
||||
first_in_buf=false;
|
||||
bufmutex.lock();
|
||||
|
||||
for (int ch=0;ch<outchs;++ch)
|
||||
{
|
||||
for (int i=0;i<outbuf.size;i++)
|
||||
{
|
||||
REALTYPE out_sample=m_stretchers[ch]->out_buf[i];
|
||||
outbuf.channeldatas[ch][outbuf.computek][i]=out_sample;
|
||||
}
|
||||
}
|
||||
outbuf.in_position[outbuf.computek]=in_pos;
|
||||
outbuf.computek++;
|
||||
if (outbuf.computek>=outbuf.n){
|
||||
outbuf.computek=0;
|
||||
};
|
||||
bufmutex.unlock();
|
||||
outbuf.nfresh++;
|
||||
}else{
|
||||
info.eof=true;
|
||||
mode=MODE_STOP;
|
||||
stop();
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
137
Source/PS_Source/Player.h
Normal file
137
Source/PS_Source/Player.h
Normal file
@ -0,0 +1,137 @@
|
||||
/*
|
||||
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
|
||||
|
||||
#ifdef PLAYERCLASSSTILLPRESENT
|
||||
|
||||
#include <string>
|
||||
#include "Input/AInputS.h"
|
||||
#include "ProcessedStretch.h"
|
||||
#include "BinauralBeats.h"
|
||||
#include "Mutex.h"
|
||||
#include "globals.h"
|
||||
|
||||
using StretchRef = std::shared_ptr<ProcessedStretch>;
|
||||
|
||||
class Player:public Thread{
|
||||
public:
|
||||
Player();
|
||||
~Player();
|
||||
|
||||
void startplay(String filename, REALTYPE startpos,REALTYPE rap, int fftsize,bool bypass,ProcessParameters *ppar,
|
||||
BinauralBeatsParameters *bbpar, double loop_start, double loop_end, int numoutchans);
|
||||
//startpos is from 0 (start) to 1.0 (end of file)
|
||||
void stop();
|
||||
void pause();
|
||||
|
||||
void freeze();
|
||||
void setrap(REALTYPE newrap);
|
||||
|
||||
void seek(REALTYPE pos);
|
||||
|
||||
|
||||
|
||||
void getaudiobuffer(int nsamples, float *out);//data este stereo
|
||||
|
||||
enum ModeType{
|
||||
MODE_PLAY,MODE_STOP,MODE_PREPARING,MODE_PAUSE
|
||||
};
|
||||
|
||||
ModeType getmode();
|
||||
|
||||
struct player_soundfile_info_t{
|
||||
float position = 0.0f;//0 is for start, 1 for end
|
||||
double liveposition = 0.0;
|
||||
int playing=0;
|
||||
int samplerate=0;
|
||||
bool eof=false;
|
||||
}info;
|
||||
|
||||
bool is_freeze(){
|
||||
return freeze_mode;
|
||||
};
|
||||
void set_window_type(FFTWindow window);
|
||||
|
||||
void set_volume(REALTYPE vol);
|
||||
void set_onset_detection_sensitivity(REALTYPE onset);
|
||||
|
||||
void set_process_parameters(ProcessParameters *ppar,BinauralBeatsParameters *bbpar);
|
||||
|
||||
std::unique_ptr<BinauralBeats> binaural_beats;
|
||||
void setPreBufferAmount(double x) { m_prebufferamount = jlimit(0.1,2.0, x); }
|
||||
private:
|
||||
void run() override;
|
||||
|
||||
std::unique_ptr<InputS> ai;
|
||||
|
||||
std::vector<StretchRef> m_stretchers;
|
||||
|
||||
std::vector<float> inbuf_i;
|
||||
int inbufsize;
|
||||
|
||||
Mutex taskmutex,bufmutex;
|
||||
|
||||
ModeType mode;
|
||||
|
||||
enum TaskMode{
|
||||
TASK_NONE, TASK_START, TASK_STOP,TASK_SEEK,TASK_RAP,TASK_PARAMETERS, TASK_ONSET
|
||||
};
|
||||
struct player_task_t {
|
||||
TaskMode mode;
|
||||
|
||||
REALTYPE startpos=0.0;
|
||||
REALTYPE rap;
|
||||
int fftsize;
|
||||
String filename;
|
||||
int m_num_outchans = 2;
|
||||
bool bypass;
|
||||
ProcessParameters *ppar;
|
||||
BinauralBeatsParameters *bbpar;
|
||||
double m_loop_start = 0.0;
|
||||
double m_loop_end = 1.0;
|
||||
}newtask,task;
|
||||
|
||||
struct{
|
||||
float2dvector m_inbuf;
|
||||
}inbuf;
|
||||
|
||||
|
||||
struct player_outbuf_t{
|
||||
int n;//how many buffers
|
||||
float3dvector channeldatas;
|
||||
int size;//size of one buffer
|
||||
int computek,outk;//current buffer
|
||||
int outpos;//the sample position in the current buffer (for out)
|
||||
int nfresh;//how many buffers are fresh added and need to be played
|
||||
//nfresh==0 for empty buffers, nfresh==n-1 for full buffers
|
||||
std::vector<float> in_position;//the position(for input samples inside the input file) of each buffers
|
||||
}outbuf;
|
||||
bool first_in_buf;
|
||||
|
||||
void newtaskcheck();
|
||||
void computesamples();
|
||||
bool freeze_mode,bypass_mode,paused;
|
||||
REALTYPE volume,onset_detection_sensitivity;
|
||||
|
||||
String current_filename;
|
||||
FFTWindow window_type;
|
||||
double m_prebufferamount = 0.2;
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Player)
|
||||
};
|
||||
#endif
|
452
Source/PS_Source/ProcessedStretch.cpp
Normal file
452
Source/PS_Source/ProcessedStretch.cpp
Normal file
@ -0,0 +1,452 @@
|
||||
/*
|
||||
Copyright (C) 2009 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
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "ProcessedStretch.h"
|
||||
|
||||
extern std::unique_ptr<PropertiesFile> g_propsfile;
|
||||
|
||||
ProcessedStretch::ProcessedStretch(REALTYPE rap_,int in_bufsize_,FFTWindow w,bool bypass_,REALTYPE samplerate_,int stereo_mode_)
|
||||
: Stretch(rap_,in_bufsize_,w,bypass_,samplerate_,stereo_mode_)
|
||||
{
|
||||
|
||||
|
||||
};
|
||||
|
||||
ProcessedStretch::~ProcessedStretch()
|
||||
{
|
||||
|
||||
// delete [] fbfreq;
|
||||
};
|
||||
|
||||
void ProcessedStretch::set_parameters(ProcessParameters *ppar)
|
||||
{
|
||||
pars=*ppar;
|
||||
//update_free_filter();
|
||||
}
|
||||
void ProcessedStretch::setBufferSize(int sz)
|
||||
{
|
||||
Stretch::setBufferSize(sz);
|
||||
nfreq = bufsize;
|
||||
infreq = floatvector(nfreq);
|
||||
sumfreq = floatvector(nfreq);
|
||||
tmpfreq1 = floatvector(nfreq);
|
||||
tmpfreq2 = floatvector(nfreq);
|
||||
//fbfreq=new REALTYPE[nfreq];
|
||||
free_filter_freqs = floatvector(nfreq);
|
||||
for (int i = 0; i<nfreq; i++) {
|
||||
free_filter_freqs[i] = 1.0;
|
||||
// fbfreq[i]=0.0;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
void ProcessedStretch::copy(const realvector& freq1,realvector& freq2)
|
||||
{
|
||||
for (int i=0;i<nfreq;i++) freq2[i]=freq1[i];
|
||||
};
|
||||
*/
|
||||
|
||||
void ProcessedStretch::copy(REALTYPE* freq1, REALTYPE* freq2)
|
||||
{
|
||||
for (int i = 0; i<nfreq; i++) freq2[i] = freq1[i];
|
||||
};
|
||||
|
||||
void ProcessedStretch::add(REALTYPE *freq2,REALTYPE *freq1,REALTYPE a){
|
||||
for (int i=0;i<nfreq;i++) freq2[i]+=freq1[i]*a;
|
||||
};
|
||||
|
||||
void ProcessedStretch::mul(REALTYPE *freq1,REALTYPE a){
|
||||
for (int i=0;i<nfreq;i++) freq1[i]*=a;
|
||||
};
|
||||
|
||||
void ProcessedStretch::zero(REALTYPE *freq1){
|
||||
for (int i=0;i<nfreq;i++) freq1[i]=0.0;
|
||||
};
|
||||
|
||||
REALTYPE ProcessedStretch::get_stretch_multiplier(REALTYPE pos_percents){
|
||||
REALTYPE result=1.0;
|
||||
/*
|
||||
if (pars.stretch_multiplier.get_enabled()){
|
||||
result*=pars.stretch_multiplier.get_value(pos_percents);
|
||||
};
|
||||
*/
|
||||
///REALTYPE transient=pars.get_transient(pos_percents);
|
||||
///printf("\n%g\n",transient);
|
||||
///REALTYPE threshold=0.05;
|
||||
///REALTYPE power=1000.0;
|
||||
///transient-=threshold;
|
||||
///if (transient>0){
|
||||
/// transient*=power*(1.0+power);
|
||||
/// result/=(1.0+transient);
|
||||
///};
|
||||
///printf("tr=%g\n",result);
|
||||
return result;
|
||||
};
|
||||
|
||||
void ProcessedStretch::process_spectrum(REALTYPE *freq)
|
||||
{
|
||||
for (auto& e : m_spectrum_processes)
|
||||
{
|
||||
copy(freq, infreq.data());
|
||||
if (e == 0 && pars.harmonics.enabled)
|
||||
do_harmonics(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)
|
||||
do_freq_shift(infreq.data(), freq);
|
||||
if (e == 3 && pars.pitch_shift.enabled)
|
||||
do_pitch_shift(infreq.data(), freq, pow(2.0f, pars.pitch_shift.cents / 1200.0f));
|
||||
if (e == 4 && pars.octave.enabled)
|
||||
do_octave(infreq.data(), freq);
|
||||
if (e == 5 && pars.spread.enabled)
|
||||
do_spread(infreq.data(), freq);
|
||||
if (e == 6 && pars.filter.enabled)
|
||||
do_filter(infreq.data(), freq);
|
||||
if (e == 7 && pars.compressor.enabled)
|
||||
do_compressor(infreq.data(), freq);
|
||||
}
|
||||
|
||||
#ifdef USE_OLD_SPEC_PROC
|
||||
if (pars.harmonics.enabled) {
|
||||
copy(freq,infreq.data());
|
||||
do_harmonics(infreq.data(),freq);
|
||||
};
|
||||
|
||||
if (pars.tonal_vs_noise.enabled){
|
||||
copy(freq,infreq.data());
|
||||
do_tonal_vs_noise(infreq.data(),freq);
|
||||
};
|
||||
|
||||
if (pars.freq_shift.enabled) {
|
||||
copy(freq,infreq.data());
|
||||
do_freq_shift(infreq.data(),freq);
|
||||
};
|
||||
if (pars.pitch_shift.enabled) {
|
||||
copy(freq,infreq.data());
|
||||
do_pitch_shift(infreq.data(),freq,pow(2.0,pars.pitch_shift.cents/1200.0));
|
||||
};
|
||||
if (pars.octave.enabled){
|
||||
copy(freq,infreq.data());
|
||||
do_octave(infreq.data(),freq);
|
||||
};
|
||||
|
||||
|
||||
if (pars.spread.enabled){
|
||||
copy(freq,infreq.data());
|
||||
do_spread(infreq.data(),freq);
|
||||
};
|
||||
|
||||
|
||||
if (pars.filter.enabled){
|
||||
copy(freq,infreq.data());
|
||||
do_filter(infreq.data(),freq);
|
||||
};
|
||||
|
||||
if (pars.free_filter.get_enabled()){
|
||||
copy(freq,infreq.data());
|
||||
do_free_filter(infreq.data(),freq);
|
||||
};
|
||||
|
||||
if (pars.compressor.enabled){
|
||||
copy(freq,infreq.data());
|
||||
do_compressor(infreq.data(),freq);
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
//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;
|
||||
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;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
void ProcessedStretch::do_freq_shift(REALTYPE *freq1,REALTYPE *freq2){
|
||||
zero(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];
|
||||
};
|
||||
};
|
||||
void ProcessedStretch::do_pitch_shift(REALTYPE *freq1,REALTYPE *freq2,REALTYPE _rap){
|
||||
zero(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)];
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
void ProcessedStretch::do_octave(REALTYPE *freq1,REALTYPE *freq2){
|
||||
zero(sumfreq.data());
|
||||
if (pars.octave.om2>1e-3){
|
||||
do_pitch_shift(freq1,tmpfreq1.data(),0.25);
|
||||
add(sumfreq.data(),tmpfreq1.data(),pars.octave.om2);
|
||||
};
|
||||
if (pars.octave.om1>1e-3){
|
||||
do_pitch_shift(freq1,tmpfreq1.data(),0.5);
|
||||
add(sumfreq.data(),tmpfreq1.data(),pars.octave.om1);
|
||||
};
|
||||
if (pars.octave.o0>1e-3){
|
||||
add(sumfreq.data(),freq1,pars.octave.o0);
|
||||
};
|
||||
if (pars.octave.o1>1e-3){
|
||||
do_pitch_shift(freq1,tmpfreq1.data(),2.0);
|
||||
add(sumfreq.data(),tmpfreq1.data(),pars.octave.o1);
|
||||
};
|
||||
if (pars.octave.o15>1e-3){
|
||||
do_pitch_shift(freq1,tmpfreq1.data(),3.0);
|
||||
add(sumfreq.data(),tmpfreq1.data(),pars.octave.o15);
|
||||
};
|
||||
if (pars.octave.o2>1e-3){
|
||||
do_pitch_shift(freq1,tmpfreq1.data(),4.0);
|
||||
add(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;
|
||||
};
|
||||
|
||||
void ProcessedStretch::do_filter(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;
|
||||
};
|
||||
};
|
||||
|
||||
void ProcessedStretch::update_free_filter()
|
||||
{
|
||||
/*
|
||||
pars.free_filter.update_curve();
|
||||
if (pars.free_filter.get_enabled()) {
|
||||
for (int i=0;i<nfreq;i++){
|
||||
REALTYPE freq=(REALTYPE)i/(REALTYPE) nfreq*samplerate*0.5f;
|
||||
free_filter_freqs[i]=pars.free_filter.get_value(freq);
|
||||
};
|
||||
}else{
|
||||
for (int i=0;i<nfreq;i++){
|
||||
free_filter_freqs[i]=1.0f;
|
||||
};
|
||||
};
|
||||
*/
|
||||
};
|
||||
|
||||
void ProcessedStretch::do_free_filter(REALTYPE *freq1,REALTYPE *freq2){
|
||||
for (int i=0;i<nfreq;i++){
|
||||
freq2[i]=freq1[i]*free_filter_freqs[i];
|
||||
};
|
||||
};
|
||||
|
||||
void ProcessedStretch::do_spread(REALTYPE *freq1,REALTYPE *freq2){
|
||||
spread(freq1,freq2,pars.spread.bandwidth);
|
||||
};
|
||||
|
||||
void ProcessedStretch::spread(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;
|
||||
};
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
void ProcessedStretch::do_compressor(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;
|
||||
};
|
||||
|
||||
void ProcessedStretch::do_tonal_vs_noise(REALTYPE *freq1,REALTYPE *freq2){
|
||||
spread(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;
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
std::vector<SpectrumProcess> make_spectrum_processes()
|
||||
{
|
||||
std::vector<SpectrumProcess> m_spectrum_processes;
|
||||
m_spectrum_processes.emplace_back("Harmonics",0);
|
||||
m_spectrum_processes.emplace_back("Tonal vs Noise",1);
|
||||
m_spectrum_processes.emplace_back("Frequency shift",2);
|
||||
m_spectrum_processes.emplace_back("Pitch shift",3);
|
||||
m_spectrum_processes.emplace_back("Octaves mix",4);
|
||||
m_spectrum_processes.emplace_back("Spread",5);
|
||||
m_spectrum_processes.emplace_back("Filter",6);
|
||||
m_spectrum_processes.emplace_back("Compressor",7);
|
||||
return m_spectrum_processes;
|
||||
}
|
167
Source/PS_Source/ProcessedStretch.h
Normal file
167
Source/PS_Source/ProcessedStretch.h
Normal file
@ -0,0 +1,167 @@
|
||||
/*
|
||||
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 "FreeEdit.h"
|
||||
#include "Stretch.h"
|
||||
|
||||
struct ProcessParameters{
|
||||
ProcessParameters(){
|
||||
pitch_shift.enabled=false;
|
||||
pitch_shift.cents=0;
|
||||
|
||||
octave.enabled=false;
|
||||
octave.om2=octave.om1=octave.o1=octave.o15=octave.o2=0.0f;
|
||||
octave.o0=1.0f;
|
||||
|
||||
freq_shift.enabled=false;
|
||||
freq_shift.Hz=0;
|
||||
|
||||
compressor.enabled=false;
|
||||
compressor.power=0.0f;
|
||||
|
||||
filter.enabled=false;
|
||||
filter.stop=false;
|
||||
filter.low=0.0f;
|
||||
filter.high=22000.0f;
|
||||
filter.hdamp=0.0f;
|
||||
|
||||
harmonics.enabled=false;
|
||||
harmonics.freq=440.0f;
|
||||
harmonics.bandwidth=25.0f;
|
||||
harmonics.nharmonics=10;
|
||||
harmonics.gauss=false;
|
||||
|
||||
spread.enabled=false;
|
||||
spread.bandwidth=0.3f;
|
||||
|
||||
tonal_vs_noise.enabled=false;
|
||||
tonal_vs_noise.preserve=0.5f;
|
||||
tonal_vs_noise.bandwidth=0.9f;
|
||||
};
|
||||
~ProcessParameters(){
|
||||
};
|
||||
|
||||
|
||||
struct{
|
||||
bool enabled;
|
||||
int cents;
|
||||
}pitch_shift;
|
||||
|
||||
struct{
|
||||
bool enabled;
|
||||
REALTYPE om2,om1,o0,o1,o15,o2;
|
||||
}octave;
|
||||
|
||||
struct{
|
||||
bool enabled;
|
||||
int Hz;
|
||||
}freq_shift;
|
||||
|
||||
struct{
|
||||
bool enabled;
|
||||
REALTYPE power;
|
||||
}compressor;
|
||||
|
||||
struct{
|
||||
bool enabled;
|
||||
REALTYPE low,high;
|
||||
REALTYPE hdamp;
|
||||
bool stop;
|
||||
}filter;
|
||||
|
||||
struct{
|
||||
bool enabled;
|
||||
REALTYPE freq;
|
||||
REALTYPE bandwidth;
|
||||
int nharmonics;
|
||||
bool gauss;
|
||||
}harmonics;
|
||||
|
||||
struct{
|
||||
bool enabled;
|
||||
REALTYPE bandwidth;
|
||||
}spread;
|
||||
|
||||
struct{
|
||||
bool enabled;
|
||||
REALTYPE preserve;
|
||||
REALTYPE bandwidth;
|
||||
}tonal_vs_noise;
|
||||
|
||||
//FreeEdit free_filter;
|
||||
//FreeEdit stretch_multiplier;
|
||||
|
||||
};
|
||||
|
||||
class SpectrumProcess
|
||||
{
|
||||
public:
|
||||
SpectrumProcess() {}
|
||||
SpectrumProcess(String name, int index) :
|
||||
m_name(name), m_index(index) {}
|
||||
String m_name;
|
||||
int m_index = -1;
|
||||
private:
|
||||
};
|
||||
|
||||
std::vector<SpectrumProcess> make_spectrum_processes();
|
||||
|
||||
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);
|
||||
std::vector<int> 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;
|
||||
void do_harmonics(REALTYPE *freq1,REALTYPE *freq2);
|
||||
void do_pitch_shift(REALTYPE *freq1,REALTYPE *freq2,REALTYPE rap);
|
||||
void do_freq_shift(REALTYPE *freq1,REALTYPE *freq2);
|
||||
void do_octave(REALTYPE *freq1,REALTYPE *freq2);
|
||||
void do_filter(REALTYPE *freq1,REALTYPE *freq2);
|
||||
void do_free_filter(REALTYPE *freq1,REALTYPE *freq2);
|
||||
void do_compressor(REALTYPE *freq1,REALTYPE *freq2);
|
||||
void do_spread(REALTYPE *freq1,REALTYPE *freq2);
|
||||
void do_tonal_vs_noise(REALTYPE *freq1,REALTYPE *freq2);
|
||||
|
||||
//void copy(const realvector& freq1,realvector& freq2);
|
||||
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 spread(REALTYPE *freq1,REALTYPE *freq2,REALTYPE spread_bandwidth);
|
||||
|
||||
void update_free_filter();
|
||||
int nfreq=0;
|
||||
|
||||
std::vector<REALTYPE> free_filter_freqs;
|
||||
ProcessParameters pars;
|
||||
|
||||
std::vector<REALTYPE> infreq,sumfreq,tmpfreq1,tmpfreq2;
|
||||
|
||||
//REALTYPE *fbfreq;
|
||||
};
|
376
Source/PS_Source/Stretch.cpp
Normal file
376
Source/PS_Source/Stretch.cpp
Normal file
@ -0,0 +1,376 @@
|
||||
/*
|
||||
Copyright (C) 2006-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
|
||||
*/
|
||||
|
||||
#include "Stretch.h"
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
extern std::unique_ptr<PropertiesFile> g_propsfile;
|
||||
|
||||
FFT::FFT(int nsamples_)
|
||||
{
|
||||
nsamples=nsamples_;
|
||||
if (nsamples%2!=0) {
|
||||
nsamples+=1;
|
||||
printf("WARNING: Odd sample size on FFT::FFT() (%d)",nsamples);
|
||||
};
|
||||
smp.resize(nsamples);
|
||||
for (int i = 0; i < nsamples; i++) smp[i] = 0.0;
|
||||
freq.resize(nsamples/2+1);
|
||||
for (int i=0;i<nsamples/2+1;i++) freq[i]=0.0;
|
||||
window.data.resize(nsamples);
|
||||
for (int i=0;i<nsamples;i++) window.data[i]=0.707f;
|
||||
window.type=W_RECTANGULAR;
|
||||
|
||||
|
||||
data.resize(nsamples,true);
|
||||
bool allow_long_planning = g_propsfile->getBoolValue("fftw_allow_long_planning",false);
|
||||
//double t0 = Time::getMillisecondCounterHiRes();
|
||||
if (allow_long_planning)
|
||||
{
|
||||
//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);
|
||||
} 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);
|
||||
}
|
||||
//double t1 = Time::getMillisecondCounterHiRes();
|
||||
//Logger::writeToLog("Creating FFTW3 plans took "+String(t1-t0)+ "ms");
|
||||
|
||||
//timeCall("Initing FFT random generator", [this]()
|
||||
//{
|
||||
std::random_device rand_device;
|
||||
m_randgen = std::mt19937(rand_device());
|
||||
//});
|
||||
};
|
||||
|
||||
FFT::~FFT()
|
||||
{
|
||||
fftwf_destroy_plan(planfftw);
|
||||
fftwf_destroy_plan(planifftw);
|
||||
};
|
||||
|
||||
void FFT::smp2freq()
|
||||
{
|
||||
|
||||
for (int i=0;i<nsamples;i++)
|
||||
data[i]=smp[i];
|
||||
fftwf_execute(planfftw);
|
||||
|
||||
|
||||
for (int i=1;i<nsamples/2;i++)
|
||||
{
|
||||
|
||||
REALTYPE c=data[i];
|
||||
REALTYPE s=data[nsamples-i];
|
||||
|
||||
freq[i]=sqrt(c*c+s*s);
|
||||
};
|
||||
freq[0]=0.0;
|
||||
};
|
||||
|
||||
void FFT::freq2smp()
|
||||
{
|
||||
REALTYPE inv_2p15_2pi=1.0f/16384.0f*(float)M_PI;
|
||||
for (int i=1;i<nsamples/2;i++)
|
||||
{
|
||||
unsigned int rand = m_randdist(m_randgen);
|
||||
REALTYPE phase=rand*inv_2p15_2pi;
|
||||
|
||||
data[i]=freq[i]*cos(phase);
|
||||
data[nsamples-i]=freq[i]*sin(phase);
|
||||
|
||||
};
|
||||
data[0]=data[nsamples/2+1]=data[nsamples/2]=0.0;
|
||||
fftwf_execute(planifftw);
|
||||
for (int i=0;i<nsamples;i++)
|
||||
smp[i]=data[i]/nsamples;
|
||||
|
||||
};
|
||||
|
||||
void FFT::applywindow(FFTWindow type)
|
||||
{
|
||||
if (window.type!=type){
|
||||
window.type=type;
|
||||
switch (type){
|
||||
case W_RECTANGULAR:
|
||||
for (int i=0;i<nsamples;i++) window.data[i]=0.707f;
|
||||
break;
|
||||
case W_HAMMING:
|
||||
for (int i=0;i<nsamples;i++) window.data[i]=(float)(0.53836-0.46164*cos(2.0*M_PI*i/(nsamples+1.0)));
|
||||
break;
|
||||
case W_HANN:
|
||||
for (int i=0;i<nsamples;i++) window.data[i]=(float)(0.5*(1.0-cos(2*M_PI*i/(nsamples-1.0))));
|
||||
break;
|
||||
case W_BLACKMAN:
|
||||
for (int i=0;i<nsamples;i++) window.data[i]=(float)(0.42-0.5*cos(2*M_PI*i/(nsamples-1.0))+0.08*cos(4*M_PI*i/(nsamples-1.0)));
|
||||
break;
|
||||
case W_BLACKMAN_HARRIS:
|
||||
for (int i=0;i<nsamples;i++) window.data[i]=(float)(0.35875-0.48829*cos(2*M_PI*i/(nsamples-1.0))+0.14128*cos(4*M_PI*i/(nsamples-1.0))-0.01168*cos(6*M_PI*i/(nsamples-1.0)));
|
||||
break;
|
||||
|
||||
};
|
||||
};
|
||||
for (int i=0;i<nsamples;i++) smp[i]*=window.data[i];
|
||||
};
|
||||
|
||||
/*******************************************/
|
||||
|
||||
|
||||
Stretch::Stretch(REALTYPE rap_,int /*bufsize_*/,FFTWindow w,bool bypass_,REALTYPE samplerate_,int /*stereo_mode_*/)
|
||||
{
|
||||
freezing=false;
|
||||
onset_detection_sensitivity=0.0;
|
||||
|
||||
samplerate=samplerate_;
|
||||
rap=rap_;
|
||||
bypass = bypass_;
|
||||
|
||||
remained_samples=0.0;
|
||||
window_type=w;
|
||||
require_new_buffer=false;
|
||||
c_pos_percents=0.0;
|
||||
extra_onset_time_credit=0.0;
|
||||
skip_samples=0;
|
||||
};
|
||||
|
||||
Stretch::~Stretch()
|
||||
{
|
||||
};
|
||||
|
||||
void Stretch::set_rap(REALTYPE newrap){
|
||||
//if ((rap>=1.0)&&(newrap>=1.0))
|
||||
rap=newrap;
|
||||
};
|
||||
|
||||
void Stretch::do_analyse_inbuf(REALTYPE *smps){
|
||||
//get the frequencies
|
||||
for (int i=0;i<bufsize;i++) {
|
||||
infft->smp[i]=old_smps[i];
|
||||
infft->smp[i+bufsize]=smps[i];
|
||||
|
||||
old_freq[i]=infft->freq[i];
|
||||
};
|
||||
infft->applywindow(window_type);
|
||||
infft->smp2freq();
|
||||
};
|
||||
|
||||
void Stretch::do_next_inbuf_smps(REALTYPE *smps){
|
||||
for (int i=0;i<bufsize;i++) {
|
||||
very_old_smps[i]=old_smps[i];
|
||||
old_smps[i]=new_smps[i];
|
||||
new_smps[i]=smps[i];
|
||||
};
|
||||
};
|
||||
|
||||
REALTYPE Stretch::do_detect_onset(){
|
||||
REALTYPE result=0.0;
|
||||
if (onset_detection_sensitivity>1e-3){
|
||||
REALTYPE os=0.0,osinc=0.0;
|
||||
REALTYPE osincold=1e-5f;
|
||||
int maxk=1+(int)(bufsize*500.0/(samplerate*0.5));
|
||||
int k=0;
|
||||
for (int i=0;i<bufsize;i++) {
|
||||
osinc+=infft->freq[i]-old_freq[i];
|
||||
osincold+=old_freq[i];
|
||||
if (k>=maxk) {
|
||||
k=0;
|
||||
os+=osinc/osincold;
|
||||
osinc=0;
|
||||
};
|
||||
k++;
|
||||
};
|
||||
os+=osinc;
|
||||
if (os<0.0) os=0.0;
|
||||
//if (os>1.0) os=1.0;
|
||||
|
||||
REALTYPE os_strength=(float)(pow(20.0,1.0-onset_detection_sensitivity)-1.0);
|
||||
REALTYPE os_strength_h=os_strength*0.75f;
|
||||
if (os>os_strength_h){
|
||||
result=(os-os_strength_h)/(os_strength-os_strength_h);
|
||||
if (result>1.0f) result=1.0f;
|
||||
};
|
||||
|
||||
if (result>1.0f) result=1.0f;
|
||||
};
|
||||
return result;
|
||||
};
|
||||
|
||||
void Stretch::setBufferSize(int bufsize_)
|
||||
{
|
||||
if (bufsize == 0 || bufsize_ != bufsize)
|
||||
{
|
||||
bufsize = bufsize_;
|
||||
|
||||
if (bufsize < 8) bufsize = 8;
|
||||
|
||||
out_buf = floatvector(bufsize);
|
||||
old_freq = floatvector(bufsize);
|
||||
|
||||
very_old_smps = floatvector(bufsize);
|
||||
new_smps = floatvector(bufsize);
|
||||
old_smps = floatvector(bufsize);
|
||||
|
||||
old_out_smps = floatvector(bufsize * 2);
|
||||
infft = std::make_unique<FFT>(bufsize * 2);
|
||||
fft = std::make_unique<FFT>(bufsize * 2);
|
||||
outfft = std::make_unique<FFT>(bufsize * 2);
|
||||
}
|
||||
jassert(infft != nullptr && fft != nullptr && outfft != nullptr);
|
||||
for (int i = 0; i<bufsize * 2; i++) {
|
||||
old_out_smps[i] = 0.0;
|
||||
};
|
||||
for (int i = 0; i<bufsize; i++) {
|
||||
old_freq[i] = 0.0;
|
||||
new_smps[i] = 0.0;
|
||||
old_smps[i] = 0.0;
|
||||
very_old_smps[i] = 0.0;
|
||||
};
|
||||
}
|
||||
|
||||
REALTYPE Stretch::process(REALTYPE *smps,int nsmps)
|
||||
{
|
||||
jassert(bufsize > 0);
|
||||
REALTYPE onset=0.0;
|
||||
if (bypass){
|
||||
for (int i=0;i<bufsize;i++) out_buf[i]=smps[i];
|
||||
return 0.0;
|
||||
};
|
||||
|
||||
if (smps!=NULL){
|
||||
if ((nsmps!=0)&&(nsmps!=bufsize)&&(nsmps!=get_max_bufsize())){
|
||||
printf("Warning wrong nsmps on Stretch::process() %d,%d\n",nsmps,bufsize);
|
||||
return 0.0;
|
||||
};
|
||||
if (nsmps!=0){//new data arrived: update the frequency components
|
||||
do_analyse_inbuf(smps);
|
||||
if (nsmps==get_max_bufsize()) {
|
||||
for (int k=bufsize;k<get_max_bufsize();k+=bufsize) do_analyse_inbuf(smps+k);
|
||||
};
|
||||
if (onset_detection_sensitivity>1e-3) onset=do_detect_onset();
|
||||
};
|
||||
|
||||
|
||||
//move the buffers
|
||||
if (nsmps!=0){//new data arrived: update the frequency components
|
||||
do_next_inbuf_smps(smps);
|
||||
if (nsmps==get_max_bufsize()) {
|
||||
for (int k=bufsize;k<get_max_bufsize();k+=bufsize) do_next_inbuf_smps(smps+k);
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
//construct the input fft
|
||||
int start_pos=(int)(floor(remained_samples*bufsize));
|
||||
if (start_pos>=bufsize) start_pos=bufsize-1;
|
||||
for (int i=0;i<bufsize-start_pos;i++) fft->smp[i]=very_old_smps[i+start_pos];
|
||||
for (int i=0;i<bufsize;i++) fft->smp[i+bufsize-start_pos]=old_smps[i];
|
||||
for (int i=0;i<start_pos;i++) fft->smp[i+2*bufsize-start_pos]=new_smps[i];
|
||||
//compute the output spectrum
|
||||
fft->applywindow(window_type);
|
||||
fft->smp2freq();
|
||||
for (int i=0;i<bufsize;i++) outfft->freq[i]=fft->freq[i];
|
||||
|
||||
|
||||
|
||||
//for (int i=0;i<bufsize;i++) outfft->freq[i]=infft->freq[i]*remained_samples+old_freq[i]*(1.0-remained_samples);
|
||||
|
||||
|
||||
process_spectrum(outfft->freq.data());
|
||||
|
||||
outfft->freq2smp();
|
||||
|
||||
//make the output buffer
|
||||
REALTYPE tmp=(float)(1.0/(float) bufsize*M_PI);
|
||||
REALTYPE hinv_sqrt2=0.853553390593f;//(1.0+1.0/sqrt(2))*0.5;
|
||||
|
||||
REALTYPE ampfactor=2.0f;
|
||||
|
||||
//remove the resulted unwanted amplitude modulation (caused by the interference of N and N+1 windowed buffer and compute the output buffer
|
||||
for (int i=0;i<bufsize;i++) {
|
||||
REALTYPE a=(float)((0.5+0.5*cos(i*tmp)));
|
||||
REALTYPE out=(float)(outfft->smp[i+bufsize]*(1.0-a)+old_out_smps[i]*a);
|
||||
out_buf[i]=(float)(out*(hinv_sqrt2-(1.0-hinv_sqrt2)*cos(i*2.0*tmp))*ampfactor);
|
||||
};
|
||||
|
||||
//copy the current output buffer to old buffer
|
||||
for (int i=0;i<bufsize*2;i++) old_out_smps[i]=outfft->smp[i];
|
||||
|
||||
};
|
||||
|
||||
if (!freezing){
|
||||
long double used_rap=rap*get_stretch_multiplier(c_pos_percents);
|
||||
|
||||
long double r=1.0/used_rap;
|
||||
if (extra_onset_time_credit>0){
|
||||
REALTYPE credit_get=(float)(0.5*r);//must be smaller than r
|
||||
extra_onset_time_credit-=credit_get;
|
||||
if (extra_onset_time_credit<0.0) extra_onset_time_credit=0.0;
|
||||
r-=credit_get;
|
||||
};
|
||||
|
||||
//long double old_remained_samples_test=remained_samples;
|
||||
remained_samples+=r;
|
||||
//int result=0;
|
||||
if (remained_samples>=1.0){
|
||||
skip_samples=(int)(floor(remained_samples-1.0)*bufsize);
|
||||
remained_samples=remained_samples-floor(remained_samples);
|
||||
require_new_buffer=true;
|
||||
}else{
|
||||
require_new_buffer=false;
|
||||
};
|
||||
};
|
||||
// long double rf_test=remained_samples-old_remained_samples_test;//this value should be almost like "rf" (for most of the time with the exception of changing the "ri" value) for extremely long stretches (otherwise the shown stretch value is not accurate)
|
||||
//for stretch up to 10^18x "long double" must have at least 64 bits in the fraction part (true for gcc compiler on x86 and macosx)
|
||||
return onset;
|
||||
};
|
||||
|
||||
void Stretch::here_is_onset(REALTYPE onset){
|
||||
if (freezing) return;
|
||||
if (onset>0.5){
|
||||
require_new_buffer=true;
|
||||
extra_onset_time_credit+=1.0-remained_samples;
|
||||
remained_samples=0.0;
|
||||
skip_samples=0;
|
||||
};
|
||||
};
|
||||
|
||||
int Stretch::get_nsamples(REALTYPE current_pos_percents){
|
||||
if (bypass) return bufsize;
|
||||
if (freezing) return 0;
|
||||
c_pos_percents=current_pos_percents;
|
||||
return require_new_buffer?bufsize:0;
|
||||
};
|
||||
|
||||
int Stretch::get_nsamples_for_fill(){
|
||||
return get_max_bufsize();
|
||||
};
|
||||
|
||||
int Stretch::get_skip_nsamples(){
|
||||
if (freezing||bypass) return 0;
|
||||
return skip_samples;
|
||||
};
|
||||
|
||||
REALTYPE Stretch::get_stretch_multiplier(REALTYPE /*pos_percents*/){
|
||||
return 1.0;
|
||||
};
|
||||
|
221
Source/PS_Source/Stretch.h
Normal file
221
Source/PS_Source/Stretch.h
Normal file
@ -0,0 +1,221 @@
|
||||
/*
|
||||
Copyright (C) 2006-2011 Nasca Octavian Paul
|
||||
Author: Nasca Octavian Paul
|
||||
|
||||
Author/Copyright (C) 2017 Xenakios
|
||||
|
||||
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 "globals.h"
|
||||
|
||||
#include "fftw3.h"
|
||||
|
||||
|
||||
#include "../JuceLibraryCode/JuceHeader.h"
|
||||
#include <random>
|
||||
|
||||
template<typename T>
|
||||
class FFTWBuffer
|
||||
{
|
||||
public:
|
||||
FFTWBuffer()
|
||||
{
|
||||
static_assert(std::is_floating_point<T>::value,"FFTWBuffer only works with floating point types");
|
||||
}
|
||||
~FFTWBuffer()
|
||||
{
|
||||
freeimpl(m_buf);
|
||||
}
|
||||
void resize(int size, bool clear)
|
||||
{
|
||||
// come on, zero size doesn't make sense!
|
||||
jassert(size>0);
|
||||
if (size==m_size && clear==false)
|
||||
return;
|
||||
if (m_buf)
|
||||
freeimpl(m_buf);
|
||||
mallocimpl(m_buf,size);
|
||||
|
||||
if (clear)
|
||||
for (int i=0;i<size;++i)
|
||||
m_buf[i]=T();
|
||||
m_size = size;
|
||||
}
|
||||
|
||||
T& operator[](int index)
|
||||
{
|
||||
jassert(index >= 0 && index < m_size);
|
||||
return m_buf[index];
|
||||
}
|
||||
const T& operator[](int index) const
|
||||
{
|
||||
jassert(index >= 0 && index < m_size);
|
||||
return m_buf[index];
|
||||
}
|
||||
|
||||
T* data()
|
||||
{
|
||||
// callers to this will likely just blow themselves up if they get a nullptr back
|
||||
jassert(m_buf!=nullptr);
|
||||
return m_buf;
|
||||
}
|
||||
int getSize() { return m_size; }
|
||||
FFTWBuffer(FFTWBuffer&& other) : m_buf(other.m_buf), m_size(other.m_size)
|
||||
{
|
||||
other.m_buf = nullptr;
|
||||
other.m_size = 0;
|
||||
}
|
||||
FFTWBuffer& operator = (FFTWBuffer&& other)
|
||||
{
|
||||
std::swap(other.m_buf, m_buf);
|
||||
std::swap(other.m_size, m_size);
|
||||
return *this;
|
||||
}
|
||||
// These buffers probably shouldn't be copied anywhere, so just disallow that for now
|
||||
FFTWBuffer(const FFTWBuffer&) = delete;
|
||||
FFTWBuffer& operator = (const FFTWBuffer&) = delete;
|
||||
private:
|
||||
T* m_buf = nullptr;
|
||||
int m_size = 0;
|
||||
void mallocimpl(float*& buf,int size)
|
||||
{
|
||||
buf = (float*)fftwf_malloc(size*sizeof(float));
|
||||
}
|
||||
void mallocimpl(double*& buf,int size)
|
||||
{
|
||||
buf = (double*)fftw_malloc(size*sizeof(double));
|
||||
}
|
||||
void freeimpl(float*& buf)
|
||||
{
|
||||
if (buf!=nullptr)
|
||||
{
|
||||
fftwf_free(buf);
|
||||
buf = nullptr;
|
||||
}
|
||||
}
|
||||
void freeimpl(double*& buf)
|
||||
{
|
||||
if (buf!=nullptr)
|
||||
{
|
||||
fftw_free(buf);
|
||||
buf = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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();
|
||||
void smp2freq();//input is smp, output is freq (phases are discarded)
|
||||
void freq2smp();//input is freq,output is smp (phases are random)
|
||||
void applywindow(FFTWindow type);
|
||||
std::vector<REALTYPE> smp;//size of samples/2
|
||||
std::vector<REALTYPE> freq;//size of samples
|
||||
int nsamples;
|
||||
private:
|
||||
|
||||
fftwf_plan planfftw,planifftw;
|
||||
FFTWBuffer<REALTYPE> data;
|
||||
|
||||
|
||||
struct{
|
||||
std::vector<REALTYPE> data;
|
||||
FFTWindow type;
|
||||
}window;
|
||||
|
||||
std::mt19937 m_randgen;
|
||||
std::uniform_int_distribution<unsigned int> m_randdist{0,32767};
|
||||
|
||||
|
||||
};
|
||||
|
||||
class Stretch
|
||||
{
|
||||
public:
|
||||
Stretch(REALTYPE rap_,int in_bufsize_,FFTWindow w=W_HAMMING,bool bypass_=false,REALTYPE samplerate_=44100,int stereo_mode_=0);
|
||||
//in_bufsize is also a half of a FFT buffer (in samples)
|
||||
virtual ~Stretch();
|
||||
|
||||
int get_max_bufsize(){
|
||||
return bufsize*3;
|
||||
};
|
||||
int get_bufsize(){
|
||||
return bufsize;
|
||||
};
|
||||
virtual void setBufferSize(int sz);
|
||||
REALTYPE get_onset_detection_sensitivity(){
|
||||
return onset_detection_sensitivity;
|
||||
};
|
||||
|
||||
REALTYPE process(REALTYPE *smps,int nsmps);//returns the onset value
|
||||
void set_freezing(bool new_freezing){
|
||||
freezing=new_freezing;
|
||||
};
|
||||
bool isFreezing() { return freezing; }
|
||||
|
||||
std::vector<REALTYPE> out_buf;//pot sa pun o variabila "max_out_bufsize" si asta sa fie marimea lui out_buf si pe out_bufsize sa il folosesc ca marime adaptiva
|
||||
|
||||
int get_nsamples(REALTYPE current_pos_percents);//how many samples are required
|
||||
int get_nsamples_for_fill();//how many samples are required to be added for a complete buffer refill (at start of the song or after seek)
|
||||
int get_skip_nsamples();//used for shorten
|
||||
|
||||
void set_rap(REALTYPE newrap);//set the current stretch value
|
||||
|
||||
void set_onset_detection_sensitivity(REALTYPE detection_sensitivity){
|
||||
onset_detection_sensitivity=detection_sensitivity;
|
||||
if (detection_sensitivity<1e-3) extra_onset_time_credit=0.0;
|
||||
};
|
||||
void here_is_onset(REALTYPE onset);
|
||||
virtual void setSampleRate(REALTYPE sr) { samplerate = jlimit(1000.0f, 38400.0f, sr); }
|
||||
REALTYPE getSampleRate() { return samplerate; }
|
||||
FFTWindow window_type;
|
||||
protected:
|
||||
int bufsize=0;
|
||||
|
||||
virtual void process_spectrum(REALTYPE *){};
|
||||
virtual REALTYPE get_stretch_multiplier(REALTYPE pos_percents);
|
||||
REALTYPE samplerate;
|
||||
|
||||
private:
|
||||
|
||||
void do_analyse_inbuf(REALTYPE *smps);
|
||||
void do_next_inbuf_smps(REALTYPE *smps);
|
||||
REALTYPE do_detect_onset();
|
||||
|
||||
// REALTYPE *in_pool;//de marimea in_bufsize
|
||||
REALTYPE rap,onset_detection_sensitivity;
|
||||
std::vector<REALTYPE> old_out_smps;
|
||||
std::vector<REALTYPE> old_freq;
|
||||
std::vector<REALTYPE> new_smps,old_smps,very_old_smps;
|
||||
|
||||
std::unique_ptr<FFT> infft,outfft;
|
||||
std::unique_ptr<FFT> fft;
|
||||
long double remained_samples;//0..1
|
||||
long double extra_onset_time_credit;
|
||||
REALTYPE c_pos_percents;
|
||||
int skip_samples;
|
||||
bool require_new_buffer;
|
||||
bool bypass,freezing;
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Stretch)
|
||||
};
|
||||
|
||||
|
||||
|
789
Source/PS_Source/StretchSource.cpp
Normal file
789
Source/PS_Source/StretchSource.cpp
Normal file
@ -0,0 +1,789 @@
|
||||
#include "StretchSource.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#include <ppl.h>
|
||||
//#define USE_PPL_TO_PROCESS_STRETCHERS
|
||||
#undef min
|
||||
#undef max
|
||||
#endif
|
||||
|
||||
extern std::unique_ptr<PropertiesFile> g_propsfile;
|
||||
|
||||
StretchAudioSource::StretchAudioSource(int initialnumoutchans, AudioFormatManager* afm) : m_afm(afm)
|
||||
{
|
||||
m_resampler = std::make_unique<WDL_Resampler>();
|
||||
m_resampler_outbuf.resize(1024*1024);
|
||||
m_inputfile = std::make_unique<AInputS>(m_afm);
|
||||
m_specproc_order = { 0,1,2,3,4,5,6,7 };
|
||||
String order = g_propsfile->getValue("spectral_order", "01234567");
|
||||
if (order.isNotEmpty())
|
||||
{
|
||||
std::vector<int> temp;
|
||||
for (int i = 0; i<order.length(); ++i)
|
||||
{
|
||||
int index = order[i] - 48;
|
||||
if (index >= 0 && index<8)
|
||||
{
|
||||
temp.push_back(index);
|
||||
//Logger::writeToLog(temp.back().m_name);
|
||||
}
|
||||
}
|
||||
m_specproc_order = temp;
|
||||
}
|
||||
setNumOutChannels(initialnumoutchans);
|
||||
}
|
||||
|
||||
StretchAudioSource::~StretchAudioSource()
|
||||
{
|
||||
String temp;
|
||||
for (auto& e : m_specproc_order)
|
||||
temp.append(String(e),1);
|
||||
g_propsfile->setValue("spectral_order", temp);
|
||||
}
|
||||
|
||||
void StretchAudioSource::prepareToPlay(int /*samplesPerBlockExpected*/, double sampleRate)
|
||||
{
|
||||
m_outsr = sampleRate;
|
||||
m_vol_smoother.reset(sampleRate, 0.5);
|
||||
m_lastplayrate = -1.0;
|
||||
m_stop_play_requested = false;
|
||||
m_output_counter = 0;
|
||||
m_output_silence_counter = 0;
|
||||
m_stream_end_reached = false;
|
||||
m_firstbuffer = true;
|
||||
m_output_has_begun = false;
|
||||
|
||||
initObjects();
|
||||
|
||||
}
|
||||
|
||||
void StretchAudioSource::releaseResources()
|
||||
{
|
||||
}
|
||||
|
||||
bool StretchAudioSource::isResampling()
|
||||
{
|
||||
if (m_inputfile==nullptr || m_inputfile->info.samplerate==0)
|
||||
return false;
|
||||
return (int)m_outsr!=m_inputfile->info.samplerate;
|
||||
}
|
||||
|
||||
std::vector<int> StretchAudioSource::getSpectrumProcessOrder()
|
||||
{
|
||||
return m_specproc_order;
|
||||
}
|
||||
|
||||
void StretchAudioSource::setSpectrumProcessOrder(std::vector<int> order)
|
||||
{
|
||||
std::lock_guard <std::mutex> locker(m_mutex);
|
||||
m_specproc_order = order;
|
||||
for (int i = 0; i < m_stretchers.size(); ++i)
|
||||
{
|
||||
m_stretchers[i]->m_spectrum_processes = order;
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<Range<double>, Range<double>> StretchAudioSource::getFileCachedRangesNormalized()
|
||||
{
|
||||
if (m_inputfile == nullptr)
|
||||
return {};
|
||||
return m_inputfile->getCachedRangesNormalized();
|
||||
}
|
||||
|
||||
ValueTree StretchAudioSource::getStateTree()
|
||||
{
|
||||
ValueTree tree("stretchsourcestate");
|
||||
storeToTreeProperties(tree, nullptr, "pitch_shift", m_ppar.pitch_shift.cents,
|
||||
"octaves_minus2", m_ppar.octave.om2,
|
||||
"octaves_minus1",m_ppar.octave.om1,
|
||||
"octave0",m_ppar.octave.o0,
|
||||
"octave_plus1",m_ppar.octave.o1,
|
||||
"octaves_plus15",m_ppar.octave.o15,
|
||||
"octaves_plus2",m_ppar.octave.o2);
|
||||
return tree;
|
||||
}
|
||||
|
||||
void StretchAudioSource::setStateTree(ValueTree state)
|
||||
{
|
||||
std::lock_guard <std::mutex> locker(m_mutex);
|
||||
getFromTreeProperties(state, "pitch_shift", m_ppar.pitch_shift.cents,
|
||||
"octaves_minus2", m_ppar.octave.om2,
|
||||
"octaves_minus1", m_ppar.octave.om1,
|
||||
"octave0", m_ppar.octave.o0,
|
||||
"octave_plus1", m_ppar.octave.o1,
|
||||
"octaves_plus15", m_ppar.octave.o15,
|
||||
"octaves_plus2", m_ppar.octave.o2);
|
||||
for (int i = 0; i < m_stretchers.size(); ++i)
|
||||
{
|
||||
m_stretchers[i]->set_parameters(&m_ppar);
|
||||
}
|
||||
}
|
||||
|
||||
bool StretchAudioSource::isLoopingEnabled()
|
||||
{
|
||||
if (m_inputfile == nullptr || m_inputfile->info.nsamples == 0)
|
||||
return false;
|
||||
return m_inputfile->isLooping();
|
||||
}
|
||||
|
||||
void StretchAudioSource::setLoopingEnabled(bool b)
|
||||
{
|
||||
std::lock_guard <std::mutex> locker(m_mutex);
|
||||
if (m_inputfile != nullptr)
|
||||
{
|
||||
m_inputfile->setLoopEnabled(b);
|
||||
}
|
||||
}
|
||||
|
||||
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...
|
||||
std::lock_guard <std::mutex> locker(m_mutex);
|
||||
if (m_stretchoutringbuf.available() > 0)
|
||||
m_output_has_begun = true;
|
||||
bool freezing = m_freezing;
|
||||
if (m_stretchers[0]->isFreezing() != freezing)
|
||||
{
|
||||
if (freezing == true && m_inputfile!=nullptr)
|
||||
m_freeze_pos = 1.0/m_inputfile->info.nsamples*m_inputfile->getCurrentPosition();
|
||||
for (auto& e : m_stretchers)
|
||||
e->set_freezing(m_freezing);
|
||||
}
|
||||
double maingain = Decibels::decibelsToGain((double)val_MainVolume.getValue());
|
||||
if (m_vol_smoother.getTargetValue() != maingain)
|
||||
m_vol_smoother.setValue(maingain);
|
||||
FloatVectorOperations::disableDenormalisedNumberSupport();
|
||||
float** outarrays = bufferToFill.buffer->getArrayOfWritePointers();
|
||||
int outbufchans = m_num_outchans; // bufferToFill.buffer->getNumChannels();
|
||||
int offset = bufferToFill.startSample;
|
||||
if (m_stretchers.size() == 0)
|
||||
return;
|
||||
if (m_inputfile == nullptr)
|
||||
return;
|
||||
if (m_inputfile->info.nsamples == 0)
|
||||
return;
|
||||
m_inputfile->setXFadeLenSeconds(val_XFadeLen.getValue());
|
||||
double* rsinbuf = nullptr;
|
||||
int wanted = m_resampler->ResamplePrepare(bufferToFill.numSamples, m_num_outchans, &rsinbuf);
|
||||
double silencethreshold = Decibels::decibelsToGain(-70.0);
|
||||
bool tempfirst = true;
|
||||
//if (m_output_counter<=m_process_fftsize*2) // && m_inputfile->hasEnded() == false)
|
||||
{
|
||||
while (m_stretchoutringbuf.available() < wanted*m_num_outchans)
|
||||
{
|
||||
int readsize = 0;
|
||||
double in_pos = (double)m_inputfile->getCurrentPosition() / (double)m_inputfile->info.nsamples;
|
||||
if (tempfirst == true)
|
||||
{
|
||||
m_lastinpos = in_pos;
|
||||
tempfirst = false;
|
||||
}
|
||||
if (m_firstbuffer)
|
||||
{
|
||||
readsize = m_stretchers[0]->get_nsamples_for_fill();
|
||||
m_firstbuffer = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
readsize = m_stretchers[0]->get_nsamples(in_pos*100.0);
|
||||
};
|
||||
int readed = 0;
|
||||
if (readsize != 0)
|
||||
{
|
||||
readed = m_inputfile->readNextBlock(m_file_inbuf, readsize, m_num_outchans);
|
||||
}
|
||||
auto inbufptrs = m_file_inbuf.getArrayOfReadPointers();
|
||||
for (int ch = 0; ch < m_num_outchans; ++ch)
|
||||
{
|
||||
int inchantouse = ch;
|
||||
for (int i = 0; i < readed; i++)
|
||||
{
|
||||
m_inbufs[ch][i] = inbufptrs[inchantouse][i];
|
||||
}
|
||||
}
|
||||
REALTYPE onset_max = std::numeric_limits<REALTYPE>::min();
|
||||
#ifdef USE_PPL_TO_PROCESS_STRETCHERS
|
||||
std::array<REALTYPE, 16> onset_values_arr;
|
||||
Concurrency::parallel_for(0, (int)m_stretchers.size(), [this, readed, &onset_values_arr](int i)
|
||||
{
|
||||
REALTYPE onset_val = m_stretchers[i]->process(m_inbufs[i].data(), readed);
|
||||
onset_values_arr[i] = onset_val;
|
||||
});
|
||||
for (int i = 0; i < m_stretchers.size(); ++i)
|
||||
onset_max = std::max(onset_max, onset_values_arr[i]);
|
||||
#else
|
||||
for (int i = 0; i < m_stretchers.size(); ++i)
|
||||
{
|
||||
REALTYPE onset_l = m_stretchers[i]->process(m_inbufs[i].data(), readed);
|
||||
onset_max = std::max(onset_max, onset_l);
|
||||
}
|
||||
#endif
|
||||
for (int i = 0; i < m_stretchers.size(); ++i)
|
||||
m_stretchers[i]->here_is_onset(onset_max);
|
||||
int outbufsize = m_stretchers[0]->get_bufsize();
|
||||
int nskip = m_stretchers[0]->get_skip_nsamples();
|
||||
if (nskip > 0)
|
||||
m_inputfile->skip(nskip);
|
||||
|
||||
for (int i = 0; i < outbufsize; i++)
|
||||
{
|
||||
for (int ch = 0; ch < m_num_outchans; ++ch)
|
||||
{
|
||||
REALTYPE outsa = m_stretchers[ch]->out_buf[i];
|
||||
m_stretchoutringbuf.push(outsa);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < wanted*m_num_outchans; ++i)
|
||||
{
|
||||
double sample = m_stretchoutringbuf.get();
|
||||
rsinbuf[i] = sample;
|
||||
}
|
||||
if (wanted*m_num_outchans > m_resampler_outbuf.size())
|
||||
{
|
||||
m_resampler_outbuf.resize(wanted*m_num_outchans);
|
||||
}
|
||||
/*int produced =*/ m_resampler->ResampleOut(m_resampler_outbuf.data(), wanted, bufferToFill.numSamples, m_num_outchans);
|
||||
|
||||
bool source_ended = m_inputfile->hasEnded();
|
||||
double samplelimit = 16384.0;
|
||||
if (m_clip_output == true)
|
||||
samplelimit = 1.0;
|
||||
for (int i = 0; i < bufferToFill.numSamples; ++i)
|
||||
{
|
||||
double smoothed_gain = m_vol_smoother.getNextValue();
|
||||
double mixed = 0.0;
|
||||
for (int j = 0; j < outbufchans; ++j)
|
||||
{
|
||||
double outsample = m_resampler_outbuf[i*m_num_outchans + j];
|
||||
outarrays[j][i + offset] = jlimit(-samplelimit,samplelimit , outsample * smoothed_gain);
|
||||
mixed += outsample;
|
||||
}
|
||||
|
||||
if (source_ended && m_output_counter>=2*m_process_fftsize)
|
||||
{
|
||||
if (fabs(mixed) < silencethreshold)
|
||||
++m_output_silence_counter;
|
||||
else
|
||||
m_output_silence_counter = 0;
|
||||
}
|
||||
|
||||
}
|
||||
//if (m_inputfile->hasEnded())
|
||||
m_output_counter += bufferToFill.numSamples;
|
||||
}
|
||||
|
||||
void StretchAudioSource::setNextReadPosition(int64 /*newPosition*/)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int64 StretchAudioSource::getNextReadPosition() const
|
||||
{
|
||||
return int64();
|
||||
}
|
||||
|
||||
int64 StretchAudioSource::getTotalLength() const
|
||||
{
|
||||
if (m_inputfile == nullptr)
|
||||
return 0;
|
||||
return m_inputfile->info.nsamples;
|
||||
}
|
||||
|
||||
bool StretchAudioSource::isLooping() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
String StretchAudioSource::setAudioFile(File file)
|
||||
{
|
||||
std::lock_guard <std::mutex> locker(m_mutex);
|
||||
if (m_inputfile->openAudioFile(file))
|
||||
{
|
||||
m_curfile = file;
|
||||
return String();
|
||||
}
|
||||
return "Could not open file";
|
||||
}
|
||||
|
||||
File StretchAudioSource::getAudioFile()
|
||||
{
|
||||
return m_curfile;
|
||||
}
|
||||
|
||||
void StretchAudioSource::setNumOutChannels(int chans)
|
||||
{
|
||||
jassert(chans > 0 && chans < g_maxnumoutchans);
|
||||
m_num_outchans = chans;
|
||||
}
|
||||
|
||||
void StretchAudioSource::initObjects()
|
||||
{
|
||||
std::lock_guard<std::mutex> locker(m_mutex);
|
||||
m_inputfile->setActiveRange(m_playrange);
|
||||
m_inputfile->seek(m_seekpos);
|
||||
|
||||
m_firstbuffer = true;
|
||||
if (m_stretchoutringbuf.getSize() < m_num_outchans*m_process_fftsize)
|
||||
{
|
||||
int newsize = m_num_outchans*m_process_fftsize*2;
|
||||
//Logger::writeToLog("Resizing circular buffer to " + String(newsize));
|
||||
m_stretchoutringbuf.resize(newsize);
|
||||
}
|
||||
m_stretchoutringbuf.clear();
|
||||
m_resampler->Reset();
|
||||
m_resampler->SetRates(m_inputfile->info.samplerate, m_outsr);
|
||||
REALTYPE stretchratio = m_playrate;
|
||||
FFTWindow windowtype = W_HAMMING;
|
||||
if (m_fft_window_type>=0)
|
||||
windowtype = (FFTWindow)m_fft_window_type;
|
||||
int inbufsize = m_process_fftsize;
|
||||
double onsetsens = m_onsetdetection;
|
||||
m_stretchers.resize(m_num_outchans);
|
||||
for (int i = 0; i < m_stretchers.size(); ++i)
|
||||
{
|
||||
if (m_stretchers[i] == nullptr)
|
||||
{
|
||||
//Logger::writeToLog("Creating stretch instance " + String(i));
|
||||
m_stretchers[i] = std::make_shared<ProcessedStretch>(stretchratio,
|
||||
m_process_fftsize, windowtype, false, (float)m_inputfile->info.samplerate, i + 1);
|
||||
}
|
||||
m_stretchers[i]->setBufferSize(m_process_fftsize);
|
||||
m_stretchers[i]->setSampleRate(m_inputfile->info.samplerate);
|
||||
m_stretchers[i]->set_onset_detection_sensitivity(onsetsens);
|
||||
m_stretchers[i]->set_parameters(&m_ppar);
|
||||
m_stretchers[i]->set_freezing(m_freezing);
|
||||
m_stretchers[i]->m_spectrum_processes = m_specproc_order;
|
||||
}
|
||||
m_inbufs.resize(m_num_outchans);
|
||||
m_file_inbuf.setSize(m_num_outchans, 3 * inbufsize);
|
||||
int poolsize = m_stretchers[0]->get_max_bufsize();
|
||||
for (int i = 0; i<m_num_outchans; ++i)
|
||||
m_inbufs[i].resize(poolsize);
|
||||
|
||||
}
|
||||
|
||||
double StretchAudioSource::getInfilePositionPercent()
|
||||
{
|
||||
if (m_inputfile == nullptr || m_inputfile->info.nsamples == 0)
|
||||
return 0.0;
|
||||
return 1.0/m_inputfile->info.nsamples*m_inputfile->getCurrentPosition();
|
||||
}
|
||||
|
||||
double StretchAudioSource::getInfilePositionSeconds()
|
||||
{
|
||||
if (m_inputfile == nullptr || m_inputfile->info.nsamples == 0)
|
||||
return 0.0;
|
||||
//return m_lastinpos*m_inputfile->getLengthSeconds();
|
||||
return (double)m_inputfile->getCurrentPosition() / m_inputfile->info.samplerate;
|
||||
}
|
||||
|
||||
double StretchAudioSource::getInfileLengthSeconds()
|
||||
{
|
||||
if (m_inputfile == nullptr || m_inputfile->info.nsamples == 0)
|
||||
return 0.0;
|
||||
return (double)m_inputfile->info.nsamples / m_inputfile->info.samplerate;
|
||||
}
|
||||
|
||||
void StretchAudioSource::setRate(double rate)
|
||||
{
|
||||
std::lock_guard<std::mutex> locker(m_mutex);
|
||||
//if (rate != m_lastplayrate)
|
||||
{
|
||||
//m_output_counter = m_output_counter*rate;
|
||||
//m_output_length = (2*m_process_fftsize)+m_inputfile->getActiveRangeFrames().getLength()*rate;
|
||||
//Logger::writeToLog("new len " + String(m_output_length) + " new output pos " + String(m_output_counter));
|
||||
m_playrate = rate;
|
||||
for (int i = 0; i < m_stretchers.size(); ++i)
|
||||
{
|
||||
m_stretchers[i]->set_rap((float)rate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StretchAudioSource::setProcessParameters(ProcessParameters * pars)
|
||||
{
|
||||
std::lock_guard<std::mutex> locker(m_mutex);
|
||||
m_ppar = *pars;
|
||||
for (int i = 0; i < m_stretchers.size(); ++i)
|
||||
{
|
||||
m_stretchers[i]->set_parameters(pars);
|
||||
}
|
||||
}
|
||||
|
||||
ProcessParameters StretchAudioSource::getProcessParameters()
|
||||
{
|
||||
return m_ppar;
|
||||
}
|
||||
|
||||
void StretchAudioSource::setFFTWindowingType(int windowtype)
|
||||
{
|
||||
if (windowtype==m_fft_window_type)
|
||||
return;
|
||||
std::lock_guard<std::mutex> locker(m_mutex);
|
||||
m_fft_window_type = windowtype;
|
||||
for (int i = 0; i < m_stretchers.size(); ++i)
|
||||
{
|
||||
m_stretchers[i]->window_type = (FFTWindow)windowtype;
|
||||
}
|
||||
}
|
||||
|
||||
void StretchAudioSource::setFFTSize(int size)
|
||||
{
|
||||
jassert(size>0);
|
||||
if (size != m_process_fftsize)
|
||||
{
|
||||
m_process_fftsize = size;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void StretchAudioSource::seekPercent(double pos)
|
||||
{
|
||||
std::lock_guard<std::mutex> locker(m_mutex);
|
||||
m_seekpos = pos;
|
||||
m_inputfile->seek(pos);
|
||||
m_lastinpos = pos;
|
||||
}
|
||||
|
||||
double StretchAudioSource::getOutputDurationSecondsForRange(Range<double> range, int fftsize)
|
||||
{
|
||||
if (m_inputfile == nullptr || m_inputfile->info.nsamples == 0)
|
||||
return 0.0;
|
||||
int64_t play_end_pos = (fftsize * 2)+range.getLength()*m_playrate*m_inputfile->info.nsamples;
|
||||
return (double)play_end_pos / m_inputfile->info.samplerate;
|
||||
}
|
||||
|
||||
void StretchAudioSource::setOnsetDetection(double x)
|
||||
{
|
||||
std::lock_guard<std::mutex> locker(m_mutex);
|
||||
m_onsetdetection = x;
|
||||
for (int i = 0; i < m_stretchers.size(); ++i)
|
||||
{
|
||||
m_stretchers[i]->set_onset_detection_sensitivity((float)x);
|
||||
}
|
||||
}
|
||||
|
||||
void StretchAudioSource::setPlayRange(Range<double> playrange, bool isloop)
|
||||
{
|
||||
std::lock_guard<std::mutex> locker(m_mutex);
|
||||
if (playrange.isEmpty())
|
||||
m_playrange = { 0.0,1.0 };
|
||||
else
|
||||
m_playrange = playrange;
|
||||
m_stretchoutringbuf.clear();
|
||||
m_stream_end_reached = false;
|
||||
m_inputfile->setActiveRange(m_playrange);
|
||||
m_inputfile->setLoopEnabled(isloop);
|
||||
m_inputfile->seek(m_playrange.getStart());
|
||||
m_seekpos = m_playrange.getStart();
|
||||
}
|
||||
|
||||
bool StretchAudioSource::isLoopEnabled()
|
||||
{
|
||||
if (m_inputfile == nullptr)
|
||||
return false;
|
||||
return m_inputfile->isLooping();
|
||||
}
|
||||
|
||||
bool StretchAudioSource::hasReachedEnd()
|
||||
{
|
||||
if (m_inputfile == nullptr)
|
||||
return false;
|
||||
if (m_inputfile->isLooping() && m_maxloops == 0)
|
||||
return false;
|
||||
if (m_inputfile->isLooping() && m_inputfile->getLoopCount() > m_maxloops)
|
||||
return true;
|
||||
//return m_output_counter>=m_process_fftsize*2;
|
||||
return m_output_silence_counter>=65536;
|
||||
}
|
||||
|
||||
std::pair<Range<double>, Range<double>> MultiStretchAudioSource::getFileCachedRangesNormalized()
|
||||
{
|
||||
return getActiveStretchSource()->getFileCachedRangesNormalized();
|
||||
}
|
||||
|
||||
StretchAudioSource * MultiStretchAudioSource::getActiveStretchSource() const
|
||||
{
|
||||
return m_stretchsources[0].get();
|
||||
}
|
||||
|
||||
void MultiStretchAudioSource::switchActiveSource()
|
||||
{
|
||||
std::swap(m_stretchsources[0], m_stretchsources[1]);
|
||||
m_is_in_switch = true;
|
||||
m_xfadegain.reset(m_samplerate, 2.0);
|
||||
m_xfadegain.setValue(1.0);
|
||||
}
|
||||
|
||||
MultiStretchAudioSource::MultiStretchAudioSource(int initialnumoutchans, AudioFormatManager* afm)
|
||||
: m_afm(afm)
|
||||
{
|
||||
m_stretchsources.resize(2);
|
||||
m_stretchsources[0] = std::make_shared<StretchAudioSource>(initialnumoutchans,m_afm);
|
||||
m_stretchsources[1] = std::make_shared<StretchAudioSource>(initialnumoutchans,m_afm);
|
||||
m_numoutchans = initialnumoutchans;
|
||||
m_processbuffers[0].setSize(m_numoutchans, 4096);
|
||||
m_processbuffers[1].setSize(m_numoutchans, 4096);
|
||||
}
|
||||
|
||||
MultiStretchAudioSource::~MultiStretchAudioSource()
|
||||
{
|
||||
String temp;
|
||||
for (auto& e : getActiveStretchSource()->getSpectrumProcessOrder())
|
||||
temp.append(String(e), 1);
|
||||
g_propsfile->setValue("spectral_order", temp);
|
||||
}
|
||||
|
||||
void MultiStretchAudioSource::prepareToPlay(int samplesPerBlockExpected, double sampleRate)
|
||||
{
|
||||
m_is_in_switch = false;
|
||||
m_is_playing = true;
|
||||
m_blocksize = samplesPerBlockExpected;
|
||||
m_samplerate = sampleRate;
|
||||
if (m_processbuffers[0].getNumSamples() < samplesPerBlockExpected)
|
||||
{
|
||||
m_processbuffers[0].setSize(m_numoutchans, samplesPerBlockExpected);
|
||||
m_processbuffers[1].setSize(m_numoutchans, samplesPerBlockExpected);
|
||||
}
|
||||
getActiveStretchSource()->prepareToPlay(samplesPerBlockExpected, sampleRate);
|
||||
|
||||
}
|
||||
|
||||
void MultiStretchAudioSource::releaseResources()
|
||||
{
|
||||
m_is_playing = false;
|
||||
getActiveStretchSource()->releaseResources();
|
||||
}
|
||||
|
||||
void MultiStretchAudioSource::getNextAudioBlock(const AudioSourceChannelInfo & bufferToFill)
|
||||
{
|
||||
std::lock_guard<std::mutex> locker(m_mutex);
|
||||
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()->setFreezing(m_freezing);
|
||||
getActiveStretchSource()->getNextAudioBlock(bufferToFill);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
//if (bufferToFill.numSamples > m_processbuffers[0].getNumSamples())
|
||||
{
|
||||
m_processbuffers[0].setSize(m_numoutchans, bufferToFill.numSamples);
|
||||
m_processbuffers[1].setSize(m_numoutchans, bufferToFill.numSamples);
|
||||
}
|
||||
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]->setFreezing(m_freezing);
|
||||
m_stretchsources[1]->setFreezing(m_freezing);
|
||||
m_stretchsources[1]->setFFTWindowingType(m_stretchsources[0]->getFFTWindowingType());
|
||||
m_stretchsources[0]->getNextAudioBlock(ascinfo1);
|
||||
m_stretchsources[1]->getNextAudioBlock(ascinfo2);
|
||||
int offset = bufferToFill.startSample;
|
||||
float** outbufpts = bufferToFill.buffer->getArrayOfWritePointers();
|
||||
for (int i = 0; i < bufferToFill.numSamples; ++i)
|
||||
{
|
||||
double fadegain = m_xfadegain.getNextValue();
|
||||
for (int j = 0; j < m_numoutchans; ++j)
|
||||
{
|
||||
double procsample0 = (1.0-fadegain)*m_processbuffers[0].getSample(j, i);
|
||||
double procsample1 = (fadegain)*m_processbuffers[1].getSample(j, i);
|
||||
outbufpts[j][i + offset] = procsample0 + procsample1;
|
||||
}
|
||||
}
|
||||
if (m_xfadegain.isSmoothing() == false)
|
||||
{
|
||||
std::swap(m_stretchsources[0], m_stretchsources[1]);
|
||||
m_xfadegain.setValue(0.0);
|
||||
m_xfadegain.reset(m_samplerate, m_switchxfadelen);
|
||||
m_is_in_switch = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MultiStretchAudioSource::setNextReadPosition(int64 newPosition)
|
||||
{
|
||||
getActiveStretchSource()->setNextReadPosition(newPosition);
|
||||
}
|
||||
|
||||
int64 MultiStretchAudioSource::getNextReadPosition() const
|
||||
{
|
||||
return getActiveStretchSource()->getNextReadPosition();
|
||||
}
|
||||
|
||||
int64 MultiStretchAudioSource::getTotalLength() const
|
||||
{
|
||||
return getActiveStretchSource()->getTotalLength();
|
||||
}
|
||||
|
||||
bool MultiStretchAudioSource::isLooping() const
|
||||
{
|
||||
return getActiveStretchSource()->isLooping();
|
||||
}
|
||||
|
||||
String MultiStretchAudioSource::setAudioFile(File file)
|
||||
{
|
||||
if (m_is_playing == false)
|
||||
{
|
||||
return m_stretchsources[0]->setAudioFile(file);
|
||||
}
|
||||
else
|
||||
{
|
||||
String result = m_stretchsources[1]->setAudioFile(file);
|
||||
m_stretchsources[1]->setFFTSize(m_stretchsources[0]->getFFTSize());
|
||||
m_stretchsources[1]->setNumOutChannels(m_stretchsources[0]->getNumOutChannels());
|
||||
m_stretchsources[1]->setRate(m_stretchsources[0]->getRate());
|
||||
m_stretchsources[1]->setPlayRange({ 0.0,1.0 }, m_stretchsources[0]->isLoopEnabled());
|
||||
auto pars = m_stretchsources[0]->getProcessParameters();
|
||||
m_stretchsources[1]->setProcessParameters(&pars);
|
||||
m_stretchsources[1]->setSpectrumProcessOrder(m_stretchsources[0]->getSpectrumProcessOrder());
|
||||
m_stretchsources[1]->prepareToPlay(m_blocksize, m_samplerate);
|
||||
|
||||
m_mutex.lock();
|
||||
m_xfadegain.reset(m_samplerate, m_switchxfadelen);
|
||||
m_xfadegain.setValue(1.0);
|
||||
m_is_in_switch = true;
|
||||
m_mutex.unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
File MultiStretchAudioSource::getAudioFile()
|
||||
{
|
||||
return getActiveStretchSource()->getAudioFile();
|
||||
}
|
||||
|
||||
void MultiStretchAudioSource::setNumOutChannels(int chans)
|
||||
{
|
||||
m_numoutchans = chans;
|
||||
getActiveStretchSource()->setNumOutChannels(chans);
|
||||
}
|
||||
|
||||
double MultiStretchAudioSource::getInfilePositionPercent()
|
||||
{
|
||||
return getActiveStretchSource()->getInfilePositionPercent();
|
||||
}
|
||||
|
||||
void MultiStretchAudioSource::setRate(double rate)
|
||||
{
|
||||
getActiveStretchSource()->setRate(rate);
|
||||
}
|
||||
|
||||
double MultiStretchAudioSource::getRate()
|
||||
{
|
||||
return getActiveStretchSource()->getRate();
|
||||
}
|
||||
|
||||
void MultiStretchAudioSource::setProcessParameters(ProcessParameters * pars)
|
||||
{
|
||||
getActiveStretchSource()->setProcessParameters(pars);
|
||||
}
|
||||
|
||||
void MultiStretchAudioSource::setFFTWindowingType(int windowtype)
|
||||
{
|
||||
getActiveStretchSource()->setFFTWindowingType(windowtype);
|
||||
}
|
||||
|
||||
void MultiStretchAudioSource::setFFTSize(int size)
|
||||
{
|
||||
if (m_is_playing == false)
|
||||
{
|
||||
getActiveStretchSource()->setFFTSize(size);
|
||||
}
|
||||
else
|
||||
{
|
||||
double curpos = m_stretchsources[0]->getInfilePositionPercent();
|
||||
m_stretchsources[1]->setFFTSize(size);
|
||||
m_stretchsources[1]->setNumOutChannels(m_stretchsources[0]->getNumOutChannels());
|
||||
m_stretchsources[1]->setAudioFile(m_stretchsources[0]->getAudioFile());
|
||||
m_stretchsources[1]->setRate(m_stretchsources[0]->getRate());
|
||||
m_stretchsources[1]->setPlayRange(m_stretchsources[0]->getPlayRange(), m_stretchsources[0]->isLoopEnabled());
|
||||
m_stretchsources[1]->seekPercent(curpos);
|
||||
auto pars = m_stretchsources[0]->getProcessParameters();
|
||||
m_stretchsources[1]->setProcessParameters(&pars);
|
||||
m_stretchsources[1]->setSpectrumProcessOrder(m_stretchsources[0]->getSpectrumProcessOrder());
|
||||
m_stretchsources[1]->prepareToPlay(m_blocksize, m_samplerate);
|
||||
m_mutex.lock();
|
||||
m_xfadegain.reset(m_samplerate, m_switchxfadelen);
|
||||
m_xfadegain.setValue(1.0);
|
||||
m_is_in_switch = true;
|
||||
m_mutex.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
int MultiStretchAudioSource::getFFTSize()
|
||||
{
|
||||
return getActiveStretchSource()->getFFTSize();
|
||||
}
|
||||
|
||||
|
||||
void MultiStretchAudioSource::seekPercent(double pos)
|
||||
{
|
||||
getActiveStretchSource()->seekPercent(pos);
|
||||
}
|
||||
|
||||
double MultiStretchAudioSource::getInfilePositionSeconds()
|
||||
{
|
||||
return getActiveStretchSource()->getInfilePositionSeconds();
|
||||
}
|
||||
|
||||
double MultiStretchAudioSource::getInfileLengthSeconds()
|
||||
{
|
||||
return getActiveStretchSource()->getInfileLengthSeconds();
|
||||
}
|
||||
|
||||
double MultiStretchAudioSource::getOutputDurationSecondsForRange(Range<double> range, int fftsize)
|
||||
{
|
||||
return getActiveStretchSource()->getOutputDurationSecondsForRange(range, fftsize);
|
||||
}
|
||||
|
||||
void MultiStretchAudioSource::setOnsetDetection(double x)
|
||||
{
|
||||
getActiveStretchSource()->setOnsetDetection(x);
|
||||
}
|
||||
|
||||
void MultiStretchAudioSource::setPlayRange(Range<double> playrange, bool isloop)
|
||||
{
|
||||
getActiveStretchSource()->setPlayRange(playrange, isloop);
|
||||
}
|
||||
|
||||
bool MultiStretchAudioSource::isLoopingEnabled()
|
||||
{
|
||||
return getActiveStretchSource()->isLoopingEnabled();
|
||||
}
|
||||
|
||||
void MultiStretchAudioSource::setLoopingEnabled(bool b)
|
||||
{
|
||||
getActiveStretchSource()->setLoopingEnabled(b);
|
||||
}
|
||||
|
||||
bool MultiStretchAudioSource::hasReachedEnd()
|
||||
{
|
||||
return getActiveStretchSource()->hasReachedEnd();
|
||||
}
|
||||
|
||||
bool MultiStretchAudioSource::isResampling()
|
||||
{
|
||||
return getActiveStretchSource()->isResampling();
|
||||
}
|
||||
|
||||
std::vector<int> MultiStretchAudioSource::getSpectrumProcessOrder()
|
||||
{
|
||||
return getActiveStretchSource()->getSpectrumProcessOrder();
|
||||
}
|
||||
|
||||
void MultiStretchAudioSource::setSpectrumProcessOrder(std::vector<int> order)
|
||||
{
|
||||
getActiveStretchSource()->setSpectrumProcessOrder(order);
|
||||
}
|
205
Source/PS_Source/StretchSource.h
Normal file
205
Source/PS_Source/StretchSource.h
Normal file
@ -0,0 +1,205 @@
|
||||
/*
|
||||
Copyright (C) 2006-2011 Nasca Octavian Paul
|
||||
Author: Nasca Octavian Paul
|
||||
|
||||
Copyright (C) 2017 Xenakios
|
||||
|
||||
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 "../JuceLibraryCode/JuceHeader.h"
|
||||
#include "Input/AInputS.h"
|
||||
#include "ProcessedStretch.h"
|
||||
#include "BinauralBeats.h"
|
||||
#include <mutex>
|
||||
#include "../WDL/resample.h"
|
||||
|
||||
class StretchAudioSource final : public PositionableAudioSource
|
||||
{
|
||||
public:
|
||||
StretchAudioSource() {}
|
||||
StretchAudioSource(int initialnumoutchans, AudioFormatManager* afm);
|
||||
~StretchAudioSource();
|
||||
// Inherited via PositionableAudioSource
|
||||
void prepareToPlay(int samplesPerBlockExpected, double sampleRate) override;
|
||||
|
||||
void releaseResources() override;
|
||||
|
||||
void getNextAudioBlock(const AudioSourceChannelInfo & bufferToFill) override;
|
||||
|
||||
void setNextReadPosition(int64 newPosition) override;
|
||||
|
||||
int64 getNextReadPosition() const override;
|
||||
|
||||
int64 getTotalLength() const override;
|
||||
|
||||
bool isLooping() const override;
|
||||
|
||||
String setAudioFile(File file);
|
||||
File getAudioFile();
|
||||
|
||||
void setNumOutChannels(int chans);
|
||||
int getNumOutChannels() { return m_num_outchans; }
|
||||
double getInfilePositionPercent();
|
||||
double getInfilePositionSeconds();
|
||||
double getInfileLengthSeconds();
|
||||
void setRate(double rate);
|
||||
double getRate() { return m_playrate; }
|
||||
void setProcessParameters(ProcessParameters* pars);
|
||||
ProcessParameters getProcessParameters();
|
||||
void setFFTSize(int size);
|
||||
int getFFTSize() { return m_process_fftsize; }
|
||||
|
||||
double getFreezePos() const { return m_freeze_pos; }
|
||||
void setFreezing(bool b) { m_freezing = b; }
|
||||
bool isFreezing() { return m_freezing; }
|
||||
|
||||
void seekPercent(double pos);
|
||||
|
||||
double getOutputDurationSecondsForRange(Range<double> range, int fftsize);
|
||||
|
||||
void setOnsetDetection(double x);
|
||||
void setPlayRange(Range<double> playrange, bool isloop);
|
||||
Range<double> getPlayRange() { return m_playrange; }
|
||||
bool isLoopEnabled();
|
||||
bool hasReachedEnd();
|
||||
bool isResampling();
|
||||
std::vector<int> getSpectrumProcessOrder();
|
||||
void setSpectrumProcessOrder(std::vector<int> order);
|
||||
void setFFTWindowingType(int windowtype);
|
||||
int getFFTWindowingType() { return m_fft_window_type; }
|
||||
std::pair<Range<double>,Range<double>> getFileCachedRangesNormalized();
|
||||
Value val_MainVolume;
|
||||
Value val_XFadeLen;
|
||||
ValueTree getStateTree();
|
||||
void setStateTree(ValueTree state);
|
||||
void setClippingEnabled(bool b) { m_clip_output = b; }
|
||||
bool isLoopingEnabled();
|
||||
void setLoopingEnabled(bool b);
|
||||
void setMaxLoops(int64_t numloops) { m_maxloops = numloops; }
|
||||
private:
|
||||
CircularBuffer<float> m_stretchoutringbuf{ 1024 * 1024 };
|
||||
AudioBuffer<float> m_file_inbuf;
|
||||
LinearSmoothedValue<double> m_vol_smoother;
|
||||
std::unique_ptr<AInputS> m_inputfile;
|
||||
std::vector<std::shared_ptr<ProcessedStretch>> m_stretchers;
|
||||
std::unique_ptr<BinauralBeats> m_binaubeats;
|
||||
float2dvector m_inbufs;
|
||||
std::function<void(StretchAudioSource*)> SourceEndedCallback;
|
||||
bool m_firstbuffer = false;
|
||||
bool m_output_has_begun = false;
|
||||
int m_num_outchans = 0;
|
||||
double m_outsr = 44100.0;
|
||||
int m_process_fftsize = 0;
|
||||
int m_fft_window_type = -1;
|
||||
ProcessParameters m_ppar;
|
||||
BinauralBeatsParameters m_bbpar;
|
||||
double m_playrate = 1.0;
|
||||
double m_lastplayrate = 0.0;
|
||||
double m_onsetdetection = 0.0;
|
||||
double m_seekpos = 0.0;
|
||||
double m_lastinpos = 0.0;
|
||||
bool m_freezing = false;
|
||||
Range<double> m_playrange{ 0.0,1.0 };
|
||||
|
||||
bool m_stream_end_reached = false;
|
||||
int64_t m_output_silence_counter = 0;
|
||||
File m_curfile;
|
||||
int64_t m_maxloops = 0;
|
||||
std::unique_ptr<WDL_Resampler> m_resampler;
|
||||
std::vector<double> m_resampler_outbuf;
|
||||
std::mutex m_mutex;
|
||||
std::vector<int> m_specproc_order;
|
||||
bool m_stop_play_requested = false;
|
||||
double m_freeze_pos = 0.0;
|
||||
int64_t m_output_counter = 0;
|
||||
int64_t m_output_length = 0;
|
||||
bool m_clip_output = true;
|
||||
void initObjects();
|
||||
AudioFormatManager* m_afm = nullptr;
|
||||
};
|
||||
|
||||
class MultiStretchAudioSource final : public PositionableAudioSource
|
||||
{
|
||||
public:
|
||||
MultiStretchAudioSource() {}
|
||||
MultiStretchAudioSource(int initialnumoutchans, AudioFormatManager* afm);
|
||||
~MultiStretchAudioSource();
|
||||
|
||||
void prepareToPlay(int samplesPerBlockExpected, double sampleRate) override;
|
||||
|
||||
void releaseResources() override;
|
||||
|
||||
void getNextAudioBlock(const AudioSourceChannelInfo & bufferToFill) override;
|
||||
|
||||
void setNextReadPosition(int64 newPosition) override;
|
||||
|
||||
int64 getNextReadPosition() const override;
|
||||
|
||||
int64 getTotalLength() const override;
|
||||
|
||||
bool isLooping() const override;
|
||||
|
||||
String setAudioFile(File file);
|
||||
File getAudioFile();
|
||||
|
||||
void setNumOutChannels(int chans);
|
||||
double getInfilePositionPercent();
|
||||
void setRate(double rate);
|
||||
double getRate();
|
||||
void setProcessParameters(ProcessParameters* pars);
|
||||
void setFFTSize(int size);
|
||||
int getFFTSize();
|
||||
|
||||
void seekPercent(double pos);
|
||||
double getInfilePositionSeconds();
|
||||
double getInfileLengthSeconds();
|
||||
|
||||
double getOutputDurationSecondsForRange(Range<double> range, int fftsize);
|
||||
|
||||
void setOnsetDetection(double x);
|
||||
void setPlayRange(Range<double> playrange, bool isloop);
|
||||
bool isLoopingEnabled();
|
||||
void setLoopingEnabled(bool b);
|
||||
bool hasReachedEnd();
|
||||
bool isResampling();
|
||||
std::vector<int> getSpectrumProcessOrder();
|
||||
void setSpectrumProcessOrder(std::vector<int> order);
|
||||
void setFFTWindowingType(int windowtype);
|
||||
std::pair<Range<double>, Range<double>> getFileCachedRangesNormalized();
|
||||
void setFreezing(bool b) { m_freezing = b; }
|
||||
bool isFreezing() { return m_freezing; }
|
||||
|
||||
Value val_MainVolume;
|
||||
Value val_XFadeLen;
|
||||
//ValueTree getStateTree();
|
||||
//void setStateTree(ValueTree state);
|
||||
private:
|
||||
std::vector<std::shared_ptr<StretchAudioSource>> m_stretchsources;
|
||||
bool m_is_in_switch{ false };
|
||||
bool m_is_playing = false;
|
||||
bool m_freezing = false;
|
||||
LinearSmoothedValue<double> m_xfadegain;
|
||||
StretchAudioSource* getActiveStretchSource() const;
|
||||
void switchActiveSource();
|
||||
int m_blocksize = 0;
|
||||
double m_samplerate = 44100.0;
|
||||
int m_numoutchans = 2;
|
||||
AudioBuffer<float> m_processbuffers[2];
|
||||
std::mutex m_mutex;
|
||||
double m_switchxfadelen = 1.0;
|
||||
AudioFormatManager* m_afm = nullptr;
|
||||
};
|
164
Source/PS_Source/contrib/_kiss_fft_guts.h
Normal file
164
Source/PS_Source/contrib/_kiss_fft_guts.h
Normal file
@ -0,0 +1,164 @@
|
||||
/*
|
||||
Copyright (c) 2003-2010, Mark Borgerding
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
* Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* kiss_fft.h
|
||||
defines kiss_fft_scalar as either short or a float type
|
||||
and defines
|
||||
typedef struct { kiss_fft_scalar r; kiss_fft_scalar i; }kiss_fft_cpx; */
|
||||
#include "kiss_fft.h"
|
||||
#include <limits.h>
|
||||
|
||||
#define MAXFACTORS 32
|
||||
/* e.g. an fft of length 128 has 4 factors
|
||||
as far as kissfft is concerned
|
||||
4*4*4*2
|
||||
*/
|
||||
|
||||
struct kiss_fft_state{
|
||||
int nfft;
|
||||
int inverse;
|
||||
int factors[2*MAXFACTORS];
|
||||
kiss_fft_cpx twiddles[1];
|
||||
};
|
||||
|
||||
/*
|
||||
Explanation of macros dealing with complex math:
|
||||
|
||||
C_MUL(m,a,b) : m = a*b
|
||||
C_FIXDIV( c , div ) : if a fixed point impl., c /= div. noop otherwise
|
||||
C_SUB( res, a,b) : res = a - b
|
||||
C_SUBFROM( res , a) : res -= a
|
||||
C_ADDTO( res , a) : res += a
|
||||
* */
|
||||
#ifdef FIXED_POINT
|
||||
#if (FIXED_POINT==32)
|
||||
# define FRACBITS 31
|
||||
# define SAMPPROD int64_t
|
||||
#define SAMP_MAX 2147483647
|
||||
#else
|
||||
# define FRACBITS 15
|
||||
# define SAMPPROD int32_t
|
||||
#define SAMP_MAX 32767
|
||||
#endif
|
||||
|
||||
#define SAMP_MIN -SAMP_MAX
|
||||
|
||||
#if defined(CHECK_OVERFLOW)
|
||||
# define CHECK_OVERFLOW_OP(a,op,b) \
|
||||
if ( (SAMPPROD)(a) op (SAMPPROD)(b) > SAMP_MAX || (SAMPPROD)(a) op (SAMPPROD)(b) < SAMP_MIN ) { \
|
||||
fprintf(stderr,"WARNING:overflow @ " __FILE__ "(%d): (%d " #op" %d) = %ld\n",__LINE__,(a),(b),(SAMPPROD)(a) op (SAMPPROD)(b) ); }
|
||||
#endif
|
||||
|
||||
|
||||
# define smul(a,b) ( (SAMPPROD)(a)*(b) )
|
||||
# define sround( x ) (kiss_fft_scalar)( ( (x) + (1<<(FRACBITS-1)) ) >> FRACBITS )
|
||||
|
||||
# define S_MUL(a,b) sround( smul(a,b) )
|
||||
|
||||
# define C_MUL(m,a,b) \
|
||||
do{ (m).r = sround( smul((a).r,(b).r) - smul((a).i,(b).i) ); \
|
||||
(m).i = sround( smul((a).r,(b).i) + smul((a).i,(b).r) ); }while(0)
|
||||
|
||||
# define DIVSCALAR(x,k) \
|
||||
(x) = sround( smul( x, SAMP_MAX/k ) )
|
||||
|
||||
# define C_FIXDIV(c,div) \
|
||||
do { DIVSCALAR( (c).r , div); \
|
||||
DIVSCALAR( (c).i , div); }while (0)
|
||||
|
||||
# define C_MULBYSCALAR( c, s ) \
|
||||
do{ (c).r = sround( smul( (c).r , s ) ) ;\
|
||||
(c).i = sround( smul( (c).i , s ) ) ; }while(0)
|
||||
|
||||
#else /* not FIXED_POINT*/
|
||||
|
||||
# define S_MUL(a,b) ( (a)*(b) )
|
||||
#define C_MUL(m,a,b) \
|
||||
do{ (m).r = (a).r*(b).r - (a).i*(b).i;\
|
||||
(m).i = (a).r*(b).i + (a).i*(b).r; }while(0)
|
||||
# define C_FIXDIV(c,div) /* NOOP */
|
||||
# define C_MULBYSCALAR( c, s ) \
|
||||
do{ (c).r *= (s);\
|
||||
(c).i *= (s); }while(0)
|
||||
#endif
|
||||
|
||||
#ifndef CHECK_OVERFLOW_OP
|
||||
# define CHECK_OVERFLOW_OP(a,op,b) /* noop */
|
||||
#endif
|
||||
|
||||
#define C_ADD( res, a,b)\
|
||||
do { \
|
||||
CHECK_OVERFLOW_OP((a).r,+,(b).r)\
|
||||
CHECK_OVERFLOW_OP((a).i,+,(b).i)\
|
||||
(res).r=(a).r+(b).r; (res).i=(a).i+(b).i; \
|
||||
}while(0)
|
||||
#define C_SUB( res, a,b)\
|
||||
do { \
|
||||
CHECK_OVERFLOW_OP((a).r,-,(b).r)\
|
||||
CHECK_OVERFLOW_OP((a).i,-,(b).i)\
|
||||
(res).r=(a).r-(b).r; (res).i=(a).i-(b).i; \
|
||||
}while(0)
|
||||
#define C_ADDTO( res , a)\
|
||||
do { \
|
||||
CHECK_OVERFLOW_OP((res).r,+,(a).r)\
|
||||
CHECK_OVERFLOW_OP((res).i,+,(a).i)\
|
||||
(res).r += (a).r; (res).i += (a).i;\
|
||||
}while(0)
|
||||
|
||||
#define C_SUBFROM( res , a)\
|
||||
do {\
|
||||
CHECK_OVERFLOW_OP((res).r,-,(a).r)\
|
||||
CHECK_OVERFLOW_OP((res).i,-,(a).i)\
|
||||
(res).r -= (a).r; (res).i -= (a).i; \
|
||||
}while(0)
|
||||
|
||||
|
||||
#ifdef FIXED_POINT
|
||||
# define KISS_FFT_COS(phase) floor(.5+SAMP_MAX * cos (phase))
|
||||
# define KISS_FFT_SIN(phase) floor(.5+SAMP_MAX * sin (phase))
|
||||
# define HALF_OF(x) ((x)>>1)
|
||||
#elif defined(USE_SIMD)
|
||||
# define KISS_FFT_COS(phase) _mm_set1_ps( cos(phase) )
|
||||
# define KISS_FFT_SIN(phase) _mm_set1_ps( sin(phase) )
|
||||
# define HALF_OF(x) ((x)*_mm_set1_ps(.5))
|
||||
#else
|
||||
# define KISS_FFT_COS(phase) (kiss_fft_scalar) cos(phase)
|
||||
# define KISS_FFT_SIN(phase) (kiss_fft_scalar) sin(phase)
|
||||
# define HALF_OF(x) ((x)*.5)
|
||||
#endif
|
||||
|
||||
#define kf_cexp(x,phase) \
|
||||
do{ \
|
||||
(x)->r = KISS_FFT_COS(phase);\
|
||||
(x)->i = KISS_FFT_SIN(phase);\
|
||||
}while(0)
|
||||
|
||||
|
||||
/* a debugging function */
|
||||
#define pcpx(c)\
|
||||
fprintf(stderr,"%g + %gi\n",(double)((c)->r),(double)((c)->i) )
|
||||
|
||||
|
||||
#ifdef KISS_FFT_USE_ALLOCA
|
||||
// define this to allow use of alloca instead of malloc for temporary buffers
|
||||
// Temporary buffers are used in two case:
|
||||
// 1. FFT sizes that have "bad" factors. i.e. not 2,3 and 5
|
||||
// 2. "in-place" FFTs. Notice the quotes, since kissfft does not really do an in-place transform.
|
||||
#include <alloca.h>
|
||||
#define KISS_FFT_TMP_ALLOC(nbytes) alloca(nbytes)
|
||||
#define KISS_FFT_TMP_FREE(ptr)
|
||||
#else
|
||||
#define KISS_FFT_TMP_ALLOC(nbytes) KISS_FFT_MALLOC(nbytes)
|
||||
#define KISS_FFT_TMP_FREE(ptr) KISS_FFT_FREE(ptr)
|
||||
#endif
|
124
Source/PS_Source/contrib/kiss_fft.h
Normal file
124
Source/PS_Source/contrib/kiss_fft.h
Normal file
@ -0,0 +1,124 @@
|
||||
#ifndef KISS_FFT_H
|
||||
#define KISS_FFT_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
ATTENTION!
|
||||
If you would like a :
|
||||
-- a utility that will handle the caching of fft objects
|
||||
-- real-only (no imaginary time component ) FFT
|
||||
-- a multi-dimensional FFT
|
||||
-- a command-line utility to perform ffts
|
||||
-- a command-line utility to perform fast-convolution filtering
|
||||
|
||||
Then see kfc.h kiss_fftr.h kiss_fftnd.h fftutil.c kiss_fastfir.c
|
||||
in the tools/ directory.
|
||||
*/
|
||||
|
||||
#ifdef USE_SIMD
|
||||
# include <xmmintrin.h>
|
||||
# define kiss_fft_scalar __m128
|
||||
#define KISS_FFT_MALLOC(nbytes) _mm_malloc(nbytes,16)
|
||||
#define KISS_FFT_FREE _mm_free
|
||||
#else
|
||||
#define KISS_FFT_MALLOC malloc
|
||||
#define KISS_FFT_FREE free
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef FIXED_POINT
|
||||
#include <sys/types.h>
|
||||
# if (FIXED_POINT == 32)
|
||||
# define kiss_fft_scalar int32_t
|
||||
# else
|
||||
# define kiss_fft_scalar int16_t
|
||||
# endif
|
||||
#else
|
||||
# ifndef kiss_fft_scalar
|
||||
/* default is float */
|
||||
# define kiss_fft_scalar float
|
||||
# endif
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
kiss_fft_scalar r;
|
||||
kiss_fft_scalar i;
|
||||
}kiss_fft_cpx;
|
||||
|
||||
typedef struct kiss_fft_state* kiss_fft_cfg;
|
||||
|
||||
/*
|
||||
* kiss_fft_alloc
|
||||
*
|
||||
* Initialize a FFT (or IFFT) algorithm's cfg/state buffer.
|
||||
*
|
||||
* typical usage: kiss_fft_cfg mycfg=kiss_fft_alloc(1024,0,NULL,NULL);
|
||||
*
|
||||
* The return value from fft_alloc is a cfg buffer used internally
|
||||
* by the fft routine or NULL.
|
||||
*
|
||||
* If lenmem is NULL, then kiss_fft_alloc will allocate a cfg buffer using malloc.
|
||||
* The returned value should be free()d when done to avoid memory leaks.
|
||||
*
|
||||
* The state can be placed in a user supplied buffer 'mem':
|
||||
* If lenmem is not NULL and mem is not NULL and *lenmem is large enough,
|
||||
* then the function places the cfg in mem and the size used in *lenmem
|
||||
* and returns mem.
|
||||
*
|
||||
* If lenmem is not NULL and ( mem is NULL or *lenmem is not large enough),
|
||||
* then the function returns NULL and places the minimum cfg
|
||||
* buffer size in *lenmem.
|
||||
* */
|
||||
|
||||
kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem);
|
||||
|
||||
/*
|
||||
* kiss_fft(cfg,in_out_buf)
|
||||
*
|
||||
* Perform an FFT on a complex input buffer.
|
||||
* for a forward FFT,
|
||||
* fin should be f[0] , f[1] , ... ,f[nfft-1]
|
||||
* fout will be F[0] , F[1] , ... ,F[nfft-1]
|
||||
* Note that each element is complex and can be accessed like
|
||||
f[k].r and f[k].i
|
||||
* */
|
||||
void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout);
|
||||
|
||||
/*
|
||||
A more generic version of the above function. It reads its input from every Nth sample.
|
||||
* */
|
||||
void kiss_fft_stride(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int fin_stride);
|
||||
|
||||
/* If kiss_fft_alloc allocated a buffer, it is one contiguous
|
||||
buffer and can be simply free()d when no longer needed*/
|
||||
#define kiss_fft_free free
|
||||
|
||||
/*
|
||||
Cleans up some memory that gets managed internally. Not necessary to call, but it might clean up
|
||||
your compiler output to call this before you exit.
|
||||
*/
|
||||
void kiss_fft_cleanup(void);
|
||||
|
||||
|
||||
/*
|
||||
* Returns the smallest integer k, such that k>=n and k has only "fast" factors (2,3,5)
|
||||
*/
|
||||
int kiss_fft_next_fast_size(int n);
|
||||
|
||||
/* for real ffts, we need an even size */
|
||||
#define kiss_fftr_next_fast_size_real(n) \
|
||||
(kiss_fft_next_fast_size( ((n)+1)>>1)<<1)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
46
Source/PS_Source/contrib/kiss_fftr.h
Normal file
46
Source/PS_Source/contrib/kiss_fftr.h
Normal file
@ -0,0 +1,46 @@
|
||||
#ifndef KISS_FTR_H
|
||||
#define KISS_FTR_H
|
||||
|
||||
#include "kiss_fft.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
|
||||
Real optimized version can save about 45% cpu time vs. complex fft of a real seq.
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
typedef struct kiss_fftr_state *kiss_fftr_cfg;
|
||||
|
||||
|
||||
kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem, size_t * lenmem);
|
||||
/*
|
||||
nfft must be even
|
||||
|
||||
If you don't care to allocate space, use mem = lenmem = NULL
|
||||
*/
|
||||
|
||||
|
||||
void kiss_fftr(kiss_fftr_cfg cfg,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata);
|
||||
/*
|
||||
input timedata has nfft scalar points
|
||||
output freqdata has nfft/2+1 complex points
|
||||
*/
|
||||
|
||||
void kiss_fftri(kiss_fftr_cfg cfg,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata);
|
||||
/*
|
||||
input freqdata has nfft/2+1 complex points
|
||||
output timedata has nfft scalar points
|
||||
*/
|
||||
|
||||
#define kiss_fftr_free free
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
514
Source/PS_Source/fftw3.h
Normal file
514
Source/PS_Source/fftw3.h
Normal file
@ -0,0 +1,514 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2007-14 Matteo Frigo
|
||||
* Copyright (c) 2003, 2007-14 Massachusetts Institute of Technology
|
||||
*
|
||||
* The following statement of license applies *only* to this header file,
|
||||
* and *not* to the other files distributed with FFTW or derived therefrom:
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
|
||||
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/***************************** NOTE TO USERS *********************************
|
||||
*
|
||||
* THIS IS A HEADER FILE, NOT A MANUAL
|
||||
*
|
||||
* If you want to know how to use FFTW, please read the manual,
|
||||
* online at http://www.fftw.org/doc/ and also included with FFTW.
|
||||
* For a quick start, see the manual's tutorial section.
|
||||
*
|
||||
* (Reading header files to learn how to use a library is a habit
|
||||
* stemming from code lacking a proper manual. Arguably, it's a
|
||||
* *bad* habit in most cases, because header files can contain
|
||||
* interfaces that are not part of the public, stable API.)
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef FFTW3_H
|
||||
#define FFTW3_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/* If <complex.h> is included, use the C99 complex type. Otherwise
|
||||
define a type bit-compatible with C99 complex */
|
||||
#if !defined(FFTW_NO_Complex) && defined(_Complex_I) && defined(complex) && defined(I)
|
||||
# define FFTW_DEFINE_COMPLEX(R, C) typedef R _Complex C
|
||||
#else
|
||||
# define FFTW_DEFINE_COMPLEX(R, C) typedef R C[2]
|
||||
#endif
|
||||
|
||||
#define FFTW_CONCAT(prefix, name) prefix ## name
|
||||
#define FFTW_MANGLE_DOUBLE(name) FFTW_CONCAT(fftw_, name)
|
||||
#define FFTW_MANGLE_FLOAT(name) FFTW_CONCAT(fftwf_, name)
|
||||
#define FFTW_MANGLE_LONG_DOUBLE(name) FFTW_CONCAT(fftwl_, name)
|
||||
#define FFTW_MANGLE_QUAD(name) FFTW_CONCAT(fftwq_, name)
|
||||
|
||||
/* IMPORTANT: for Windows compilers, you should add a line
|
||||
#define FFTW_DLL
|
||||
here and in kernel/ifftw.h if you are compiling/using FFTW as a
|
||||
DLL, in order to do the proper importing/exporting, or
|
||||
alternatively compile with -DFFTW_DLL or the equivalent
|
||||
command-line flag. This is not necessary under MinGW/Cygwin, where
|
||||
libtool does the imports/exports automatically. */
|
||||
#if defined(FFTW_DLL) && (defined(_WIN32) || defined(__WIN32__))
|
||||
/* annoying Windows syntax for shared-library declarations */
|
||||
# if defined(COMPILING_FFTW) /* defined in api.h when compiling FFTW */
|
||||
# define FFTW_EXTERN extern __declspec(dllexport)
|
||||
# else /* user is calling FFTW; import symbol */
|
||||
# define FFTW_EXTERN extern __declspec(dllimport)
|
||||
# endif
|
||||
#else
|
||||
# define FFTW_EXTERN extern
|
||||
#endif
|
||||
|
||||
/* specify calling convention (Windows only) */
|
||||
#if defined(_WIN32) || defined(__WIN32__)
|
||||
# define FFTW_CDECL __cdecl
|
||||
#else
|
||||
# define FFTW_CDECL
|
||||
#endif
|
||||
|
||||
enum fftw_r2r_kind_do_not_use_me {
|
||||
FFTW_R2HC=0, FFTW_HC2R=1, FFTW_DHT=2,
|
||||
FFTW_REDFT00=3, FFTW_REDFT01=4, FFTW_REDFT10=5, FFTW_REDFT11=6,
|
||||
FFTW_RODFT00=7, FFTW_RODFT01=8, FFTW_RODFT10=9, FFTW_RODFT11=10
|
||||
};
|
||||
|
||||
struct fftw_iodim_do_not_use_me {
|
||||
int n; /* dimension size */
|
||||
int is; /* input stride */
|
||||
int os; /* output stride */
|
||||
};
|
||||
|
||||
#include <stddef.h> /* for ptrdiff_t */
|
||||
struct fftw_iodim64_do_not_use_me {
|
||||
ptrdiff_t n; /* dimension size */
|
||||
ptrdiff_t is; /* input stride */
|
||||
ptrdiff_t os; /* output stride */
|
||||
};
|
||||
|
||||
typedef void (FFTW_CDECL *fftw_write_char_func_do_not_use_me)(char c, void *);
|
||||
typedef int (FFTW_CDECL *fftw_read_char_func_do_not_use_me)(void *);
|
||||
|
||||
/*
|
||||
huge second-order macro that defines prototypes for all API
|
||||
functions. We expand this macro for each supported precision
|
||||
|
||||
X: name-mangling macro
|
||||
R: real data type
|
||||
C: complex data type
|
||||
*/
|
||||
|
||||
#define FFTW_DEFINE_API(X, R, C) \
|
||||
\
|
||||
FFTW_DEFINE_COMPLEX(R, C); \
|
||||
\
|
||||
typedef struct X(plan_s) *X(plan); \
|
||||
\
|
||||
typedef struct fftw_iodim_do_not_use_me X(iodim); \
|
||||
typedef struct fftw_iodim64_do_not_use_me X(iodim64); \
|
||||
\
|
||||
typedef enum fftw_r2r_kind_do_not_use_me X(r2r_kind); \
|
||||
\
|
||||
typedef fftw_write_char_func_do_not_use_me X(write_char_func); \
|
||||
typedef fftw_read_char_func_do_not_use_me X(read_char_func); \
|
||||
\
|
||||
FFTW_EXTERN void \
|
||||
FFTW_CDECL X(execute)(const X(plan) p); \
|
||||
\
|
||||
FFTW_EXTERN X(plan) \
|
||||
FFTW_CDECL X(plan_dft)(int rank, const int *n, \
|
||||
C *in, C *out, int sign, unsigned flags); \
|
||||
\
|
||||
FFTW_EXTERN X(plan) \
|
||||
FFTW_CDECL X(plan_dft_1d)(int n, C *in, C *out, int sign, \
|
||||
unsigned flags); \
|
||||
FFTW_EXTERN X(plan) \
|
||||
FFTW_CDECL X(plan_dft_2d)(int n0, int n1, \
|
||||
C *in, C *out, int sign, unsigned flags); \
|
||||
FFTW_EXTERN X(plan) \
|
||||
FFTW_CDECL X(plan_dft_3d)(int n0, int n1, int n2, \
|
||||
C *in, C *out, int sign, unsigned flags); \
|
||||
\
|
||||
FFTW_EXTERN X(plan) \
|
||||
FFTW_CDECL X(plan_many_dft)(int rank, const int *n, \
|
||||
int howmany, \
|
||||
C *in, const int *inembed, \
|
||||
int istride, int idist, \
|
||||
C *out, const int *onembed, \
|
||||
int ostride, int odist, \
|
||||
int sign, unsigned flags); \
|
||||
\
|
||||
FFTW_EXTERN X(plan) \
|
||||
FFTW_CDECL X(plan_guru_dft)(int rank, const X(iodim) *dims, \
|
||||
int howmany_rank, \
|
||||
const X(iodim) *howmany_dims, \
|
||||
C *in, C *out, \
|
||||
int sign, unsigned flags); \
|
||||
FFTW_EXTERN X(plan) \
|
||||
FFTW_CDECL X(plan_guru_split_dft)(int rank, const X(iodim) *dims, \
|
||||
int howmany_rank, \
|
||||
const X(iodim) *howmany_dims, \
|
||||
R *ri, R *ii, R *ro, R *io, \
|
||||
unsigned flags); \
|
||||
\
|
||||
FFTW_EXTERN X(plan) \
|
||||
FFTW_CDECL X(plan_guru64_dft)(int rank, \
|
||||
const X(iodim64) *dims, \
|
||||
int howmany_rank, \
|
||||
const X(iodim64) *howmany_dims, \
|
||||
C *in, C *out, \
|
||||
int sign, unsigned flags); \
|
||||
FFTW_EXTERN X(plan) \
|
||||
FFTW_CDECL X(plan_guru64_split_dft)(int rank, \
|
||||
const X(iodim64) *dims, \
|
||||
int howmany_rank, \
|
||||
const X(iodim64) *howmany_dims, \
|
||||
R *ri, R *ii, R *ro, R *io, \
|
||||
unsigned flags); \
|
||||
\
|
||||
FFTW_EXTERN void \
|
||||
FFTW_CDECL X(execute_dft)(const X(plan) p, C *in, C *out); \
|
||||
\
|
||||
FFTW_EXTERN void \
|
||||
FFTW_CDECL X(execute_split_dft)(const X(plan) p, R *ri, R *ii, \
|
||||
R *ro, R *io); \
|
||||
\
|
||||
FFTW_EXTERN X(plan) \
|
||||
FFTW_CDECL X(plan_many_dft_r2c)(int rank, const int *n, \
|
||||
int howmany, \
|
||||
R *in, const int *inembed, \
|
||||
int istride, int idist, \
|
||||
C *out, const int *onembed, \
|
||||
int ostride, int odist, \
|
||||
unsigned flags); \
|
||||
\
|
||||
FFTW_EXTERN X(plan) \
|
||||
FFTW_CDECL X(plan_dft_r2c)(int rank, const int *n, \
|
||||
R *in, C *out, unsigned flags); \
|
||||
\
|
||||
FFTW_EXTERN X(plan) \
|
||||
FFTW_CDECL X(plan_dft_r2c_1d)(int n,R *in,C *out,unsigned flags); \
|
||||
\
|
||||
FFTW_EXTERN X(plan) \
|
||||
FFTW_CDECL X(plan_dft_r2c_2d)(int n0, int n1, \
|
||||
R *in, C *out, unsigned flags); \
|
||||
\
|
||||
FFTW_EXTERN X(plan) \
|
||||
FFTW_CDECL X(plan_dft_r2c_3d)(int n0, int n1, \
|
||||
int n2, \
|
||||
R *in, C *out, unsigned flags); \
|
||||
\
|
||||
FFTW_EXTERN X(plan) \
|
||||
FFTW_CDECL X(plan_many_dft_c2r)(int rank, const int *n, \
|
||||
int howmany, \
|
||||
C *in, const int *inembed, \
|
||||
int istride, int idist, \
|
||||
R *out, const int *onembed, \
|
||||
int ostride, int odist, \
|
||||
unsigned flags); \
|
||||
\
|
||||
FFTW_EXTERN X(plan) \
|
||||
FFTW_CDECL X(plan_dft_c2r)(int rank, const int *n, \
|
||||
C *in, R *out, unsigned flags); \
|
||||
\
|
||||
FFTW_EXTERN X(plan) \
|
||||
FFTW_CDECL X(plan_dft_c2r_1d)(int n,C *in,R *out,unsigned flags); \
|
||||
\
|
||||
FFTW_EXTERN X(plan) \
|
||||
FFTW_CDECL X(plan_dft_c2r_2d)(int n0, int n1, \
|
||||
C *in, R *out, unsigned flags); \
|
||||
\
|
||||
FFTW_EXTERN X(plan) \
|
||||
FFTW_CDECL X(plan_dft_c2r_3d)(int n0, int n1, \
|
||||
int n2, \
|
||||
C *in, R *out, unsigned flags); \
|
||||
\
|
||||
FFTW_EXTERN X(plan) \
|
||||
FFTW_CDECL X(plan_guru_dft_r2c)(int rank, const X(iodim) *dims, \
|
||||
int howmany_rank, \
|
||||
const X(iodim) *howmany_dims, \
|
||||
R *in, C *out, \
|
||||
unsigned flags); \
|
||||
\
|
||||
FFTW_EXTERN X(plan) \
|
||||
FFTW_CDECL X(plan_guru_dft_c2r)(int rank, const X(iodim) *dims, \
|
||||
int howmany_rank, \
|
||||
const X(iodim) *howmany_dims, \
|
||||
C *in, R *out, \
|
||||
unsigned flags); \
|
||||
\
|
||||
FFTW_EXTERN X(plan) \
|
||||
FFTW_CDECL X(plan_guru_split_dft_r2c)(int rank, const X(iodim) *dims, \
|
||||
int howmany_rank, \
|
||||
const X(iodim) *howmany_dims, \
|
||||
R *in, R *ro, R *io, \
|
||||
unsigned flags); \
|
||||
\
|
||||
FFTW_EXTERN X(plan) \
|
||||
FFTW_CDECL X(plan_guru_split_dft_c2r)(int rank, const X(iodim) *dims, \
|
||||
int howmany_rank, \
|
||||
const X(iodim) *howmany_dims, \
|
||||
R *ri, R *ii, R *out, \
|
||||
unsigned flags); \
|
||||
\
|
||||
FFTW_EXTERN X(plan) \
|
||||
FFTW_CDECL X(plan_guru64_dft_r2c)(int rank, \
|
||||
const X(iodim64) *dims, \
|
||||
int howmany_rank, \
|
||||
const X(iodim64) *howmany_dims, \
|
||||
R *in, C *out, \
|
||||
unsigned flags); \
|
||||
\
|
||||
FFTW_EXTERN X(plan) \
|
||||
FFTW_CDECL X(plan_guru64_dft_c2r)(int rank, \
|
||||
const X(iodim64) *dims, \
|
||||
int howmany_rank, \
|
||||
const X(iodim64) *howmany_dims, \
|
||||
C *in, R *out, \
|
||||
unsigned flags); \
|
||||
\
|
||||
FFTW_EXTERN X(plan) \
|
||||
FFTW_CDECL X(plan_guru64_split_dft_r2c)(int rank, const X(iodim64) *dims, \
|
||||
int howmany_rank, \
|
||||
const X(iodim64) *howmany_dims, \
|
||||
R *in, R *ro, R *io, \
|
||||
unsigned flags); \
|
||||
FFTW_EXTERN X(plan) \
|
||||
FFTW_CDECL X(plan_guru64_split_dft_c2r)(int rank, const X(iodim64) *dims, \
|
||||
int howmany_rank, \
|
||||
const X(iodim64) *howmany_dims, \
|
||||
R *ri, R *ii, R *out, \
|
||||
unsigned flags); \
|
||||
\
|
||||
FFTW_EXTERN void \
|
||||
FFTW_CDECL X(execute_dft_r2c)(const X(plan) p, R *in, C *out); \
|
||||
\
|
||||
FFTW_EXTERN void \
|
||||
FFTW_CDECL X(execute_dft_c2r)(const X(plan) p, C *in, R *out); \
|
||||
\
|
||||
FFTW_EXTERN void \
|
||||
FFTW_CDECL X(execute_split_dft_r2c)(const X(plan) p, \
|
||||
R *in, R *ro, R *io); \
|
||||
\
|
||||
FFTW_EXTERN void \
|
||||
FFTW_CDECL X(execute_split_dft_c2r)(const X(plan) p, \
|
||||
R *ri, R *ii, R *out); \
|
||||
\
|
||||
FFTW_EXTERN X(plan) \
|
||||
FFTW_CDECL X(plan_many_r2r)(int rank, const int *n, \
|
||||
int howmany, \
|
||||
R *in, const int *inembed, \
|
||||
int istride, int idist, \
|
||||
R *out, const int *onembed, \
|
||||
int ostride, int odist, \
|
||||
const X(r2r_kind) *kind, unsigned flags); \
|
||||
\
|
||||
FFTW_EXTERN X(plan) \
|
||||
FFTW_CDECL X(plan_r2r)(int rank, const int *n, R *in, R *out, \
|
||||
const X(r2r_kind) *kind, unsigned flags); \
|
||||
\
|
||||
FFTW_EXTERN X(plan) \
|
||||
FFTW_CDECL X(plan_r2r_1d)(int n, R *in, R *out, \
|
||||
X(r2r_kind) kind, unsigned flags); \
|
||||
\
|
||||
FFTW_EXTERN X(plan) \
|
||||
FFTW_CDECL X(plan_r2r_2d)(int n0, int n1, R *in, R *out, \
|
||||
X(r2r_kind) kind0, X(r2r_kind) kind1, \
|
||||
unsigned flags); \
|
||||
\
|
||||
FFTW_EXTERN X(plan) \
|
||||
FFTW_CDECL X(plan_r2r_3d)(int n0, int n1, int n2, \
|
||||
R *in, R *out, X(r2r_kind) kind0, \
|
||||
X(r2r_kind) kind1, X(r2r_kind) kind2, \
|
||||
unsigned flags); \
|
||||
\
|
||||
FFTW_EXTERN X(plan) \
|
||||
FFTW_CDECL X(plan_guru_r2r)(int rank, const X(iodim) *dims, \
|
||||
int howmany_rank, \
|
||||
const X(iodim) *howmany_dims, \
|
||||
R *in, R *out, \
|
||||
const X(r2r_kind) *kind, unsigned flags); \
|
||||
\
|
||||
FFTW_EXTERN X(plan) \
|
||||
FFTW_CDECL X(plan_guru64_r2r)(int rank, const X(iodim64) *dims, \
|
||||
int howmany_rank, \
|
||||
const X(iodim64) *howmany_dims, \
|
||||
R *in, R *out, \
|
||||
const X(r2r_kind) *kind, unsigned flags); \
|
||||
\
|
||||
FFTW_EXTERN void \
|
||||
FFTW_CDECL X(execute_r2r)(const X(plan) p, R *in, R *out); \
|
||||
\
|
||||
FFTW_EXTERN void \
|
||||
FFTW_CDECL X(destroy_plan)(X(plan) p); \
|
||||
\
|
||||
FFTW_EXTERN void \
|
||||
FFTW_CDECL X(forget_wisdom)(void); \
|
||||
FFTW_EXTERN void \
|
||||
FFTW_CDECL X(cleanup)(void); \
|
||||
\
|
||||
FFTW_EXTERN void \
|
||||
FFTW_CDECL X(set_timelimit)(double t); \
|
||||
\
|
||||
FFTW_EXTERN void \
|
||||
FFTW_CDECL X(plan_with_nthreads)(int nthreads); \
|
||||
\
|
||||
FFTW_EXTERN int \
|
||||
FFTW_CDECL X(init_threads)(void); \
|
||||
\
|
||||
FFTW_EXTERN void \
|
||||
FFTW_CDECL X(cleanup_threads)(void); \
|
||||
\
|
||||
FFTW_EXTERN void \
|
||||
FFTW_CDECL X(make_planner_thread_safe)(void); \
|
||||
\
|
||||
FFTW_EXTERN int \
|
||||
FFTW_CDECL X(export_wisdom_to_filename)(const char *filename); \
|
||||
\
|
||||
FFTW_EXTERN void \
|
||||
FFTW_CDECL X(export_wisdom_to_file)(FILE *output_file); \
|
||||
\
|
||||
FFTW_EXTERN char * \
|
||||
FFTW_CDECL X(export_wisdom_to_string)(void); \
|
||||
\
|
||||
FFTW_EXTERN void \
|
||||
FFTW_CDECL X(export_wisdom)(X(write_char_func) write_char, \
|
||||
void *data); \
|
||||
FFTW_EXTERN int \
|
||||
FFTW_CDECL X(import_system_wisdom)(void); \
|
||||
\
|
||||
FFTW_EXTERN int \
|
||||
FFTW_CDECL X(import_wisdom_from_filename)(const char *filename); \
|
||||
\
|
||||
FFTW_EXTERN int \
|
||||
FFTW_CDECL X(import_wisdom_from_file)(FILE *input_file); \
|
||||
\
|
||||
FFTW_EXTERN int \
|
||||
FFTW_CDECL X(import_wisdom_from_string)(const char *input_string); \
|
||||
\
|
||||
FFTW_EXTERN int \
|
||||
FFTW_CDECL X(import_wisdom)(X(read_char_func) read_char, void *data); \
|
||||
\
|
||||
FFTW_EXTERN void \
|
||||
FFTW_CDECL X(fprint_plan)(const X(plan) p, FILE *output_file); \
|
||||
\
|
||||
FFTW_EXTERN void \
|
||||
FFTW_CDECL X(print_plan)(const X(plan) p); \
|
||||
\
|
||||
FFTW_EXTERN char * \
|
||||
FFTW_CDECL X(sprint_plan)(const X(plan) p); \
|
||||
\
|
||||
FFTW_EXTERN void * \
|
||||
FFTW_CDECL X(malloc)(size_t n); \
|
||||
\
|
||||
FFTW_EXTERN R * \
|
||||
FFTW_CDECL X(alloc_real)(size_t n); \
|
||||
FFTW_EXTERN C * \
|
||||
FFTW_CDECL X(alloc_complex)(size_t n); \
|
||||
\
|
||||
FFTW_EXTERN void \
|
||||
FFTW_CDECL X(free)(void *p); \
|
||||
\
|
||||
FFTW_EXTERN void \
|
||||
FFTW_CDECL X(flops)(const X(plan) p, \
|
||||
double *add, double *mul, double *fmas); \
|
||||
FFTW_EXTERN double \
|
||||
FFTW_CDECL X(estimate_cost)(const X(plan) p); \
|
||||
\
|
||||
FFTW_EXTERN double \
|
||||
FFTW_CDECL X(cost)(const X(plan) p); \
|
||||
\
|
||||
FFTW_EXTERN int \
|
||||
FFTW_CDECL X(alignment_of)(R *p); \
|
||||
\
|
||||
FFTW_EXTERN const char X(version)[]; \
|
||||
FFTW_EXTERN const char X(cc)[]; \
|
||||
FFTW_EXTERN const char X(codelet_optim)[];
|
||||
|
||||
|
||||
/* end of FFTW_DEFINE_API macro */
|
||||
|
||||
FFTW_DEFINE_API(FFTW_MANGLE_DOUBLE, double, fftw_complex)
|
||||
FFTW_DEFINE_API(FFTW_MANGLE_FLOAT, float, fftwf_complex)
|
||||
FFTW_DEFINE_API(FFTW_MANGLE_LONG_DOUBLE, long double, fftwl_complex)
|
||||
|
||||
/* __float128 (quad precision) is a gcc extension on i386, x86_64, and ia64
|
||||
for gcc >= 4.6 (compiled in FFTW with --enable-quad-precision) */
|
||||
#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) \
|
||||
&& !(defined(__ICC) || defined(__INTEL_COMPILER) || defined(__CUDACC__) || defined(__PGI)) \
|
||||
&& (defined(__i386__) || defined(__x86_64__) || defined(__ia64__))
|
||||
# if !defined(FFTW_NO_Complex) && defined(_Complex_I) && defined(complex) && defined(I)
|
||||
/* note: __float128 is a typedef, which is not supported with the _Complex
|
||||
keyword in gcc, so instead we use this ugly __attribute__ version.
|
||||
However, we can't simply pass the __attribute__ version to
|
||||
FFTW_DEFINE_API because the __attribute__ confuses gcc in pointer
|
||||
types. Hence redefining FFTW_DEFINE_COMPLEX. Ugh. */
|
||||
# undef FFTW_DEFINE_COMPLEX
|
||||
# define FFTW_DEFINE_COMPLEX(R, C) typedef _Complex float __attribute__((mode(TC))) C
|
||||
# endif
|
||||
FFTW_DEFINE_API(FFTW_MANGLE_QUAD, __float128, fftwq_complex)
|
||||
#endif
|
||||
|
||||
#define FFTW_FORWARD (-1)
|
||||
#define FFTW_BACKWARD (+1)
|
||||
|
||||
#define FFTW_NO_TIMELIMIT (-1.0)
|
||||
|
||||
/* documented flags */
|
||||
#define FFTW_MEASURE (0U)
|
||||
#define FFTW_DESTROY_INPUT (1U << 0)
|
||||
#define FFTW_UNALIGNED (1U << 1)
|
||||
#define FFTW_CONSERVE_MEMORY (1U << 2)
|
||||
#define FFTW_EXHAUSTIVE (1U << 3) /* NO_EXHAUSTIVE is default */
|
||||
#define FFTW_PRESERVE_INPUT (1U << 4) /* cancels FFTW_DESTROY_INPUT */
|
||||
#define FFTW_PATIENT (1U << 5) /* IMPATIENT is default */
|
||||
#define FFTW_ESTIMATE (1U << 6)
|
||||
#define FFTW_WISDOM_ONLY (1U << 21)
|
||||
|
||||
/* undocumented beyond-guru flags */
|
||||
#define FFTW_ESTIMATE_PATIENT (1U << 7)
|
||||
#define FFTW_BELIEVE_PCOST (1U << 8)
|
||||
#define FFTW_NO_DFT_R2HC (1U << 9)
|
||||
#define FFTW_NO_NONTHREADED (1U << 10)
|
||||
#define FFTW_NO_BUFFERING (1U << 11)
|
||||
#define FFTW_NO_INDIRECT_OP (1U << 12)
|
||||
#define FFTW_ALLOW_LARGE_GENERIC (1U << 13) /* NO_LARGE_GENERIC is default */
|
||||
#define FFTW_NO_RANK_SPLITS (1U << 14)
|
||||
#define FFTW_NO_VRANK_SPLITS (1U << 15)
|
||||
#define FFTW_NO_VRECURSE (1U << 16)
|
||||
#define FFTW_NO_SIMD (1U << 17)
|
||||
#define FFTW_NO_SLOW (1U << 18)
|
||||
#define FFTW_NO_FIXED_RADIX_LARGE_N (1U << 19)
|
||||
#define FFTW_ALLOW_PRUNING (1U << 20)
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* FFTW3_H */
|
27
Source/PS_Source/globals.cpp
Normal file
27
Source/PS_Source/globals.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
Copyright (C) 2006 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
|
||||
*/
|
||||
#include "globals.h"
|
||||
#ifdef WINDOWS
|
||||
#include <windows.h>
|
||||
#include <winbase.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
|
||||
|
189
Source/PS_Source/globals.h
Normal file
189
Source/PS_Source/globals.h
Normal file
@ -0,0 +1,189 @@
|
||||
/*
|
||||
Copyright (C) 2006-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 <vector>
|
||||
#include <memory>
|
||||
#include "../JuceLibraryCode/JuceHeader.h"
|
||||
|
||||
using REALTYPE = float;
|
||||
|
||||
using floatvector = std::vector<REALTYPE>;
|
||||
using float2dvector = std::vector<std::vector<float>>;
|
||||
using float3dvector = std::vector<std::vector<std::vector<float>>>;
|
||||
|
||||
template<typename T>
|
||||
inline std::unique_ptr<T> unique_from_raw(T* ptr)
|
||||
{
|
||||
return std::unique_ptr<T>(ptr);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline String toString(const T& x)
|
||||
{
|
||||
return String(x);
|
||||
}
|
||||
|
||||
inline String toString(double x)
|
||||
{
|
||||
return String(x,3);
|
||||
}
|
||||
|
||||
|
||||
template<typename... Args>
|
||||
inline String formatted(Args... args)
|
||||
{
|
||||
String result;
|
||||
(result << ... << toString(args));
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifndef USEOLDPLAYCURSOR
|
||||
#define USEOLDPLAYCURSOR
|
||||
#endif
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL 0
|
||||
#endif
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265359
|
||||
#endif
|
||||
|
||||
const int g_maxnumoutchans = 32;
|
||||
|
||||
#ifndef USE_LUA_SCRIPTING
|
||||
//#define USE_LUA_SCRIPTING
|
||||
#endif
|
||||
|
||||
inline void storeToTreeProperties(ValueTree dest, UndoManager* uman, juce::Identifier varname, var val)
|
||||
{
|
||||
dest.setProperty(varname, val, uman);
|
||||
}
|
||||
|
||||
template<typename... Ts>
|
||||
inline void storeToTreeProperties(ValueTree dest, UndoManager* uman, juce::Identifier varname, var val, Ts&&... args)
|
||||
{
|
||||
dest.setProperty(varname, val, uman);
|
||||
storeToTreeProperties(dest, uman, args...);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void storeToTreeProperties(ValueTree dest, UndoManager* uman, juce::Identifier varname, Range<T> x)
|
||||
{
|
||||
dest.setProperty(varname+"_start", x.getStart(), uman);
|
||||
dest.setProperty(varname+"_end", x.getEnd(), uman);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void getFromTreeProperties(ValueTree src, juce::Identifier varname, T& val)
|
||||
{
|
||||
if (src.hasProperty(varname))
|
||||
val = src.getProperty(varname);
|
||||
}
|
||||
|
||||
template<typename... Ts, typename T>
|
||||
inline void getFromTreeProperties(ValueTree src, juce::Identifier varname, T& val, Ts&... args)
|
||||
{
|
||||
if (src.hasProperty(varname))
|
||||
val = src.getProperty(varname);
|
||||
getFromTreeProperties(src, args...);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void getFromTreeProperties(ValueTree src, juce::Identifier varname, Range<T>& rng)
|
||||
{
|
||||
if (src.hasProperty(varname + "_start") && src.hasProperty(varname + "_end"))
|
||||
{
|
||||
rng.setStart(src.getProperty(varname + "_start"));
|
||||
rng.setEnd(src.getProperty(varname + "_end"));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
inline void timeCall(String msgprefix,F&& f)
|
||||
{
|
||||
double t0 = Time::getMillisecondCounterHiRes();
|
||||
f();
|
||||
double t1 = Time::getMillisecondCounterHiRes();
|
||||
Logger::writeToLog(formatted(msgprefix, " took " , t1 - t0 , " ms"));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
class CircularBuffer final
|
||||
{
|
||||
public:
|
||||
CircularBuffer(int size)
|
||||
{
|
||||
m_buf.resize(size);
|
||||
}
|
||||
void clear()
|
||||
{
|
||||
m_avail = 0;
|
||||
m_readpos = 0;
|
||||
m_writepos = 0;
|
||||
std::fill(m_buf.begin(), m_buf.end(), T());
|
||||
}
|
||||
void push(T x)
|
||||
{
|
||||
m_buf[m_writepos] = x;
|
||||
++m_writepos;
|
||||
++m_avail;
|
||||
if (m_writepos >= m_buf.size())
|
||||
m_writepos = 0;
|
||||
}
|
||||
T get()
|
||||
{
|
||||
jassert(m_avail > 0);
|
||||
T x = m_buf[m_readpos];
|
||||
++m_readpos;
|
||||
--m_avail;
|
||||
if (m_readpos >= m_buf.size())
|
||||
m_readpos = 0;
|
||||
return x;
|
||||
}
|
||||
int available() { return m_avail; }
|
||||
int getToBuf(T* buf, int len)
|
||||
{
|
||||
jassert(m_avail > 0);
|
||||
if (len > m_avail)
|
||||
len = m_avail;
|
||||
for (int i = 0; i < len; ++i)
|
||||
buf[i] = get();
|
||||
return len;
|
||||
}
|
||||
int getFromBuf(T* buf, int len)
|
||||
{
|
||||
for (int i = 0; i < len; ++i)
|
||||
push(buf[i]);
|
||||
return len;
|
||||
}
|
||||
int getSize() { return (int)m_buf.size(); }
|
||||
void resize(int size)
|
||||
{
|
||||
m_avail = 0;
|
||||
m_readpos = 0;
|
||||
m_writepos = 0;
|
||||
m_buf.resize(size);
|
||||
}
|
||||
private:
|
||||
int m_writepos = 0;
|
||||
int m_readpos = 0;
|
||||
int m_avail = 0;
|
||||
std::vector<T> m_buf;
|
||||
};
|
8
Source/PS_Source/version.h
Normal file
8
Source/PS_Source/version.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef VERSION_H
|
||||
#define VERSION_H
|
||||
|
||||
#define VERSION "version 2.2-2 (20110305)"
|
||||
//#define VERSION ""
|
||||
|
||||
#endif
|
||||
|
43
Source/PluginEditor.cpp
Normal file
43
Source/PluginEditor.cpp
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file was auto-generated!
|
||||
|
||||
It contains the basic framework code for a JUCE plugin editor.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "PluginProcessor.h"
|
||||
#include "PluginEditor.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
PaulstretchpluginAudioProcessorEditor::PaulstretchpluginAudioProcessorEditor (PaulstretchpluginAudioProcessor& p)
|
||||
: AudioProcessorEditor (&p), processor (p)
|
||||
{
|
||||
// Make sure that before the constructor has finished, you've set the
|
||||
// editor's size to whatever you need it to be.
|
||||
setSize (400, 300);
|
||||
}
|
||||
|
||||
PaulstretchpluginAudioProcessorEditor::~PaulstretchpluginAudioProcessorEditor()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void PaulstretchpluginAudioProcessorEditor::paint (Graphics& g)
|
||||
{
|
||||
// (Our component is opaque, so we must completely fill the background with a solid colour)
|
||||
g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));
|
||||
|
||||
g.setColour (Colours::white);
|
||||
g.setFont (15.0f);
|
||||
g.drawFittedText ("Hello World!", getLocalBounds(), Justification::centred, 1);
|
||||
}
|
||||
|
||||
void PaulstretchpluginAudioProcessorEditor::resized()
|
||||
{
|
||||
// This is generally where you'll want to lay out the positions of any
|
||||
// subcomponents in your editor..
|
||||
}
|
36
Source/PluginEditor.h
Normal file
36
Source/PluginEditor.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file was auto-generated!
|
||||
|
||||
It contains the basic framework code for a JUCE plugin editor.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../JuceLibraryCode/JuceHeader.h"
|
||||
#include "PluginProcessor.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
*/
|
||||
class PaulstretchpluginAudioProcessorEditor : public AudioProcessorEditor
|
||||
{
|
||||
public:
|
||||
PaulstretchpluginAudioProcessorEditor (PaulstretchpluginAudioProcessor&);
|
||||
~PaulstretchpluginAudioProcessorEditor();
|
||||
|
||||
//==============================================================================
|
||||
void paint (Graphics&) override;
|
||||
void resized() override;
|
||||
|
||||
private:
|
||||
// This reference is provided as a quick way for your editor to
|
||||
// access the processor object that created it.
|
||||
PaulstretchpluginAudioProcessor& processor;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PaulstretchpluginAudioProcessorEditor)
|
||||
};
|
191
Source/PluginProcessor.cpp
Normal file
191
Source/PluginProcessor.cpp
Normal file
@ -0,0 +1,191 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file was auto-generated!
|
||||
|
||||
It contains the basic framework code for a JUCE plugin processor.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "PluginProcessor.h"
|
||||
#include "PluginEditor.h"
|
||||
std::unique_ptr<PropertiesFile> g_propsfile;
|
||||
|
||||
//==============================================================================
|
||||
PaulstretchpluginAudioProcessor::PaulstretchpluginAudioProcessor()
|
||||
#ifndef JucePlugin_PreferredChannelConfigurations
|
||||
: AudioProcessor (BusesProperties()
|
||||
#if ! JucePlugin_IsMidiEffect
|
||||
#if ! JucePlugin_IsSynth
|
||||
.withInput ("Input", AudioChannelSet::stereo(), true)
|
||||
#endif
|
||||
.withOutput ("Output", AudioChannelSet::stereo(), true)
|
||||
#endif
|
||||
)
|
||||
#endif
|
||||
{
|
||||
m_afm = std::make_unique<AudioFormatManager>();
|
||||
m_afm->registerBasicFormats();
|
||||
m_control = std::make_unique<Control>(m_afm.get());
|
||||
}
|
||||
|
||||
PaulstretchpluginAudioProcessor::~PaulstretchpluginAudioProcessor()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
const String PaulstretchpluginAudioProcessor::getName() const
|
||||
{
|
||||
return JucePlugin_Name;
|
||||
}
|
||||
|
||||
bool PaulstretchpluginAudioProcessor::acceptsMidi() const
|
||||
{
|
||||
#if JucePlugin_WantsMidiInput
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool PaulstretchpluginAudioProcessor::producesMidi() const
|
||||
{
|
||||
#if JucePlugin_ProducesMidiOutput
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool PaulstretchpluginAudioProcessor::isMidiEffect() const
|
||||
{
|
||||
#if JucePlugin_IsMidiEffect
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
double PaulstretchpluginAudioProcessor::getTailLengthSeconds() const
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
int PaulstretchpluginAudioProcessor::getNumPrograms()
|
||||
{
|
||||
return 1; // NB: some hosts don't cope very well if you tell them there are 0 programs,
|
||||
// so this should be at least 1, even if you're not really implementing programs.
|
||||
}
|
||||
|
||||
int PaulstretchpluginAudioProcessor::getCurrentProgram()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PaulstretchpluginAudioProcessor::setCurrentProgram (int index)
|
||||
{
|
||||
}
|
||||
|
||||
const String PaulstretchpluginAudioProcessor::getProgramName (int index)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
void PaulstretchpluginAudioProcessor::changeProgramName (int index, const String& newName)
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void PaulstretchpluginAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
|
||||
{
|
||||
// Use this method as the place to do any pre-playback
|
||||
// initialisation that you need..
|
||||
}
|
||||
|
||||
void PaulstretchpluginAudioProcessor::releaseResources()
|
||||
{
|
||||
// When playback stops, you can use this as an opportunity to free up any
|
||||
// spare memory, etc.
|
||||
}
|
||||
|
||||
#ifndef JucePlugin_PreferredChannelConfigurations
|
||||
bool PaulstretchpluginAudioProcessor::isBusesLayoutSupported (const BusesLayout& layouts) const
|
||||
{
|
||||
#if JucePlugin_IsMidiEffect
|
||||
ignoreUnused (layouts);
|
||||
return true;
|
||||
#else
|
||||
// This is the place where you check if the layout is supported.
|
||||
// In this template code we only support mono or stereo.
|
||||
if (layouts.getMainOutputChannelSet() != AudioChannelSet::mono()
|
||||
&& layouts.getMainOutputChannelSet() != AudioChannelSet::stereo())
|
||||
return false;
|
||||
|
||||
// This checks if the input layout matches the output layout
|
||||
#if ! JucePlugin_IsSynth
|
||||
if (layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet())
|
||||
return false;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
void PaulstretchpluginAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
|
||||
{
|
||||
ScopedNoDenormals noDenormals;
|
||||
const int totalNumInputChannels = getTotalNumInputChannels();
|
||||
const int totalNumOutputChannels = getTotalNumOutputChannels();
|
||||
|
||||
// In case we have more outputs than inputs, this code clears any output
|
||||
// channels that didn't contain input data, (because these aren't
|
||||
// guaranteed to be empty - they may contain garbage).
|
||||
// This is here to avoid people getting screaming feedback
|
||||
// when they first compile a plugin, but obviously you don't need to keep
|
||||
// this code if your algorithm always overwrites all the output channels.
|
||||
for (int i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
|
||||
buffer.clear (i, 0, buffer.getNumSamples());
|
||||
|
||||
// This is the place where you'd normally do the guts of your plugin's
|
||||
// audio processing...
|
||||
for (int channel = 0; channel < totalNumInputChannels; ++channel)
|
||||
{
|
||||
float* channelData = buffer.getWritePointer (channel);
|
||||
|
||||
// ..do something to the data...
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool PaulstretchpluginAudioProcessor::hasEditor() const
|
||||
{
|
||||
return true; // (change this to false if you choose to not supply an editor)
|
||||
}
|
||||
|
||||
AudioProcessorEditor* PaulstretchpluginAudioProcessor::createEditor()
|
||||
{
|
||||
return new PaulstretchpluginAudioProcessorEditor (*this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void PaulstretchpluginAudioProcessor::getStateInformation (MemoryBlock& destData)
|
||||
{
|
||||
// You should use this method to store your parameters in the memory block.
|
||||
// You could do that either as raw data, or use the XML or ValueTree classes
|
||||
// as intermediaries to make it easy to save and load complex data.
|
||||
}
|
||||
|
||||
void PaulstretchpluginAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
|
||||
{
|
||||
// You should use this method to restore your parameters from this memory block,
|
||||
// whose contents will have been created by the getStateInformation() call.
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// This creates new instances of the plugin..
|
||||
AudioProcessor* JUCE_CALLTYPE createPluginFilter()
|
||||
{
|
||||
return new PaulstretchpluginAudioProcessor();
|
||||
}
|
64
Source/PluginProcessor.h
Normal file
64
Source/PluginProcessor.h
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file was auto-generated!
|
||||
|
||||
It contains the basic framework code for a JUCE plugin processor.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../JuceLibraryCode/JuceHeader.h"
|
||||
#include "PS_Source/PaulStretchControl.h"
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
*/
|
||||
class PaulstretchpluginAudioProcessor : public AudioProcessor
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
PaulstretchpluginAudioProcessor();
|
||||
~PaulstretchpluginAudioProcessor();
|
||||
|
||||
//==============================================================================
|
||||
void prepareToPlay (double sampleRate, int samplesPerBlock) override;
|
||||
void releaseResources() override;
|
||||
|
||||
#ifndef JucePlugin_PreferredChannelConfigurations
|
||||
bool isBusesLayoutSupported (const BusesLayout& layouts) const override;
|
||||
#endif
|
||||
|
||||
void processBlock (AudioSampleBuffer&, MidiBuffer&) override;
|
||||
|
||||
//==============================================================================
|
||||
AudioProcessorEditor* createEditor() override;
|
||||
bool hasEditor() const override;
|
||||
|
||||
//==============================================================================
|
||||
const String getName() const override;
|
||||
|
||||
bool acceptsMidi() const override;
|
||||
bool producesMidi() const override;
|
||||
bool isMidiEffect () const override;
|
||||
double getTailLengthSeconds() const override;
|
||||
|
||||
//==============================================================================
|
||||
int getNumPrograms() override;
|
||||
int getCurrentProgram() override;
|
||||
void setCurrentProgram (int index) override;
|
||||
const String getProgramName (int index) override;
|
||||
void changeProgramName (int index, const String& newName) override;
|
||||
|
||||
//==============================================================================
|
||||
void getStateInformation (MemoryBlock& destData) override;
|
||||
void setStateInformation (const void* data, int sizeInBytes) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<Control> m_control;
|
||||
std::unique_ptr<AudioFormatManager> m_afm;
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PaulstretchpluginAudioProcessor)
|
||||
};
|
166
Source/WDL/denormal.h
Normal file
166
Source/WDL/denormal.h
Normal file
@ -0,0 +1,166 @@
|
||||
#ifndef _WDL_DENORMAL_H_
|
||||
#define _WDL_DENORMAL_H_
|
||||
|
||||
typedef struct
|
||||
{
|
||||
#ifdef __ppc__ // todo: other big endian platforms...
|
||||
unsigned int hw;
|
||||
unsigned int lw;
|
||||
#else
|
||||
unsigned int lw;
|
||||
unsigned int hw;
|
||||
#endif
|
||||
} WDL_DenormalTwoInts;
|
||||
|
||||
typedef union { double fl; WDL_DenormalTwoInts w; } WDL_DenormalDoubleAccess;
|
||||
typedef union { float fl; unsigned int w; } WDL_DenormalFloatAccess;
|
||||
|
||||
|
||||
// note: the _aggressive versions filter out anything less than around 1.0e-16 or so (approximately) to 0.0, including -0.0 (becomes 0.0)
|
||||
// note: new! the _aggressive versions also filter inf and NaN to 0.0
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define WDL_DENORMAL_INLINE inline
|
||||
#elif defined(_MSC_VER)
|
||||
#define WDL_DENORMAL_INLINE __inline
|
||||
#else
|
||||
#define WDL_DENORMAL_INLINE
|
||||
#endif
|
||||
|
||||
#define WDL_DENORMAL_DOUBLE_HW(a) (((const WDL_DenormalDoubleAccess*)(a))->w.hw)
|
||||
#define WDL_DENORMAL_DOUBLE_LW(a) (((const WDL_DenormalDoubleAccess*)(a))->w.lw)
|
||||
#define WDL_DENORMAL_FLOAT_W(a) (((const WDL_DenormalFloatAccess*)(a))->w)
|
||||
|
||||
#define WDL_DENORMAL_DOUBLE_HW_NC(a) (((WDL_DenormalDoubleAccess*)(a))->w.hw)
|
||||
#define WDL_DENORMAL_DOUBLE_LW_NC(a) (((WDL_DenormalDoubleAccess*)(a))->w.lw)
|
||||
#define WDL_DENORMAL_FLOAT_W_NC(a) (((WDL_DenormalFloatAccess*)(a))->w)
|
||||
|
||||
#define WDL_DENORMAL_DOUBLE_AGGRESSIVE_CUTOFF 0x3cA00000 // 0x3B8000000 maybe instead? that's 10^-5 smaller or so
|
||||
#define WDL_DENORMAL_FLOAT_AGGRESSIVE_CUTOFF 0x25000000
|
||||
|
||||
|
||||
static double WDL_DENORMAL_INLINE denormal_filter_double(double a)
|
||||
{
|
||||
return (WDL_DENORMAL_DOUBLE_HW(&a)&0x7ff00000) ? a : 0.0;
|
||||
}
|
||||
|
||||
static double WDL_DENORMAL_INLINE denormal_filter_double2(double a)
|
||||
{
|
||||
return ((WDL_DENORMAL_DOUBLE_HW(&a)+0x100000)&0x7ff00000) > 0x100000 ? a : 0.0;
|
||||
}
|
||||
|
||||
static double WDL_DENORMAL_INLINE denormal_filter_double_aggressive(double a)
|
||||
{
|
||||
return ((WDL_DENORMAL_DOUBLE_HW(&a)+0x100000)&0x7ff00000) >= WDL_DENORMAL_DOUBLE_AGGRESSIVE_CUTOFF ? a : 0.0;
|
||||
}
|
||||
|
||||
static float WDL_DENORMAL_INLINE denormal_filter_float(float a)
|
||||
{
|
||||
return (WDL_DENORMAL_FLOAT_W(&a)&0x7f800000) ? a : 0.0f;
|
||||
}
|
||||
|
||||
static float WDL_DENORMAL_INLINE denormal_filter_float2(float a)
|
||||
{
|
||||
return ((WDL_DENORMAL_FLOAT_W(&a)+0x800000)&0x7f800000) > 0x800000 ? a : 0.0f;
|
||||
}
|
||||
|
||||
|
||||
static float WDL_DENORMAL_INLINE denormal_filter_float_aggressive(float a)
|
||||
{
|
||||
return ((WDL_DENORMAL_FLOAT_W(&a)+0x800000)&0x7f800000) >= WDL_DENORMAL_FLOAT_AGGRESSIVE_CUTOFF ? a : 0.0f;
|
||||
}
|
||||
static void WDL_DENORMAL_INLINE denormal_fix_double(double *a)
|
||||
{
|
||||
if (!(WDL_DENORMAL_DOUBLE_HW(a)&0x7ff00000)) *a=0.0;
|
||||
}
|
||||
|
||||
static void WDL_DENORMAL_INLINE denormal_fix_double_aggressive(double *a)
|
||||
{
|
||||
if (((WDL_DENORMAL_DOUBLE_HW(a)+0x100000)&0x7ff00000) < WDL_DENORMAL_DOUBLE_AGGRESSIVE_CUTOFF) *a=0.0;
|
||||
}
|
||||
|
||||
static void WDL_DENORMAL_INLINE denormal_fix_float(float *a)
|
||||
{
|
||||
if (!(WDL_DENORMAL_FLOAT_W(a)&0x7f800000)) *a=0.0f;
|
||||
}
|
||||
static void WDL_DENORMAL_INLINE denormal_fix_float_aggressive(float *a)
|
||||
{
|
||||
if (((WDL_DENORMAL_FLOAT_W(a)+0x800000)&0x7f800000) < WDL_DENORMAL_FLOAT_AGGRESSIVE_CUTOFF) *a=0.0f;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus // automatic typed versions (though one should probably use the explicit versions...
|
||||
|
||||
|
||||
static double WDL_DENORMAL_INLINE denormal_filter(double a)
|
||||
{
|
||||
return (WDL_DENORMAL_DOUBLE_HW(&a)&0x7ff00000) ? a : 0.0;
|
||||
}
|
||||
static double WDL_DENORMAL_INLINE denormal_filter_aggressive(double a)
|
||||
{
|
||||
return ((WDL_DENORMAL_DOUBLE_HW(&a)+0x100000)&0x7ff00000) >= WDL_DENORMAL_DOUBLE_AGGRESSIVE_CUTOFF ? a : 0.0;
|
||||
}
|
||||
|
||||
static float WDL_DENORMAL_INLINE denormal_filter(float a)
|
||||
{
|
||||
return (WDL_DENORMAL_FLOAT_W(&a)&0x7f800000) ? a : 0.0f;
|
||||
}
|
||||
|
||||
static float WDL_DENORMAL_INLINE denormal_filter_aggressive(float a)
|
||||
{
|
||||
return ((WDL_DENORMAL_FLOAT_W(&a)+0x800000)&0x7f800000) >= WDL_DENORMAL_FLOAT_AGGRESSIVE_CUTOFF ? a : 0.0f;
|
||||
}
|
||||
|
||||
static void WDL_DENORMAL_INLINE denormal_fix(double *a)
|
||||
{
|
||||
if (!(WDL_DENORMAL_DOUBLE_HW(a)&0x7ff00000)) *a=0.0;
|
||||
}
|
||||
static void WDL_DENORMAL_INLINE denormal_fix_aggressive(double *a)
|
||||
{
|
||||
if (((WDL_DENORMAL_DOUBLE_HW(a)+0x100000)&0x7ff00000) < WDL_DENORMAL_DOUBLE_AGGRESSIVE_CUTOFF) *a=0.0;
|
||||
}
|
||||
static void WDL_DENORMAL_INLINE denormal_fix(float *a)
|
||||
{
|
||||
if (!(WDL_DENORMAL_FLOAT_W(a)&0x7f800000)) *a=0.0f;
|
||||
}
|
||||
static void WDL_DENORMAL_INLINE denormal_fix_aggressive(float *a)
|
||||
{
|
||||
if (((WDL_DENORMAL_FLOAT_W(a)+0x800000)&0x7f800000) < WDL_DENORMAL_FLOAT_AGGRESSIVE_CUTOFF) *a=0.0f;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif // cplusplus versions
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////
|
||||
// this isnt a denormal function but it is similar, so we'll put it here as a bonus
|
||||
|
||||
static void WDL_DENORMAL_INLINE GetDoubleMaxAbsValue(double *out, const double *in) // note: the value pointed to by "out" must be >=0.0, __NOT__ <= -0.0
|
||||
{
|
||||
unsigned int hw = WDL_DENORMAL_DOUBLE_HW(in)&0x7fffffff;
|
||||
if (hw >= WDL_DENORMAL_DOUBLE_HW(out) && (hw>WDL_DENORMAL_DOUBLE_HW(out) || WDL_DENORMAL_DOUBLE_LW(in) > WDL_DENORMAL_DOUBLE_LW(out)))
|
||||
{
|
||||
WDL_DENORMAL_DOUBLE_LW_NC(out) = WDL_DENORMAL_DOUBLE_LW(in);
|
||||
WDL_DENORMAL_DOUBLE_HW_NC(out) = hw;
|
||||
}
|
||||
}
|
||||
|
||||
static void WDL_DENORMAL_INLINE GetFloatMaxAbsValue(float *out, const float *in) // note: the value pointed to by "out" must be >=0.0, __NOT__ <= -0.0
|
||||
{
|
||||
unsigned int hw = WDL_DENORMAL_FLOAT_W(in)&0x7fffffff;
|
||||
if (hw > WDL_DENORMAL_FLOAT_W(out)) WDL_DENORMAL_FLOAT_W_NC(out)=hw;
|
||||
}
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
static void WDL_DENORMAL_INLINE GetFloatMaxAbsValue(double *out, const double *in) // note: the value pointed to by "out" must be >=0.0, __NOT__ <= -0.0
|
||||
{
|
||||
GetDoubleMaxAbsValue(out,in);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
374
Source/WDL/heapbuf.h
Normal file
374
Source/WDL/heapbuf.h
Normal file
@ -0,0 +1,374 @@
|
||||
/*
|
||||
WDL - heapbuf.h
|
||||
Copyright (C) 2005 and later Cockos Incorporated
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This file provides the interface and implementation for WDL_HeapBuf, a simple
|
||||
malloc() wrapper for resizeable blocks.
|
||||
|
||||
Also in this file is WDL_TypedBuf which is a templated version WDL_HeapBuf
|
||||
that manages type and type-size.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _WDL_HEAPBUF_H_
|
||||
#define _WDL_HEAPBUF_H_
|
||||
|
||||
#ifndef WDL_HEAPBUF_IMPL_ONLY
|
||||
|
||||
#ifdef WDL_HEAPBUF_TRACE
|
||||
#include <windows.h>
|
||||
#define WDL_HEAPBUF_TRACEPARM(x) ,(x)
|
||||
#else
|
||||
#define WDL_HEAPBUF_TRACEPARM(x)
|
||||
#endif
|
||||
|
||||
#include "wdltypes.h"
|
||||
|
||||
class WDL_HeapBuf
|
||||
{
|
||||
public:
|
||||
// interface
|
||||
#ifdef WDL_HEAPBUF_INTF_ONLY
|
||||
void *Resize(int newsize, bool resizedown=true);
|
||||
void CopyFrom(const WDL_HeapBuf *hb, bool exactCopyOfConfig=false);
|
||||
#endif
|
||||
void *Get() const { return m_size?m_buf:NULL; }
|
||||
int GetSize() const { return m_size; }
|
||||
void *GetAligned(int align) const { return (void *)(((UINT_PTR)Get() + (align-1)) & ~(UINT_PTR)(align-1)); }
|
||||
|
||||
void SetGranul(int granul) { m_granul = granul; }
|
||||
int GetGranul() const { return m_granul; }
|
||||
|
||||
void *ResizeOK(int newsize, bool resizedown = true) { void *p=Resize(newsize, resizedown); return GetSize() == newsize ? p : NULL; }
|
||||
|
||||
WDL_HeapBuf(const WDL_HeapBuf &cp)
|
||||
{
|
||||
m_buf=0;
|
||||
CopyFrom(&cp,true);
|
||||
}
|
||||
WDL_HeapBuf &operator=(const WDL_HeapBuf &cp)
|
||||
{
|
||||
CopyFrom(&cp,false);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifndef WDL_HEAPBUF_TRACE
|
||||
explicit WDL_HeapBuf(int granul=4096) : m_buf(NULL), m_alloc(0), m_size(0), m_granul(granul)
|
||||
{
|
||||
}
|
||||
~WDL_HeapBuf()
|
||||
{
|
||||
free(m_buf);
|
||||
}
|
||||
#else
|
||||
explicit WDL_HeapBuf(int granul=4096, const char *tracetype="WDL_HeapBuf"
|
||||
) : m_buf(NULL), m_alloc(0), m_size(0), m_granul(granul)
|
||||
{
|
||||
m_tracetype = tracetype;
|
||||
char tmp[512];
|
||||
wsprintf(tmp,"WDL_HeapBuf: created type: %s granul=%d\n",tracetype,granul);
|
||||
OutputDebugString(tmp);
|
||||
}
|
||||
~WDL_HeapBuf()
|
||||
{
|
||||
char tmp[512];
|
||||
wsprintf(tmp,"WDL_HeapBuf: destroying type: %s (alloc=%d, size=%d)\n",m_tracetype,m_alloc,m_size);
|
||||
OutputDebugString(tmp);
|
||||
free(m_buf);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // !WDL_HEAPBUF_IMPL_ONLY
|
||||
|
||||
// implementation bits
|
||||
#ifndef WDL_HEAPBUF_INTF_ONLY
|
||||
#ifdef WDL_HEAPBUF_IMPL_ONLY
|
||||
void *WDL_HeapBuf::Resize(int newsize, bool resizedown)
|
||||
#else
|
||||
void *Resize(int newsize, bool resizedown=true)
|
||||
#endif
|
||||
{
|
||||
if (newsize<0) newsize=0;
|
||||
#ifdef DEBUG_TIGHT_ALLOC // horribly slow, do not use for release builds
|
||||
if (newsize == m_size) return m_buf;
|
||||
|
||||
int a = newsize;
|
||||
if (a > m_size) a=m_size;
|
||||
void *newbuf = newsize ? malloc(newsize) : 0;
|
||||
if (!newbuf && newsize)
|
||||
{
|
||||
#ifdef WDL_HEAPBUF_ONMALLOCFAIL
|
||||
WDL_HEAPBUF_ONMALLOCFAIL(newsize)
|
||||
#endif
|
||||
return m_buf;
|
||||
}
|
||||
if (newbuf&&m_buf) memcpy(newbuf,m_buf,a);
|
||||
m_size=m_alloc=newsize;
|
||||
free(m_buf);
|
||||
return m_buf=newbuf;
|
||||
#endif
|
||||
|
||||
if (newsize!=m_size || (resizedown && newsize < m_alloc/2))
|
||||
{
|
||||
int resizedown_under = 0;
|
||||
if (resizedown && newsize < m_size)
|
||||
{
|
||||
// shrinking buffer: only shrink if allocation decreases to min(alloc/2, alloc-granul*4) or 0
|
||||
resizedown_under = m_alloc - (m_granul<<2);
|
||||
if (resizedown_under > m_alloc/2) resizedown_under = m_alloc/2;
|
||||
if (resizedown_under < 1) resizedown_under=1;
|
||||
}
|
||||
|
||||
if (newsize > m_alloc || newsize < resizedown_under)
|
||||
{
|
||||
int granul=newsize/2;
|
||||
int newalloc;
|
||||
if (granul < m_granul) granul=m_granul;
|
||||
|
||||
if (newsize<1) newalloc=0;
|
||||
else if (m_granul<4096) newalloc=newsize+granul;
|
||||
else
|
||||
{
|
||||
granul &= ~4095;
|
||||
if (granul< 4096) granul=4096;
|
||||
else if (granul>4*1024*1024) granul=4*1024*1024;
|
||||
newalloc = ((newsize + granul + 96)&~4095)-96;
|
||||
}
|
||||
|
||||
if (newalloc != m_alloc)
|
||||
{
|
||||
|
||||
#ifdef WDL_HEAPBUF_TRACE
|
||||
char tmp[512];
|
||||
wsprintf(tmp,"WDL_HeapBuf: type %s realloc(%d) from %d\n",m_tracetype,newalloc,m_alloc);
|
||||
OutputDebugString(tmp);
|
||||
#endif
|
||||
if (newalloc <= 0)
|
||||
{
|
||||
free(m_buf);
|
||||
m_buf=0;
|
||||
m_alloc=0;
|
||||
m_size=0;
|
||||
return 0;
|
||||
}
|
||||
void *nbuf=realloc(m_buf,newalloc);
|
||||
if (!nbuf)
|
||||
{
|
||||
if (!(nbuf=malloc(newalloc)))
|
||||
{
|
||||
#ifdef WDL_HEAPBUF_ONMALLOCFAIL
|
||||
WDL_HEAPBUF_ONMALLOCFAIL(newalloc);
|
||||
#endif
|
||||
return m_size?m_buf:0; // failed, do not resize
|
||||
}
|
||||
|
||||
if (m_buf)
|
||||
{
|
||||
int sz=newsize<m_size?newsize:m_size;
|
||||
if (sz>0) memcpy(nbuf,m_buf,sz);
|
||||
free(m_buf);
|
||||
}
|
||||
}
|
||||
|
||||
m_buf=nbuf;
|
||||
m_alloc=newalloc;
|
||||
} // alloc size change
|
||||
} // need size up or down
|
||||
m_size=newsize;
|
||||
} // size change
|
||||
return m_size?m_buf:0;
|
||||
}
|
||||
|
||||
#ifdef WDL_HEAPBUF_IMPL_ONLY
|
||||
void WDL_HeapBuf::CopyFrom(const WDL_HeapBuf *hb, bool exactCopyOfConfig)
|
||||
#else
|
||||
void CopyFrom(const WDL_HeapBuf *hb, bool exactCopyOfConfig=false)
|
||||
#endif
|
||||
{
|
||||
if (exactCopyOfConfig) // copy all settings
|
||||
{
|
||||
free(m_buf);
|
||||
|
||||
#ifdef WDL_HEAPBUF_TRACE
|
||||
m_tracetype = hb->m_tracetype;
|
||||
#endif
|
||||
m_granul = hb->m_granul;
|
||||
|
||||
m_size=m_alloc=0;
|
||||
m_buf=hb->m_buf && hb->m_alloc>0 ? malloc(m_alloc = hb->m_alloc) : NULL;
|
||||
#ifdef WDL_HEAPBUF_ONMALLOCFAIL
|
||||
if (!m_buf && m_alloc) { WDL_HEAPBUF_ONMALLOCFAIL(m_alloc) } ;
|
||||
#endif
|
||||
if (m_buf) memcpy(m_buf,hb->m_buf,m_size = hb->m_size);
|
||||
else m_alloc=0;
|
||||
}
|
||||
else // copy just the data + size
|
||||
{
|
||||
const int newsz=hb->GetSize();
|
||||
Resize(newsz,true);
|
||||
if (GetSize()!=newsz) Resize(0);
|
||||
else memcpy(Get(),hb->Get(),newsz);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ! WDL_HEAPBUF_INTF_ONLY
|
||||
|
||||
#ifndef WDL_HEAPBUF_IMPL_ONLY
|
||||
|
||||
private:
|
||||
void *m_buf;
|
||||
int m_alloc;
|
||||
int m_size;
|
||||
int m_granul;
|
||||
|
||||
#if defined(_WIN64) || defined(__LP64__)
|
||||
public:
|
||||
int ___pad; // keep size 8 byte aligned
|
||||
#endif
|
||||
|
||||
#ifdef WDL_HEAPBUF_TRACE
|
||||
const char *m_tracetype;
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
template<class PTRTYPE> class WDL_TypedBuf
|
||||
{
|
||||
public:
|
||||
PTRTYPE *Get() const { return (PTRTYPE *) m_hb.Get(); }
|
||||
int GetSize() const { return m_hb.GetSize()/(unsigned int)sizeof(PTRTYPE); }
|
||||
|
||||
PTRTYPE *Resize(int newsize, bool resizedown = true) { return (PTRTYPE *)m_hb.Resize(newsize*sizeof(PTRTYPE),resizedown); }
|
||||
PTRTYPE *ResizeOK(int newsize, bool resizedown = true) { return (PTRTYPE *)m_hb.ResizeOK(newsize*sizeof(PTRTYPE), resizedown); }
|
||||
|
||||
PTRTYPE *GetAligned(int align) const { return (PTRTYPE *) m_hb.GetAligned(align); }
|
||||
|
||||
PTRTYPE *Add(PTRTYPE val)
|
||||
{
|
||||
const int sz=GetSize();
|
||||
PTRTYPE* p=ResizeOK(sz+1,false);
|
||||
if (p)
|
||||
{
|
||||
p[sz]=val;
|
||||
return p+sz;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
PTRTYPE *Add(const PTRTYPE *buf, int bufsz)
|
||||
{
|
||||
if (bufsz>0)
|
||||
{
|
||||
const int sz=GetSize();
|
||||
PTRTYPE* p=ResizeOK(sz+bufsz,false);
|
||||
if (p)
|
||||
{
|
||||
p+=sz;
|
||||
if (buf) memcpy(p,buf,bufsz*sizeof(PTRTYPE));
|
||||
else memset(p,0,bufsz*sizeof(PTRTYPE));
|
||||
return p;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
PTRTYPE *Set(const PTRTYPE *buf, int bufsz)
|
||||
{
|
||||
if (bufsz>=0)
|
||||
{
|
||||
PTRTYPE* p=ResizeOK(bufsz,false);
|
||||
if (p)
|
||||
{
|
||||
if (buf) memcpy(p,buf,bufsz*sizeof(PTRTYPE));
|
||||
else memset(p,0,bufsz*sizeof(PTRTYPE));
|
||||
return p;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
PTRTYPE* Insert(PTRTYPE val, int idx)
|
||||
{
|
||||
const int sz=GetSize();
|
||||
if (idx >= 0 && idx <= sz)
|
||||
{
|
||||
PTRTYPE* p=ResizeOK(sz+1,false);
|
||||
if (p)
|
||||
{
|
||||
memmove(p+idx+1, p+idx, (sz-idx)*sizeof(PTRTYPE));
|
||||
p[idx]=val;
|
||||
return p+idx;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void Delete(int idx)
|
||||
{
|
||||
PTRTYPE* p=Get();
|
||||
const int sz=GetSize();
|
||||
if (idx >= 0 && idx < sz)
|
||||
{
|
||||
memmove(p+idx, p+idx+1, (sz-idx-1)*sizeof(PTRTYPE));
|
||||
Resize(sz-1,false);
|
||||
}
|
||||
}
|
||||
|
||||
void SetGranul(int gran) { m_hb.SetGranul(gran); }
|
||||
|
||||
int Find(PTRTYPE val) const
|
||||
{
|
||||
const PTRTYPE* p=Get();
|
||||
const int sz=GetSize();
|
||||
int i;
|
||||
for (i=0; i < sz; ++i) if (p[i] == val) return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifndef WDL_HEAPBUF_TRACE
|
||||
explicit WDL_TypedBuf(int granul=4096) : m_hb(granul) { }
|
||||
#else
|
||||
explicit WDL_TypedBuf(int granul=4096, const char *tracetype="WDL_TypedBuf") : m_hb(granul WDL_HEAPBUF_TRACEPARM(tracetype)) { }
|
||||
#endif
|
||||
~WDL_TypedBuf()
|
||||
{
|
||||
}
|
||||
|
||||
PTRTYPE* begin() const { return static_cast<PTRTYPE*>(m_hb.Get()); }
|
||||
PTRTYPE* end() const
|
||||
{
|
||||
PTRTYPE* temp = static_cast<PTRTYPE*>(m_hb.Get());
|
||||
return temp + m_hb.GetSize() / sizeof(PTRTYPE);
|
||||
}
|
||||
|
||||
|
||||
WDL_HeapBuf *GetHeapBuf() { return &m_hb; }
|
||||
const WDL_HeapBuf *GetHeapBuf() const { return &m_hb; }
|
||||
|
||||
|
||||
private:
|
||||
WDL_HeapBuf m_hb;
|
||||
};
|
||||
|
||||
#endif // ! WDL_HEAPBUF_IMPL_ONLY
|
||||
|
||||
#endif // _WDL_HEAPBUF_H_
|
639
Source/WDL/resample.cpp
Normal file
639
Source/WDL/resample.cpp
Normal file
@ -0,0 +1,639 @@
|
||||
/*
|
||||
WDL - resample.cpp
|
||||
Copyright (C) 2010 and later Cockos Incorporated
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
You may also distribute this software under the LGPL v2 or later.
|
||||
|
||||
*/
|
||||
|
||||
#include "resample.h"
|
||||
#include <math.h>
|
||||
|
||||
#include "denormal.h"
|
||||
|
||||
#ifndef PI
|
||||
#define PI 3.1415926535897932384626433832795
|
||||
#endif
|
||||
|
||||
class WDL_Resampler::WDL_Resampler_IIRFilter
|
||||
{
|
||||
public:
|
||||
WDL_Resampler_IIRFilter()
|
||||
{
|
||||
m_fpos=-1;
|
||||
Reset();
|
||||
}
|
||||
~WDL_Resampler_IIRFilter()
|
||||
{
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
memset(m_hist,0,sizeof(m_hist));
|
||||
}
|
||||
|
||||
void setParms(double fpos, double Q)
|
||||
{
|
||||
if (fabs(fpos-m_fpos)<0.000001) return;
|
||||
m_fpos=fpos;
|
||||
|
||||
double pos = fpos * PI;
|
||||
double cpos=cos(pos);
|
||||
double spos=sin(pos);
|
||||
|
||||
double alpha=spos/(2.0*Q);
|
||||
|
||||
double sc=1.0/( 1 + alpha);
|
||||
m_b1 = (1-cpos) * sc;
|
||||
m_b2 = m_b0 = m_b1*0.5;
|
||||
m_a1 = -2 * cpos * sc;
|
||||
m_a2 = (1-alpha)*sc;
|
||||
|
||||
}
|
||||
|
||||
void Apply(WDL_ResampleSample *in1, WDL_ResampleSample *out1, int ns, int span, int w)
|
||||
{
|
||||
double b0=m_b0,b1=m_b1,b2=m_b2,a1=m_a1,a2=m_a2;
|
||||
double *hist=m_hist[w];
|
||||
while (ns--)
|
||||
{
|
||||
double in=*in1;
|
||||
in1+=span;
|
||||
double out = (double) ( in*b0 + hist[0]*b1 + hist[1]*b2 - hist[2]*a1 - hist[3]*a2);
|
||||
hist[1]=hist[0]; hist[0]=in;
|
||||
hist[3]=hist[2]; *out1 = hist[2]=denormal_filter_double(out);
|
||||
|
||||
out1+=span;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
double m_fpos;
|
||||
double m_a1,m_a2;
|
||||
double m_b0,m_b1,m_b2;
|
||||
double m_hist[WDL_RESAMPLE_MAX_FILTERS*WDL_RESAMPLE_MAX_NCH][4];
|
||||
};
|
||||
|
||||
|
||||
void inline WDL_Resampler::SincSample(WDL_ResampleSample *outptr, const WDL_ResampleSample *inptr, double fracpos, int nch, const WDL_SincFilterSample *filter, int filtsz)
|
||||
{
|
||||
const int oversize=m_lp_oversize;
|
||||
|
||||
fracpos *= oversize;
|
||||
const int ifpos=(int)fracpos;
|
||||
filter += (oversize-ifpos) * filtsz;
|
||||
fracpos -= ifpos;
|
||||
|
||||
int x;
|
||||
for (x = 0; x < nch; x ++)
|
||||
{
|
||||
double sum=0.0,sum2=0.0;
|
||||
const WDL_SincFilterSample *fptr2=filter;
|
||||
const WDL_SincFilterSample *fptr=fptr2 - filtsz;
|
||||
const WDL_ResampleSample *iptr=inptr+x;
|
||||
int i=filtsz/2;
|
||||
while (i--)
|
||||
{
|
||||
sum += fptr[0]*iptr[0];
|
||||
sum2 += fptr2[0]*iptr[0];
|
||||
sum += fptr[1]*iptr[nch];
|
||||
sum2 += fptr2[1]*iptr[nch];
|
||||
iptr+=nch*2;
|
||||
fptr+=2;
|
||||
fptr2+=2;
|
||||
}
|
||||
outptr[x]=sum*fracpos + sum2*(1.0-fracpos);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void inline WDL_Resampler::SincSample1(WDL_ResampleSample *outptr, const WDL_ResampleSample *inptr, double fracpos, const WDL_SincFilterSample *filter, int filtsz)
|
||||
{
|
||||
const int oversize=m_lp_oversize;
|
||||
fracpos *= oversize;
|
||||
const int ifpos=(int)fracpos;
|
||||
fracpos -= ifpos;
|
||||
|
||||
double sum=0.0,sum2=0.0;
|
||||
const WDL_SincFilterSample *fptr2=filter + (oversize-ifpos) * filtsz;
|
||||
const WDL_SincFilterSample *fptr=fptr2 - filtsz;
|
||||
const WDL_ResampleSample *iptr=inptr;
|
||||
int i=filtsz/2;
|
||||
while (i--)
|
||||
{
|
||||
sum += fptr[0]*iptr[0];
|
||||
sum2 += fptr2[0]*iptr[0];
|
||||
sum += fptr[1]*iptr[1];
|
||||
sum2 += fptr2[1]*iptr[1];
|
||||
iptr+=2;
|
||||
fptr+=2;
|
||||
fptr2+=2;
|
||||
}
|
||||
outptr[0]=sum*fracpos+sum2*(1.0-fracpos);
|
||||
}
|
||||
|
||||
void inline WDL_Resampler::SincSample2(WDL_ResampleSample *outptr, const WDL_ResampleSample *inptr, double fracpos, const WDL_SincFilterSample *filter, int filtsz)
|
||||
{
|
||||
const int oversize=m_lp_oversize;
|
||||
fracpos *= oversize;
|
||||
const int ifpos=(int)fracpos;
|
||||
fracpos -= ifpos;
|
||||
|
||||
const WDL_SincFilterSample *fptr2=filter + (oversize-ifpos) * filtsz;
|
||||
const WDL_SincFilterSample *fptr=fptr2 - filtsz;
|
||||
|
||||
double sum=0.0;
|
||||
double sum2=0.0;
|
||||
double sumb=0.0;
|
||||
double sum2b=0.0;
|
||||
const WDL_ResampleSample *iptr=inptr;
|
||||
int i=filtsz/2;
|
||||
while (i--)
|
||||
{
|
||||
sum += fptr[0]*iptr[0];
|
||||
sum2 += fptr[0]*iptr[1];
|
||||
sumb += fptr2[0]*iptr[0];
|
||||
sum2b += fptr2[0]*iptr[1];
|
||||
sum += fptr[1]*iptr[2];
|
||||
sum2 += fptr[1]*iptr[3];
|
||||
sumb += fptr2[1]*iptr[2];
|
||||
sum2b += fptr2[1]*iptr[3];
|
||||
iptr+=4;
|
||||
fptr+=2;
|
||||
fptr2+=2;
|
||||
}
|
||||
outptr[0]=sum*fracpos + sumb*(1.0-fracpos);
|
||||
outptr[1]=sum2*fracpos + sum2b*(1.0-fracpos);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
WDL_Resampler::WDL_Resampler()
|
||||
{
|
||||
m_filterq=0.707f;
|
||||
m_filterpos=0.693f; // .792 ?
|
||||
|
||||
m_sincoversize=0;
|
||||
m_lp_oversize=1;
|
||||
m_sincsize=0;
|
||||
m_filtercnt=1;
|
||||
m_interp=true;
|
||||
m_feedmode=false;
|
||||
|
||||
m_filter_coeffs_size=0;
|
||||
m_sratein=44100.0;
|
||||
m_srateout=44100.0;
|
||||
m_ratio=1.0;
|
||||
m_filter_ratio=-1.0;
|
||||
m_iirfilter=0;
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
WDL_Resampler::~WDL_Resampler()
|
||||
{
|
||||
delete m_iirfilter;
|
||||
}
|
||||
|
||||
void WDL_Resampler::Reset(double fracpos)
|
||||
{
|
||||
m_last_requested=0;
|
||||
m_filtlatency=0;
|
||||
m_fracpos=fracpos;
|
||||
m_samples_in_rsinbuf=0;
|
||||
if (m_iirfilter) m_iirfilter->Reset();
|
||||
}
|
||||
|
||||
void WDL_Resampler::SetMode(bool interp, int filtercnt, bool sinc, int sinc_size, int sinc_interpsize)
|
||||
{
|
||||
m_sincsize = sinc && sinc_size>= 4 ? sinc_size > 8192 ? 8192 : (sinc_size&~1) : 0;
|
||||
m_sincoversize = m_sincsize ? (sinc_interpsize<= 1 ? 1 : sinc_interpsize>=8192 ? 8192 : sinc_interpsize) : 1;
|
||||
|
||||
m_filtercnt = m_sincsize ? 0 : (filtercnt<=0?0 : filtercnt >= WDL_RESAMPLE_MAX_FILTERS ? WDL_RESAMPLE_MAX_FILTERS : filtercnt);
|
||||
m_interp=interp && !m_sincsize;
|
||||
// char buf[512];
|
||||
// sprintf(buf,"setting interp=%d, filtercnt=%d, sinc=%d,%d\n",m_interp,m_filtercnt,m_sincsize,m_sincoversize);
|
||||
// OutputDebugString(buf);
|
||||
|
||||
if (!m_sincsize)
|
||||
{
|
||||
m_filter_coeffs.Resize(0);
|
||||
m_filter_coeffs_size=0;
|
||||
}
|
||||
if (!m_filtercnt)
|
||||
{
|
||||
delete m_iirfilter;
|
||||
m_iirfilter=0;
|
||||
}
|
||||
}
|
||||
|
||||
void WDL_Resampler::SetRates(double rate_in, double rate_out)
|
||||
{
|
||||
if (rate_in<1.0) rate_in=1.0;
|
||||
if (rate_out<1.0) rate_out=1.0;
|
||||
if (rate_in != m_sratein || rate_out != m_srateout)
|
||||
{
|
||||
m_sratein=rate_in;
|
||||
m_srateout=rate_out;
|
||||
m_ratio=m_sratein / m_srateout;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WDL_Resampler::BuildLowPass(double filtpos) // only called in sinc modes
|
||||
{
|
||||
const int wantsize=m_sincsize;
|
||||
const int wantinterp=m_sincoversize;
|
||||
|
||||
if (m_filter_ratio!=filtpos ||
|
||||
m_filter_coeffs_size != wantsize ||
|
||||
m_lp_oversize != wantinterp)
|
||||
{
|
||||
m_lp_oversize = wantinterp;
|
||||
m_filter_ratio=filtpos;
|
||||
|
||||
// build lowpass filter
|
||||
const int allocsize = wantsize*(m_lp_oversize+1);
|
||||
WDL_SincFilterSample *cfout=m_filter_coeffs.Resize(allocsize);
|
||||
if (m_filter_coeffs.GetSize()==allocsize)
|
||||
{
|
||||
m_filter_coeffs_size=wantsize;
|
||||
|
||||
const double dwindowpos = 2.0 * PI/(double)wantsize;
|
||||
const double dsincpos = PI * filtpos; // filtpos is outrate/inrate, i.e. 0.5 is going to half rate
|
||||
const int hwantsize=wantsize/2;
|
||||
|
||||
double filtpower=0.0;
|
||||
WDL_SincFilterSample *ptrout = cfout;
|
||||
int slice;
|
||||
for (slice=0;slice<=wantinterp;slice++)
|
||||
{
|
||||
const double frac = slice / (double)wantinterp;
|
||||
const int center_x = slice == 0 ? hwantsize : slice == wantinterp ? hwantsize-1 : -1;
|
||||
|
||||
int x;
|
||||
for (x=0;x<wantsize;x++)
|
||||
{
|
||||
if (x==center_x)
|
||||
{
|
||||
// we know this will be 1.0
|
||||
*ptrout++ = 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
const double xfrac = frac + x;
|
||||
const double windowpos = dwindowpos * xfrac;
|
||||
const double sincpos = dsincpos * (xfrac - hwantsize);
|
||||
|
||||
// blackman-harris * sinc
|
||||
const double val = (0.35875 - 0.48829 * cos(windowpos) + 0.14128 * cos(2*windowpos) - 0.01168 * cos(3*windowpos)) * sin(sincpos) / sincpos;
|
||||
if (slice<wantinterp) filtpower+=val;
|
||||
*ptrout++ = (WDL_SincFilterSample)val;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
filtpower = wantinterp/(filtpower+1.0);
|
||||
int x;
|
||||
for (x = 0; x < allocsize; x ++)
|
||||
{
|
||||
cfout[x] = (WDL_SincFilterSample) (cfout[x]*filtpower);
|
||||
}
|
||||
}
|
||||
else m_filter_coeffs_size=0;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
double WDL_Resampler::GetCurrentLatency()
|
||||
{
|
||||
double v=((double)m_samples_in_rsinbuf-m_filtlatency)/m_sratein;
|
||||
|
||||
if (v<0.0)v=0.0;
|
||||
return v;
|
||||
}
|
||||
|
||||
int WDL_Resampler::ResamplePrepare(int out_samples, int nch, WDL_ResampleSample **inbuffer)
|
||||
{
|
||||
if (nch > WDL_RESAMPLE_MAX_NCH || nch < 1) return 0;
|
||||
|
||||
int fsize=0;
|
||||
if (m_sincsize>1) fsize = m_sincsize;
|
||||
|
||||
int hfs=fsize/2;
|
||||
if (hfs>1 && m_samples_in_rsinbuf<hfs-1)
|
||||
{
|
||||
m_filtlatency+=hfs-1 - m_samples_in_rsinbuf;
|
||||
|
||||
m_samples_in_rsinbuf=hfs-1;
|
||||
|
||||
if (m_samples_in_rsinbuf>0)
|
||||
{
|
||||
WDL_ResampleSample *p = m_rsinbuf.Resize(m_samples_in_rsinbuf*nch,false);
|
||||
memset(p,0,sizeof(WDL_ResampleSample)*m_rsinbuf.GetSize());
|
||||
}
|
||||
}
|
||||
|
||||
int sreq = 0;
|
||||
|
||||
if (!m_feedmode) sreq = (int)(m_ratio * out_samples) + 4 + fsize - m_samples_in_rsinbuf;
|
||||
else sreq = out_samples;
|
||||
|
||||
if (sreq<0)sreq=0;
|
||||
|
||||
again:
|
||||
m_rsinbuf.Resize((m_samples_in_rsinbuf+sreq)*nch,false);
|
||||
|
||||
int sz = m_rsinbuf.GetSize()/(nch?nch:1) - m_samples_in_rsinbuf;
|
||||
if (sz!=sreq)
|
||||
{
|
||||
if (sreq>4 && !sz)
|
||||
{
|
||||
sreq/=2;
|
||||
goto again; // try again with half the size
|
||||
}
|
||||
// todo: notify of error?
|
||||
sreq=sz;
|
||||
}
|
||||
|
||||
*inbuffer = m_rsinbuf.Get() + m_samples_in_rsinbuf*nch;
|
||||
|
||||
m_last_requested=sreq;
|
||||
return sreq;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int WDL_Resampler::ResampleOut(WDL_ResampleSample *out, int nsamples_in, int nsamples_out, int nch)
|
||||
{
|
||||
if (nch > WDL_RESAMPLE_MAX_NCH || nch < 1) return 0;
|
||||
|
||||
if (m_filtercnt>0)
|
||||
{
|
||||
if (m_ratio > 1.0 && nsamples_in > 0) // filter input
|
||||
{
|
||||
if (!m_iirfilter) m_iirfilter = new WDL_Resampler_IIRFilter;
|
||||
|
||||
int n=m_filtercnt;
|
||||
m_iirfilter->setParms((1.0/m_ratio)*m_filterpos,m_filterq);
|
||||
|
||||
WDL_ResampleSample *buf=(WDL_ResampleSample *)m_rsinbuf.Get() + m_samples_in_rsinbuf*nch;
|
||||
int a,x;
|
||||
int offs=0;
|
||||
for (x=0; x < nch; x ++)
|
||||
for (a = 0; a < n; a ++)
|
||||
m_iirfilter->Apply(buf+x,buf+x,nsamples_in,nch,offs++);
|
||||
}
|
||||
}
|
||||
|
||||
// prevent the caller from corrupting the internal state
|
||||
m_samples_in_rsinbuf += nsamples_in < m_last_requested ? nsamples_in : m_last_requested;
|
||||
|
||||
int rsinbuf_availtemp = m_samples_in_rsinbuf;
|
||||
|
||||
if (nsamples_in < m_last_requested) // flush out to ensure we can deliver
|
||||
{
|
||||
int fsize=(m_last_requested-nsamples_in)*2 + m_sincsize*2;
|
||||
|
||||
int alloc_size=(m_samples_in_rsinbuf+fsize)*nch;
|
||||
WDL_ResampleSample *zb=m_rsinbuf.Resize(alloc_size,false);
|
||||
if (m_rsinbuf.GetSize()==alloc_size)
|
||||
{
|
||||
memset(zb+m_samples_in_rsinbuf*nch,0,fsize*nch*sizeof(WDL_ResampleSample));
|
||||
rsinbuf_availtemp = m_samples_in_rsinbuf+fsize;
|
||||
}
|
||||
}
|
||||
|
||||
int ret=0;
|
||||
double srcpos=m_fracpos;
|
||||
double drspos = m_ratio;
|
||||
WDL_ResampleSample *localin = m_rsinbuf.Get();
|
||||
|
||||
WDL_ResampleSample *outptr=out;
|
||||
|
||||
int ns=nsamples_out;
|
||||
|
||||
int outlatadj=0;
|
||||
|
||||
if (m_sincsize) // sinc interpolating
|
||||
{
|
||||
if (m_ratio > 1.0) BuildLowPass(1.0 / (m_ratio*1.03));
|
||||
else BuildLowPass(1.0);
|
||||
|
||||
int filtsz=m_filter_coeffs_size;
|
||||
int filtlen = rsinbuf_availtemp - filtsz;
|
||||
outlatadj=filtsz/2-1;
|
||||
WDL_SincFilterSample *filter=m_filter_coeffs.Get();
|
||||
|
||||
if (nch == 1)
|
||||
{
|
||||
while (ns--)
|
||||
{
|
||||
int ipos = (int)srcpos;
|
||||
|
||||
if (ipos >= filtlen-1) break; // quit decoding, not enough input samples
|
||||
|
||||
SincSample1(outptr,localin + ipos,srcpos-ipos,filter,filtsz);
|
||||
outptr ++;
|
||||
srcpos+=drspos;
|
||||
ret++;
|
||||
}
|
||||
}
|
||||
else if (nch==2)
|
||||
{
|
||||
while (ns--)
|
||||
{
|
||||
int ipos = (int)srcpos;
|
||||
|
||||
if (ipos >= filtlen-1) break; // quit decoding, not enough input samples
|
||||
|
||||
SincSample2(outptr,localin + ipos*2,srcpos-ipos,filter,filtsz);
|
||||
outptr+=2;
|
||||
srcpos+=drspos;
|
||||
ret++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (ns--)
|
||||
{
|
||||
int ipos = (int)srcpos;
|
||||
|
||||
if (ipos >= filtlen-1) break; // quit decoding, not enough input samples
|
||||
|
||||
SincSample(outptr,localin + ipos*nch,srcpos-ipos,nch,filter,filtsz);
|
||||
outptr += nch;
|
||||
srcpos+=drspos;
|
||||
ret++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!m_interp) // point sampling
|
||||
{
|
||||
if (nch == 1)
|
||||
{
|
||||
while (ns--)
|
||||
{
|
||||
int ipos = (int)srcpos;
|
||||
if (ipos >= rsinbuf_availtemp) break; // quit decoding, not enough input samples
|
||||
|
||||
*outptr++ = localin[ipos];
|
||||
srcpos+=drspos;
|
||||
ret++;
|
||||
}
|
||||
}
|
||||
else if (nch == 2)
|
||||
{
|
||||
while (ns--)
|
||||
{
|
||||
int ipos = (int)srcpos;
|
||||
if (ipos >= rsinbuf_availtemp) break; // quit decoding, not enough input samples
|
||||
|
||||
ipos+=ipos;
|
||||
|
||||
outptr[0] = localin[ipos];
|
||||
outptr[1] = localin[ipos+1];
|
||||
outptr+=2;
|
||||
srcpos+=drspos;
|
||||
ret++;
|
||||
}
|
||||
}
|
||||
else
|
||||
while (ns--)
|
||||
{
|
||||
int ipos = (int)srcpos;
|
||||
if (ipos >= rsinbuf_availtemp) break; // quit decoding, not enough input samples
|
||||
|
||||
memcpy(outptr,localin + ipos*nch,nch*sizeof(WDL_ResampleSample));
|
||||
outptr += nch;
|
||||
srcpos+=drspos;
|
||||
ret++;
|
||||
}
|
||||
}
|
||||
else // linear interpolation
|
||||
{
|
||||
if (nch == 1)
|
||||
{
|
||||
while (ns--)
|
||||
{
|
||||
int ipos = (int)srcpos;
|
||||
double fracpos=srcpos-ipos;
|
||||
|
||||
if (ipos >= rsinbuf_availtemp-1)
|
||||
{
|
||||
break; // quit decoding, not enough input samples
|
||||
}
|
||||
|
||||
double ifracpos=1.0-fracpos;
|
||||
WDL_ResampleSample *inptr = localin + ipos;
|
||||
*outptr++ = inptr[0]*(ifracpos) + inptr[1]*(fracpos);
|
||||
srcpos+=drspos;
|
||||
ret++;
|
||||
}
|
||||
}
|
||||
else if (nch == 2)
|
||||
{
|
||||
while (ns--)
|
||||
{
|
||||
int ipos = (int)srcpos;
|
||||
double fracpos=srcpos-ipos;
|
||||
|
||||
if (ipos >= rsinbuf_availtemp-1)
|
||||
{
|
||||
break; // quit decoding, not enough input samples
|
||||
}
|
||||
|
||||
double ifracpos=1.0-fracpos;
|
||||
WDL_ResampleSample *inptr = localin + ipos*2;
|
||||
outptr[0] = inptr[0]*(ifracpos) + inptr[2]*(fracpos);
|
||||
outptr[1] = inptr[1]*(ifracpos) + inptr[3]*(fracpos);
|
||||
outptr += 2;
|
||||
srcpos+=drspos;
|
||||
ret++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (ns--)
|
||||
{
|
||||
int ipos = (int)srcpos;
|
||||
double fracpos=srcpos-ipos;
|
||||
|
||||
if (ipos >= rsinbuf_availtemp-1)
|
||||
{
|
||||
break; // quit decoding, not enough input samples
|
||||
}
|
||||
|
||||
double ifracpos=1.0-fracpos;
|
||||
int ch=nch;
|
||||
WDL_ResampleSample *inptr = localin + ipos*nch;
|
||||
while (ch--)
|
||||
{
|
||||
*outptr++ = inptr[0]*(ifracpos) + inptr[nch]*(fracpos);
|
||||
inptr++;
|
||||
}
|
||||
srcpos+=drspos;
|
||||
ret++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (m_filtercnt>0)
|
||||
{
|
||||
if (m_ratio < 1.0 && ret>0) // filter output
|
||||
{
|
||||
if (!m_iirfilter) m_iirfilter = new WDL_Resampler_IIRFilter;
|
||||
int n=m_filtercnt;
|
||||
m_iirfilter->setParms(m_ratio*m_filterpos,m_filterq);
|
||||
|
||||
int x,a;
|
||||
int offs=0;
|
||||
for (x=0; x < nch; x ++)
|
||||
for (a = 0; a < n; a ++)
|
||||
m_iirfilter->Apply(out+x,out+x,ret,nch,offs++);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (ret>0 && rsinbuf_availtemp>m_samples_in_rsinbuf) // we had to pad!!
|
||||
{
|
||||
// check for the case where rsinbuf_availtemp>m_samples_in_rsinbuf, decrease ret down to actual valid samples
|
||||
double adj=(srcpos-m_samples_in_rsinbuf + outlatadj) / drspos;
|
||||
if (adj>0)
|
||||
{
|
||||
ret -= (int) (adj + 0.5);
|
||||
if (ret<0)ret=0;
|
||||
}
|
||||
}
|
||||
|
||||
int isrcpos=(int)srcpos;
|
||||
if (isrcpos > m_samples_in_rsinbuf) isrcpos=m_samples_in_rsinbuf;
|
||||
m_fracpos = srcpos - isrcpos;
|
||||
m_samples_in_rsinbuf -= isrcpos;
|
||||
if (m_samples_in_rsinbuf <= 0) m_samples_in_rsinbuf=0;
|
||||
else
|
||||
memcpy(localin, localin + isrcpos*nch,m_samples_in_rsinbuf*sizeof(WDL_ResampleSample)*nch);
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
120
Source/WDL/resample.h
Normal file
120
Source/WDL/resample.h
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
WDL - resample.h
|
||||
Copyright (C) 2010 and later Cockos Incorporated
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
You may also distribute this software under the LGPL v2 or later.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _WDL_RESAMPLE_H_
|
||||
#define _WDL_RESAMPLE_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "wdltypes.h"
|
||||
#include "heapbuf.h"
|
||||
|
||||
// default to floats for sinc filter ceofficients
|
||||
#ifdef WDL_RESAMPLE_FULL_SINC_PRECISION
|
||||
typedef double WDL_SincFilterSample;
|
||||
#else
|
||||
typedef float WDL_SincFilterSample;
|
||||
#endif
|
||||
|
||||
// default to doubles for audio samples
|
||||
#ifdef WDL_RESAMPLE_TYPE
|
||||
typedef WDL_RESAMPLE_TYPE WDL_ResampleSample;
|
||||
#else
|
||||
typedef double WDL_ResampleSample;
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef WDL_RESAMPLE_MAX_FILTERS
|
||||
#define WDL_RESAMPLE_MAX_FILTERS 4
|
||||
#endif
|
||||
|
||||
#ifndef WDL_RESAMPLE_MAX_NCH
|
||||
#define WDL_RESAMPLE_MAX_NCH 64
|
||||
#endif
|
||||
|
||||
|
||||
class WDL_Resampler
|
||||
{
|
||||
public:
|
||||
WDL_Resampler();
|
||||
~WDL_Resampler();
|
||||
// if sinc set, it overrides interp or filtercnt
|
||||
void SetMode(bool interp, int filtercnt, bool sinc, int sinc_size=64, int sinc_interpsize=32);
|
||||
|
||||
void SetFilterParms(float filterpos=0.693, float filterq=0.707) { m_filterpos=filterpos; m_filterq=filterq; } // used for filtercnt>0 but not sinc
|
||||
void SetFeedMode(bool wantInputDriven) { m_feedmode=wantInputDriven; } // if true, that means the first parameter to ResamplePrepare will specify however much input you have, not how much you want
|
||||
|
||||
void Reset(double fracpos=0.0);
|
||||
void SetRates(double rate_in, double rate_out);
|
||||
|
||||
double GetCurrentLatency(); // amount of input that has been received but not yet converted to output, in seconds
|
||||
|
||||
// req_samples is output samples desired if !wantInputDriven, or if wantInputDriven is input samples that we have
|
||||
// returns number of samples desired (put these into *inbuffer)
|
||||
// note that it is safe to call ResamplePrepare without calling ResampleOut (the next call of ResamplePrepare will function as normal)
|
||||
int ResamplePrepare(int req_samples, int nch, WDL_ResampleSample **inbuffer);
|
||||
|
||||
|
||||
// if numsamples_in < the value return by ResamplePrepare(), then it will be flushed to produce all remaining valid samples
|
||||
// do NOT call with nsamples_in greater than the value returned from resamplerprpare()! the extra samples will be ignored.
|
||||
// returns number of samples successfully outputted to out
|
||||
int ResampleOut(WDL_ResampleSample *out, int nsamples_in, int nsamples_out, int nch);
|
||||
|
||||
|
||||
|
||||
private:
|
||||
void BuildLowPass(double filtpos);
|
||||
void inline SincSample(WDL_ResampleSample *outptr, const WDL_ResampleSample *inptr, double fracpos, int nch, const WDL_SincFilterSample *filter, int filtsz);
|
||||
void inline SincSample1(WDL_ResampleSample *outptr, const WDL_ResampleSample *inptr, double fracpos, const WDL_SincFilterSample *filter, int filtsz);
|
||||
void inline SincSample2(WDL_ResampleSample *outptr, const WDL_ResampleSample *inptr, double fracpos, const WDL_SincFilterSample *filter, int filtsz);
|
||||
|
||||
double m_sratein WDL_FIXALIGN;
|
||||
double m_srateout;
|
||||
double m_fracpos;
|
||||
double m_ratio;
|
||||
double m_filter_ratio;
|
||||
float m_filterq, m_filterpos;
|
||||
WDL_TypedBuf<WDL_ResampleSample> m_rsinbuf;
|
||||
WDL_TypedBuf<WDL_SincFilterSample> m_filter_coeffs;
|
||||
|
||||
class WDL_Resampler_IIRFilter;
|
||||
WDL_Resampler_IIRFilter *m_iirfilter;
|
||||
|
||||
int m_filter_coeffs_size;
|
||||
int m_last_requested;
|
||||
int m_filtlatency;
|
||||
int m_samples_in_rsinbuf;
|
||||
int m_lp_oversize;
|
||||
|
||||
int m_sincsize;
|
||||
int m_filtercnt;
|
||||
int m_sincoversize;
|
||||
bool m_interp;
|
||||
bool m_feedmode;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
160
Source/WDL/wdltypes.h
Normal file
160
Source/WDL/wdltypes.h
Normal file
@ -0,0 +1,160 @@
|
||||
#ifndef _WDLTYPES_
|
||||
#define _WDLTYPES_
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
||||
typedef __int64 WDL_INT64;
|
||||
typedef unsigned __int64 WDL_UINT64;
|
||||
|
||||
#else
|
||||
|
||||
typedef long long WDL_INT64;
|
||||
typedef unsigned long long WDL_UINT64;
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define WDL_UINT64_CONST(x) (x##ui64)
|
||||
#define WDL_INT64_CONST(x) (x##i64)
|
||||
#else
|
||||
#define WDL_UINT64_CONST(x) (x##ULL)
|
||||
#define WDL_INT64_CONST(x) (x##LL)
|
||||
#endif
|
||||
|
||||
|
||||
#if !defined(_MSC_VER) || _MSC_VER > 1200
|
||||
#define WDL_DLGRET INT_PTR CALLBACK
|
||||
#else
|
||||
#define WDL_DLGRET BOOL CALLBACK
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <stdint.h>
|
||||
typedef intptr_t INT_PTR;
|
||||
typedef uintptr_t UINT_PTR;
|
||||
#endif
|
||||
|
||||
#if defined(__ppc__) || !defined(__cplusplus)
|
||||
typedef char WDL_bool;
|
||||
#else
|
||||
typedef bool WDL_bool;
|
||||
#endif
|
||||
|
||||
#ifndef GWLP_USERDATA
|
||||
#define GWLP_USERDATA GWL_USERDATA
|
||||
#define GWLP_WNDPROC GWL_WNDPROC
|
||||
#define GWLP_HINSTANCE GWL_HINSTANCE
|
||||
#define GWLP_HWNDPARENT GWL_HWNDPARENT
|
||||
#define DWLP_USER DWL_USER
|
||||
#define DWLP_DLGPROC DWL_DLGPROC
|
||||
#define DWLP_MSGRESULT DWL_MSGRESULT
|
||||
#define SetWindowLongPtr(a,b,c) SetWindowLong(a,b,c)
|
||||
#define GetWindowLongPtr(a,b) GetWindowLong(a,b)
|
||||
#define SetWindowLongPtrW(a,b,c) SetWindowLongW(a,b,c)
|
||||
#define GetWindowLongPtrW(a,b) GetWindowLongW(a,b)
|
||||
#define SetWindowLongPtrA(a,b,c) SetWindowLongA(a,b,c)
|
||||
#define GetWindowLongPtrA(a,b) GetWindowLongA(a,b)
|
||||
|
||||
#define GCLP_WNDPROC GCL_WNDPROC
|
||||
#define GCLP_HICON GCL_HICON
|
||||
#define GCLP_HICONSM GCL_HICONSM
|
||||
#define SetClassLongPtr(a,b,c) SetClassLong(a,b,c)
|
||||
#define GetClassLongPtr(a,b) GetClassLong(a,b)
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __GNUC__
|
||||
// for structures that contain doubles, or doubles in structures that are after stuff of questionable alignment (for OSX/linux)
|
||||
#define WDL_FIXALIGN __attribute__ ((aligned (8)))
|
||||
// usage: void func(int a, const char *fmt, ...) WDL_VARARG_WARN(printf,2,3); // note: if member function, this pointer is counted as well, so as member function that would be 3,4
|
||||
#define WDL_VARARG_WARN(x,n,s) __attribute__ ((format (x,n,s)))
|
||||
#define WDL_STATICFUNC_UNUSED __attribute__((unused))
|
||||
|
||||
#else
|
||||
#define WDL_FIXALIGN
|
||||
#define WDL_VARARG_WARN(x,n,s)
|
||||
#define WDL_STATICFUNC_UNUSED
|
||||
#endif
|
||||
|
||||
#ifndef WDL_WANT_NEW_EXCEPTIONS
|
||||
#if defined(__cplusplus)
|
||||
#include <new>
|
||||
#define WDL_NEW (std::nothrow)
|
||||
#endif
|
||||
#else
|
||||
#define WDL_NEW
|
||||
#endif
|
||||
|
||||
|
||||
#if !defined(max) && defined(WDL_DEFINE_MINMAX)
|
||||
#define max(x,y) ((x)<(y)?(y):(x))
|
||||
#define min(x,y) ((x)<(y)?(x):(y))
|
||||
#endif
|
||||
|
||||
#ifndef wdl_max
|
||||
#define wdl_max(x,y) ((x)<(y)?(y):(x))
|
||||
#define wdl_min(x,y) ((x)<(y)?(x):(y))
|
||||
#define wdl_abs(x) ((x)<0 ? -(x) : (x))
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
#ifndef strnicmp
|
||||
#define strnicmp(x,y,z) strncasecmp(x,y,z)
|
||||
#endif
|
||||
#ifndef stricmp
|
||||
#define stricmp(x,y) strcasecmp(x,y)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef WDL_BACKSLASHES_ARE_ORDINARY
|
||||
#define WDL_IS_DIRCHAR(x) ((x) == '/')
|
||||
#else
|
||||
// for multi-platform applications it seems better to treat backslashes as directory separators even if it
|
||||
// isn't supported by the underying system (for resolving filenames, etc)
|
||||
#ifdef _WIN32
|
||||
#define WDL_IS_DIRCHAR(x) ((x) == '\\' || (x) == '/')
|
||||
#else
|
||||
#define WDL_IS_DIRCHAR(x) ((x) == '/' || (x) == '\\')
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && !defined(WDL_BACKSLASHES_ARE_ORDINARY)
|
||||
#define WDL_DIRCHAR '\\'
|
||||
#define WDL_DIRCHAR_STR "\\"
|
||||
#else
|
||||
#define WDL_DIRCHAR '/'
|
||||
#define WDL_DIRCHAR_STR "/"
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) || defined(__APPLE__)
|
||||
// on __APPLE__ we should ideally check the filesystem for case-sensitivity, assuming a case-insensitive-only match
|
||||
#define wdl_filename_cmp(x,y) stricmp(x,y)
|
||||
#define wdl_filename_cmpn(x,y,n) strnicmp(x,y,n)
|
||||
#else
|
||||
#define wdl_filename_cmp(x,y) strcmp(x,y)
|
||||
#define wdl_filename_cmpn(x,y,n) strncmp(x,y,n)
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) || defined(__INTEL_COMPILER)
|
||||
#define WDL_likely(x) __builtin_expect(!!(x),1)
|
||||
#define WDL_unlikely(x) __builtin_expect(!!(x),0)
|
||||
#else
|
||||
#define WDL_likely(x) (!!(x))
|
||||
#define WDL_unlikely(x) (!!(x))
|
||||
#endif
|
||||
|
||||
#if defined(_DEBUG) || defined(DEBUG)
|
||||
#include <assert.h>
|
||||
#define WDL_ASSERT(x) assert(x)
|
||||
#define WDL_NORMALLY(x) (assert(x),1)
|
||||
#define WDL_NOT_NORMALLY(x) (assert(!(x)),0)
|
||||
#else
|
||||
#define WDL_ASSERT(x)
|
||||
#define WDL_NORMALLY(x) WDL_likely(x)
|
||||
#define WDL_NOT_NORMALLY(x) WDL_unlikely(x)
|
||||
#endif
|
||||
|
||||
#endif
|
322
Source/ps3_BufferingAudioSource.cpp
Normal file
322
Source/ps3_BufferingAudioSource.cpp
Normal file
@ -0,0 +1,322 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "ps3_BufferingAudioSource.h"
|
||||
|
||||
MyBufferingAudioSource::MyBufferingAudioSource(PositionableAudioSource* s,
|
||||
TimeSliceThread& thread,
|
||||
const bool deleteSourceWhenDeleted,
|
||||
const int bufferSizeSamples,
|
||||
const int numChannels,
|
||||
bool prefillBufferOnPrepareToPlay)
|
||||
: source (s, deleteSourceWhenDeleted),
|
||||
backgroundThread (thread),
|
||||
numberOfSamplesToBuffer (jmax (1024, bufferSizeSamples)),
|
||||
numberOfChannels (numChannels),
|
||||
bufferValidStart (0),
|
||||
bufferValidEnd (0),
|
||||
nextPlayPos (0),
|
||||
sampleRate (0),
|
||||
wasSourceLooping (false),
|
||||
isPrepared (false),
|
||||
prefillBuffer (prefillBufferOnPrepareToPlay)
|
||||
{
|
||||
jassert (source != nullptr);
|
||||
|
||||
jassert (numberOfSamplesToBuffer > 1024); // not much point using this class if you're
|
||||
// not using a larger buffer..
|
||||
}
|
||||
|
||||
MyBufferingAudioSource::~MyBufferingAudioSource()
|
||||
{
|
||||
releaseResources();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void MyBufferingAudioSource::prepareToPlay (int samplesPerBlockExpected, double newSampleRate)
|
||||
{
|
||||
const int bufferSizeNeeded = jmax (samplesPerBlockExpected * 2, numberOfSamplesToBuffer);
|
||||
|
||||
if (newSampleRate != sampleRate
|
||||
|| bufferSizeNeeded != buffer.getNumSamples()
|
||||
|| ! isPrepared)
|
||||
{
|
||||
backgroundThread.removeTimeSliceClient (this);
|
||||
|
||||
isPrepared = true;
|
||||
sampleRate = newSampleRate;
|
||||
|
||||
source->prepareToPlay (samplesPerBlockExpected, newSampleRate);
|
||||
|
||||
buffer.setSize (numberOfChannels, bufferSizeNeeded);
|
||||
buffer.clear();
|
||||
|
||||
bufferValidStart = 0;
|
||||
bufferValidEnd = 0;
|
||||
|
||||
backgroundThread.addTimeSliceClient (this);
|
||||
|
||||
do
|
||||
{
|
||||
backgroundThread.moveToFrontOfQueue (this);
|
||||
Thread::sleep (5);
|
||||
}
|
||||
while (prefillBuffer
|
||||
&& (bufferValidEnd - bufferValidStart < jmin (((int) newSampleRate) / 4, buffer.getNumSamples() / 2)));
|
||||
}
|
||||
}
|
||||
|
||||
void MyBufferingAudioSource::releaseResources()
|
||||
{
|
||||
isPrepared = false;
|
||||
backgroundThread.removeTimeSliceClient (this);
|
||||
|
||||
buffer.setSize (numberOfChannels, 0);
|
||||
|
||||
// MSVC2015 seems to need this if statement to not generate a warning during linking.
|
||||
// As source is set in the constructor, there is no way that source could
|
||||
// ever equal this, but it seems to make MSVC2015 happy.
|
||||
if (source != this)
|
||||
source->releaseResources();
|
||||
}
|
||||
|
||||
void MyBufferingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info)
|
||||
{
|
||||
const ScopedLock sl (bufferStartPosLock);
|
||||
|
||||
const int validStart = (int) (jlimit (bufferValidStart, bufferValidEnd, nextPlayPos) - nextPlayPos);
|
||||
const int validEnd = (int) (jlimit (bufferValidStart, bufferValidEnd, nextPlayPos + info.numSamples) - nextPlayPos);
|
||||
|
||||
if (validStart == validEnd)
|
||||
{
|
||||
// total cache miss
|
||||
info.clearActiveBufferRegion();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (validStart > 0)
|
||||
info.buffer->clear (info.startSample, validStart); // partial cache miss at start
|
||||
|
||||
if (validEnd < info.numSamples)
|
||||
info.buffer->clear (info.startSample + validEnd,
|
||||
info.numSamples - validEnd); // partial cache miss at end
|
||||
|
||||
if (validStart < validEnd)
|
||||
{
|
||||
for (int chan = jmin (numberOfChannels, info.buffer->getNumChannels()); --chan >= 0;)
|
||||
{
|
||||
jassert (buffer.getNumSamples() > 0);
|
||||
const int startBufferIndex = (int) ((validStart + nextPlayPos) % buffer.getNumSamples());
|
||||
const int endBufferIndex = (int) ((validEnd + nextPlayPos) % buffer.getNumSamples());
|
||||
|
||||
if (startBufferIndex < endBufferIndex)
|
||||
{
|
||||
info.buffer->copyFrom (chan, info.startSample + validStart,
|
||||
buffer,
|
||||
chan, startBufferIndex,
|
||||
validEnd - validStart);
|
||||
}
|
||||
else
|
||||
{
|
||||
const int initialSize = buffer.getNumSamples() - startBufferIndex;
|
||||
|
||||
info.buffer->copyFrom (chan, info.startSample + validStart,
|
||||
buffer,
|
||||
chan, startBufferIndex,
|
||||
initialSize);
|
||||
|
||||
info.buffer->copyFrom (chan, info.startSample + validStart + initialSize,
|
||||
buffer,
|
||||
chan, 0,
|
||||
(validEnd - validStart) - initialSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nextPlayPos += info.numSamples;
|
||||
}
|
||||
}
|
||||
|
||||
bool MyBufferingAudioSource::waitForNextAudioBlockReady (const AudioSourceChannelInfo& info, const uint32 timeout)
|
||||
{
|
||||
if (!source || source->getTotalLength() <= 0)
|
||||
return false;
|
||||
|
||||
if (nextPlayPos + info.numSamples < 0)
|
||||
return true;
|
||||
|
||||
if (! isLooping() && nextPlayPos > getTotalLength())
|
||||
return true;
|
||||
|
||||
uint32 now = Time::getMillisecondCounter();
|
||||
const uint32 startTime = now;
|
||||
|
||||
uint32 elapsed = (now >= startTime ? now - startTime
|
||||
: (std::numeric_limits<uint32>::max() - startTime) + now);
|
||||
|
||||
while (elapsed <= timeout)
|
||||
{
|
||||
{
|
||||
const ScopedLock sl (bufferStartPosLock);
|
||||
|
||||
const int validStart = static_cast<int> (jlimit (bufferValidStart, bufferValidEnd, nextPlayPos) - nextPlayPos);
|
||||
const int validEnd = static_cast<int> (jlimit (bufferValidStart, bufferValidEnd, nextPlayPos + info.numSamples) - nextPlayPos);
|
||||
|
||||
if (validStart <= 0 && validStart < validEnd && validEnd >= info.numSamples)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (elapsed < timeout && (! bufferReadyEvent.wait (static_cast<int> (timeout - elapsed))))
|
||||
return false;
|
||||
|
||||
now = Time::getMillisecondCounter();
|
||||
elapsed = (now >= startTime ? now - startTime
|
||||
: (std::numeric_limits<uint32>::max() - startTime) + now);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
double MyBufferingAudioSource::getPercentReady()
|
||||
{
|
||||
if (bufferValidEnd == bufferValidStart)
|
||||
return 0.0;
|
||||
if (numberOfSamplesToBuffer == 0)
|
||||
return 0.0;
|
||||
return 1.0 / numberOfSamplesToBuffer * (bufferValidEnd - bufferValidStart);
|
||||
}
|
||||
|
||||
int64 MyBufferingAudioSource::getNextReadPosition() const
|
||||
{
|
||||
jassert (source->getTotalLength() > 0);
|
||||
return (source->isLooping() && nextPlayPos > 0)
|
||||
? nextPlayPos % source->getTotalLength()
|
||||
: nextPlayPos;
|
||||
}
|
||||
|
||||
void MyBufferingAudioSource::setNextReadPosition (int64 newPosition)
|
||||
{
|
||||
const ScopedLock sl (bufferStartPosLock);
|
||||
|
||||
nextPlayPos = newPosition;
|
||||
backgroundThread.moveToFrontOfQueue (this);
|
||||
}
|
||||
|
||||
bool MyBufferingAudioSource::readNextBufferChunk()
|
||||
{
|
||||
int64 newBVS, newBVE, sectionToReadStart, sectionToReadEnd;
|
||||
|
||||
{
|
||||
const ScopedLock sl (bufferStartPosLock);
|
||||
|
||||
if (wasSourceLooping != isLooping())
|
||||
{
|
||||
wasSourceLooping = isLooping();
|
||||
bufferValidStart = 0;
|
||||
bufferValidEnd = 0;
|
||||
}
|
||||
|
||||
newBVS = jmax ((int64) 0, nextPlayPos);
|
||||
newBVE = newBVS + buffer.getNumSamples() - 4;
|
||||
sectionToReadStart = 0;
|
||||
sectionToReadEnd = 0;
|
||||
|
||||
const int maxChunkSize = 2048;
|
||||
|
||||
if (newBVS < bufferValidStart || newBVS >= bufferValidEnd)
|
||||
{
|
||||
newBVE = jmin (newBVE, newBVS + maxChunkSize);
|
||||
|
||||
sectionToReadStart = newBVS;
|
||||
sectionToReadEnd = newBVE;
|
||||
|
||||
bufferValidStart = 0;
|
||||
bufferValidEnd = 0;
|
||||
}
|
||||
else if (std::abs ((int) (newBVS - bufferValidStart)) > 512
|
||||
|| std::abs ((int) (newBVE - bufferValidEnd)) > 512)
|
||||
{
|
||||
newBVE = jmin (newBVE, bufferValidEnd + maxChunkSize);
|
||||
|
||||
sectionToReadStart = bufferValidEnd;
|
||||
sectionToReadEnd = newBVE;
|
||||
|
||||
bufferValidStart = newBVS;
|
||||
bufferValidEnd = jmin (bufferValidEnd, newBVE);
|
||||
}
|
||||
}
|
||||
|
||||
if (sectionToReadStart == sectionToReadEnd)
|
||||
return false;
|
||||
|
||||
jassert (buffer.getNumSamples() > 0);
|
||||
const int bufferIndexStart = (int) (sectionToReadStart % buffer.getNumSamples());
|
||||
const int bufferIndexEnd = (int) (sectionToReadEnd % buffer.getNumSamples());
|
||||
|
||||
if (bufferIndexStart < bufferIndexEnd)
|
||||
{
|
||||
readBufferSection (sectionToReadStart,
|
||||
(int) (sectionToReadEnd - sectionToReadStart),
|
||||
bufferIndexStart);
|
||||
}
|
||||
else
|
||||
{
|
||||
const int initialSize = buffer.getNumSamples() - bufferIndexStart;
|
||||
|
||||
readBufferSection (sectionToReadStart,
|
||||
initialSize,
|
||||
bufferIndexStart);
|
||||
|
||||
readBufferSection (sectionToReadStart + initialSize,
|
||||
(int) (sectionToReadEnd - sectionToReadStart) - initialSize,
|
||||
0);
|
||||
}
|
||||
|
||||
{
|
||||
const ScopedLock sl2 (bufferStartPosLock);
|
||||
|
||||
bufferValidStart = newBVS;
|
||||
bufferValidEnd = newBVE;
|
||||
}
|
||||
|
||||
bufferReadyEvent.signal();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MyBufferingAudioSource::readBufferSection (const int64 start, const int length, const int bufferOffset)
|
||||
{
|
||||
if (source->getNextReadPosition() != start)
|
||||
source->setNextReadPosition (start);
|
||||
|
||||
AudioSourceChannelInfo info (&buffer, bufferOffset, length);
|
||||
source->getNextAudioBlock (info);
|
||||
}
|
||||
|
||||
int MyBufferingAudioSource::useTimeSlice()
|
||||
{
|
||||
return readNextBufferChunk() ? 1 : 100;
|
||||
}
|
||||
|
||||
|
118
Source/ps3_BufferingAudioSource.h
Normal file
118
Source/ps3_BufferingAudioSource.h
Normal file
@ -0,0 +1,118 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
An AudioSource which takes another source as input, and buffers it using a thread.
|
||||
|
||||
Create this as a wrapper around another thread, and it will read-ahead with
|
||||
a background thread to smooth out playback. You can either create one of these
|
||||
directly, or use it indirectly using an AudioTransportSource.
|
||||
|
||||
@see PositionableAudioSource, AudioTransportSource
|
||||
*/
|
||||
|
||||
#include "../JuceLibraryCode/JuceHeader.h"
|
||||
|
||||
class MyBufferingAudioSource : public PositionableAudioSource,
|
||||
private TimeSliceClient
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a BufferingAudioSource.
|
||||
|
||||
@param source the input source to read from
|
||||
@param backgroundThread a background thread that will be used for the
|
||||
background read-ahead. This object must not be deleted
|
||||
until after any BufferingAudioSources that are using it
|
||||
have been deleted!
|
||||
@param deleteSourceWhenDeleted if true, then the input source object will
|
||||
be deleted when this object is deleted
|
||||
@param numberOfSamplesToBuffer the size of buffer to use for reading ahead
|
||||
@param numberOfChannels the number of channels that will be played
|
||||
@param prefillBufferOnPrepareToPlay if true, then calling prepareToPlay on this object will
|
||||
block until the buffer has been filled
|
||||
*/
|
||||
MyBufferingAudioSource(PositionableAudioSource* source,
|
||||
TimeSliceThread& backgroundThread,
|
||||
bool deleteSourceWhenDeleted,
|
||||
int numberOfSamplesToBuffer,
|
||||
int numberOfChannels = 2,
|
||||
bool prefillBufferOnPrepareToPlay = true);
|
||||
|
||||
/** Destructor.
|
||||
|
||||
The input source may be deleted depending on whether the deleteSourceWhenDeleted
|
||||
flag was set in the constructor.
|
||||
*/
|
||||
~MyBufferingAudioSource();
|
||||
|
||||
//==============================================================================
|
||||
/** Implementation of the AudioSource method. */
|
||||
void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override;
|
||||
|
||||
/** Implementation of the AudioSource method. */
|
||||
void releaseResources() override;
|
||||
|
||||
/** Implementation of the AudioSource method. */
|
||||
void getNextAudioBlock (const AudioSourceChannelInfo&) override;
|
||||
|
||||
//==============================================================================
|
||||
/** Implements the PositionableAudioSource method. */
|
||||
void setNextReadPosition (int64 newPosition) override;
|
||||
|
||||
/** Implements the PositionableAudioSource method. */
|
||||
int64 getNextReadPosition() const override;
|
||||
|
||||
/** Implements the PositionableAudioSource method. */
|
||||
int64 getTotalLength() const override { return source->getTotalLength(); }
|
||||
|
||||
/** Implements the PositionableAudioSource method. */
|
||||
bool isLooping() const override { return source->isLooping(); }
|
||||
|
||||
/** A useful function to block until the next the buffer info can be filled.
|
||||
|
||||
This is useful for offline rendering.
|
||||
*/
|
||||
bool waitForNextAudioBlockReady (const AudioSourceChannelInfo& info, const uint32 timeout);
|
||||
double getPercentReady();
|
||||
int getNumberOfChannels() { return numberOfChannels; }
|
||||
private:
|
||||
//==============================================================================
|
||||
OptionalScopedPointer<PositionableAudioSource> source;
|
||||
TimeSliceThread& backgroundThread;
|
||||
int numberOfSamplesToBuffer, numberOfChannels;
|
||||
AudioBuffer<float> buffer;
|
||||
CriticalSection bufferStartPosLock;
|
||||
WaitableEvent bufferReadyEvent;
|
||||
int64 volatile bufferValidStart, bufferValidEnd, nextPlayPos;
|
||||
double volatile sampleRate;
|
||||
bool wasSourceLooping, isPrepared, prefillBuffer;
|
||||
|
||||
bool readNextBufferChunk();
|
||||
void readBufferSection (int64 start, int length, int bufferOffset);
|
||||
int useTimeSlice() override;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MyBufferingAudioSource)
|
||||
};
|
||||
|
||||
|
150
paulstretchplugin.jucer
Normal file
150
paulstretchplugin.jucer
Normal file
@ -0,0 +1,150 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<JUCERPROJECT id="fn1Rg8" name="paulstretchplugin" displaySplashScreen="0"
|
||||
reportAppUsage="1" splashScreenColour="Dark" projectType="audioplug"
|
||||
version="1.0.0" bundleIdentifier="com.yourcompany.paulstretchplugin"
|
||||
includeBinaryInAppConfig="1" cppLanguageStandard="latest" companyCopyright=""
|
||||
buildVST="1" buildVST3="0" buildAU="1" buildAUv3="0" buildRTAS="0"
|
||||
buildAAX="0" buildStandalone="1" enableIAA="0" pluginName="paulstretchplugin"
|
||||
pluginDesc="paulstretchplugin" pluginManufacturer="Xenakios"
|
||||
pluginManufacturerCode="XenS" pluginCode="Fn1r" pluginChannelConfigs="{2,2},{8,8}"
|
||||
pluginIsSynth="0" pluginWantsMidiIn="0" pluginProducesMidiOut="0"
|
||||
pluginIsMidiEffectPlugin="0" pluginEditorRequiresKeys="0" pluginAUExportPrefix="paulstretchpluginAU"
|
||||
pluginRTASCategory="" aaxIdentifier="com.yourcompany.paulstretchplugin"
|
||||
pluginAAXCategory="AAX_ePlugInCategory_Dynamics" jucerVersion="5.2.0"
|
||||
headerPath=" ">
|
||||
<MAINGROUP id="nozXHl" name="paulstretchplugin">
|
||||
<GROUP id="{03DA6B32-F666-FF60-F168-4385D0847058}" name="Source">
|
||||
<FILE id="TDOHpE" name="resample.cpp" compile="1" resource="0" file="Source/WDL/resample.cpp"/>
|
||||
<GROUP id="{3B6D1AF9-E53E-2F78-24A5-D12A34009E6A}" name="PS_Source">
|
||||
<FILE id="bnWZA4" name="BinauralBeats.cpp" compile="1" resource="0"
|
||||
file="Source/PS_Source/BinauralBeats.cpp"/>
|
||||
<FILE id="qAo4mG" name="FreeEdit.cpp" compile="1" resource="0" file="Source/PS_Source/FreeEdit.cpp"/>
|
||||
<FILE id="gDsFRp" name="globals.h" compile="0" resource="0" file="Source/PS_Source/globals.h"/>
|
||||
<FILE id="O0cKQ4" name="Mutex.h" compile="0" resource="0" file="Source/PS_Source/Mutex.h"/>
|
||||
<FILE id="Rl76Ct" name="PaulStretchControl.h" compile="0" resource="0"
|
||||
file="Source/PS_Source/PaulStretchControl.h"/>
|
||||
<FILE id="MOQjrp" name="ProcessedStretch.h" compile="0" resource="0"
|
||||
file="Source/PS_Source/ProcessedStretch.h"/>
|
||||
<FILE id="TcGuiz" name="Stretch.h" compile="0" resource="0" file="Source/PS_Source/Stretch.h"/>
|
||||
<FILE id="yz0SM3" name="StretchSource.h" compile="0" resource="0" file="Source/PS_Source/StretchSource.h"/>
|
||||
<FILE id="G6XXRZ" name="AInputS.h" compile="0" resource="0" file="Source/PS_Source/Input/AInputS.h"/>
|
||||
<FILE id="jSSkH5" name="InputS.h" compile="0" resource="0" file="Source/PS_Source/Input/InputS.h"/>
|
||||
<FILE id="iCUwyO" name="PaulStretchControl.cpp" compile="1" resource="0"
|
||||
file="Source/PS_Source/PaulStretchControl.cpp"/>
|
||||
<FILE id="EIKlL1" name="ProcessedStretch.cpp" compile="1" resource="0"
|
||||
file="Source/PS_Source/ProcessedStretch.cpp"/>
|
||||
<FILE id="x1qOMW" name="Stretch.cpp" compile="1" resource="0" file="Source/PS_Source/Stretch.cpp"/>
|
||||
<FILE id="a7ur6I" name="StretchSource.cpp" compile="1" resource="0"
|
||||
file="Source/PS_Source/StretchSource.cpp"/>
|
||||
</GROUP>
|
||||
<FILE id="KcXfhC" name="ps3_BufferingAudioSource.cpp" compile="1" resource="0"
|
||||
file="Source/ps3_BufferingAudioSource.cpp"/>
|
||||
<FILE id="oWbh5E" name="ps3_BufferingAudioSource.h" compile="0" resource="0"
|
||||
file="Source/ps3_BufferingAudioSource.h"/>
|
||||
<FILE id="jdPS0A" name="PluginProcessor.cpp" compile="1" resource="0"
|
||||
file="Source/PluginProcessor.cpp"/>
|
||||
<FILE id="pt5tX8" name="PluginProcessor.h" compile="0" resource="0"
|
||||
file="Source/PluginProcessor.h"/>
|
||||
<FILE id="lyNyYp" name="PluginEditor.cpp" compile="1" resource="0"
|
||||
file="Source/PluginEditor.cpp"/>
|
||||
<FILE id="IO7X2q" name="PluginEditor.h" compile="0" resource="0" file="Source/PluginEditor.h"/>
|
||||
</GROUP>
|
||||
</MAINGROUP>
|
||||
<EXPORTFORMATS>
|
||||
<XCODE_MAC targetFolder="Builds/MacOSX">
|
||||
<CONFIGURATIONS>
|
||||
<CONFIGURATION name="Debug" enablePluginBinaryCopyStep="1" isDebug="1" optimisation="1"
|
||||
linkTimeOptimisation="0" targetName="paulstretchplugin" headerPath="Source/PS_Source Source/WDL "/>
|
||||
<CONFIGURATION name="Release" enablePluginBinaryCopyStep="1" isDebug="0" optimisation="3"
|
||||
linkTimeOptimisation="1" targetName="paulstretchplugin" headerPath="Source/PS_Source Source/WDL "/>
|
||||
</CONFIGURATIONS>
|
||||
<MODULEPATHS>
|
||||
<MODULEPATH id="juce_core" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_events" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_graphics" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_data_structures" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_gui_basics" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_gui_extra" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_cryptography" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_opengl" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_audio_basics" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_audio_devices" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_audio_formats" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_audio_processors" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_audio_plugin_client" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_audio_utils" path="../JUCE/modules"/>
|
||||
</MODULEPATHS>
|
||||
</XCODE_MAC>
|
||||
<VS2017 targetFolder="Builds/VisualStudio2017" externalLibraries="C:\ProgrammingProjects\gitrepos\fftw-3.3.6\fftw-3.3-libs\x64\Static-Release\libfftwf-3.3.lib">
|
||||
<CONFIGURATIONS>
|
||||
<CONFIGURATION name="Debug" winWarningLevel="4" generateManifest="1" winArchitecture="x64"
|
||||
debugInformationFormat="ProgramDatabase" enablePluginBinaryCopyStep="0"
|
||||
linkTimeOptimisation="0" isDebug="1" optimisation="1" targetName="paulstretchplugin"
|
||||
headerPath="Source/PS_Source Source/WDL "/>
|
||||
<CONFIGURATION name="Release" winWarningLevel="4" generateManifest="1" winArchitecture="x64"
|
||||
debugInformationFormat="ProgramDatabase" enablePluginBinaryCopyStep="0"
|
||||
linkTimeOptimisation="1" isDebug="0" optimisation="3" targetName="paulstretchplugin"
|
||||
headerPath="Source/PS_Source Source/WDL "/>
|
||||
</CONFIGURATIONS>
|
||||
<MODULEPATHS>
|
||||
<MODULEPATH id="juce_core" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_events" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_graphics" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_data_structures" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_gui_basics" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_gui_extra" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_cryptography" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_opengl" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_audio_basics" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_audio_devices" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_audio_formats" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_audio_processors" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_audio_plugin_client" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_audio_utils" path="../JUCE/modules"/>
|
||||
</MODULEPATHS>
|
||||
</VS2017>
|
||||
<LINUX_MAKE targetFolder="Builds/LinuxMakefile">
|
||||
<CONFIGURATIONS>
|
||||
<CONFIGURATION name="Debug" isDebug="1" optimisation="1" linkTimeOptimisation="0"
|
||||
targetName="paulstretchplugin"/>
|
||||
<CONFIGURATION name="Release" isDebug="0" optimisation="3" linkTimeOptimisation="1"
|
||||
targetName="paulstretchplugin"/>
|
||||
</CONFIGURATIONS>
|
||||
<MODULEPATHS>
|
||||
<MODULEPATH id="juce_core" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_events" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_graphics" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_data_structures" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_gui_basics" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_gui_extra" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_cryptography" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_opengl" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_audio_basics" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_audio_devices" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_audio_formats" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_audio_processors" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_audio_plugin_client" path="../JUCE/modules"/>
|
||||
<MODULEPATH id="juce_audio_utils" path="../JUCE/modules"/>
|
||||
</MODULEPATHS>
|
||||
</LINUX_MAKE>
|
||||
</EXPORTFORMATS>
|
||||
<MODULES>
|
||||
<MODULE id="juce_audio_basics" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
|
||||
<MODULE id="juce_audio_devices" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
|
||||
<MODULE id="juce_audio_formats" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
|
||||
<MODULE id="juce_audio_plugin_client" showAllCode="1" useLocalCopy="0"
|
||||
useGlobalPath="1"/>
|
||||
<MODULE id="juce_audio_processors" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
|
||||
<MODULE id="juce_audio_utils" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
|
||||
<MODULE id="juce_core" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
|
||||
<MODULE id="juce_cryptography" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
|
||||
<MODULE id="juce_data_structures" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
|
||||
<MODULE id="juce_events" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
|
||||
<MODULE id="juce_graphics" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
|
||||
<MODULE id="juce_gui_basics" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
|
||||
<MODULE id="juce_gui_extra" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
|
||||
<MODULE id="juce_opengl" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
|
||||
</MODULES>
|
||||
<JUCEOPTIONS JUCE_QUICKTIME="disabled"/>
|
||||
</JUCERPROJECT>
|
Loading…
Reference in New Issue
Block a user