mirror of
https://gitlab.com/then-try-this/samplebrain.git
synced 2025-05-12 18:47:21 +00:00
experimental pitch shifting
This commit is contained in:
parent
ad64e64984
commit
44be9d76cb
@ -23,6 +23,7 @@
|
|||||||
|
|
||||||
#include "process_thread.h"
|
#include "process_thread.h"
|
||||||
#include "audio_thread.h"
|
#include "audio_thread.h"
|
||||||
|
#include "pitchshift.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
@ -31,6 +32,8 @@ int main( int argc , char *argv[] ){
|
|||||||
MainWindow mainWin;
|
MainWindow mainWin;
|
||||||
mainWin.show();
|
mainWin.show();
|
||||||
|
|
||||||
|
pitchshift::init(44100);
|
||||||
|
|
||||||
process_thread pt;
|
process_thread pt;
|
||||||
audio_thread at(pt);
|
audio_thread at(pt);
|
||||||
pt.register_renderer(at.m_renderer);
|
pt.register_renderer(at.m_renderer);
|
||||||
|
@ -25,6 +25,7 @@ SOURCES += MainWindow.cpp \
|
|||||||
../src/renderer.cpp \
|
../src/renderer.cpp \
|
||||||
../src/status.cpp \
|
../src/status.cpp \
|
||||||
../src/window.cpp \
|
../src/window.cpp \
|
||||||
|
../src/pitchshift.cpp \
|
||||||
../src/aquila/filter/MelFilterBank.cpp \
|
../src/aquila/filter/MelFilterBank.cpp \
|
||||||
../src/aquila/filter/MelFilter.cpp \
|
../src/aquila/filter/MelFilter.cpp \
|
||||||
../src/aquila/transform/Dct.cpp \
|
../src/aquila/transform/Dct.cpp \
|
||||||
@ -38,7 +39,7 @@ SOURCES += MainWindow.cpp \
|
|||||||
../../../jellyfish/src/core/stream.cpp
|
../../../jellyfish/src/core/stream.cpp
|
||||||
|
|
||||||
INCLUDEPATH += ../src
|
INCLUDEPATH += ../src
|
||||||
LIBS += -L.. -lportaudio -lfftw3 -lsndfile -llo -ldl -lpthread -lm
|
LIBS += -L.. -lrubberband -lportaudio -lfftw3 -lsndfile -llo -ldl -lpthread -lm
|
||||||
#CONFIG+=debug
|
#CONFIG+=debug
|
||||||
|
|
||||||
QMAKE_CXXFLAGS += -Wall -Wno-unused -std=c++11 -DDONT_USE_FLUXA_GRAPH
|
QMAKE_CXXFLAGS += -Wall -Wno-unused -std=c++11 -DDONT_USE_FLUXA_GRAPH
|
||||||
|
@ -26,274 +26,279 @@ FFT *block::m_fftw;
|
|||||||
Aquila::Mfcc *block::m_mfcc_proc;
|
Aquila::Mfcc *block::m_mfcc_proc;
|
||||||
|
|
||||||
static const int MFCC_FILTERS=12;
|
static const int MFCC_FILTERS=12;
|
||||||
|
static const int FFT_BIAS=200;
|
||||||
|
|
||||||
double blend(double a, double b, double t) {
|
double blend(double a, double b, double t) {
|
||||||
return a*(1-t)+b*t;
|
return a*(1-t)+b*t;
|
||||||
}
|
}
|
||||||
|
|
||||||
double square(double a) {
|
double square(double a) {
|
||||||
return a*a;
|
return a*a;
|
||||||
}
|
}
|
||||||
|
|
||||||
void normalise(sample &in) {
|
void normalise(sample &in) {
|
||||||
// find min/max
|
// find min/max
|
||||||
float max = 0;
|
float max = 0;
|
||||||
float min = FLT_MAX;
|
float min = FLT_MAX;
|
||||||
for (u32 i=0; i<in.get_length(); ++i) {
|
for (u32 i=0; i<in.get_length(); ++i) {
|
||||||
if (in[i]<min) min=in[i];
|
if (in[i]<min) min=in[i];
|
||||||
if (in[i]>max) max=in[i];
|
if (in[i]>max) max=in[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
float mid = min+(max-min)/2.0f;
|
float mid = min+(max-min)/2.0f;
|
||||||
|
|
||||||
// remove dc
|
// remove dc
|
||||||
for (u32 i=0; i<in.get_length(); ++i) {
|
for (u32 i=0; i<in.get_length(); ++i) {
|
||||||
in[i]-=mid;
|
in[i]-=mid;
|
||||||
}
|
}
|
||||||
min-=mid;
|
min-=mid;
|
||||||
max-=mid;
|
max-=mid;
|
||||||
|
|
||||||
// squash so biggest value is 1 or -1
|
// squash so biggest value is 1 or -1
|
||||||
float div = fabs(min);
|
float div = fabs(min);
|
||||||
if (div<max) div=max;
|
if (div<max) div=max;
|
||||||
div=1/div;
|
div=1/div;
|
||||||
|
|
||||||
for (u32 i=0; i<in.get_length(); ++i) {
|
for (u32 i=0; i<in.get_length(); ++i) {
|
||||||
in[i]*=div;
|
in[i]*=div;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
block::block(u64 id, const string &filename, const sample &pcm, u32 rate, const window &w, bool ditchpcm) :
|
block::block(u64 id, const string &filename, const sample &pcm, u32 rate, const window &w, bool ditchpcm) :
|
||||||
m_id(id),
|
m_id(id),
|
||||||
m_pcm(pcm),
|
m_pcm(pcm),
|
||||||
m_fft(pcm.get_length()),
|
m_fft(pcm.get_length()),
|
||||||
m_mfcc(MFCC_FILTERS),
|
m_mfcc(MFCC_FILTERS),
|
||||||
m_n_pcm(pcm),
|
m_n_pcm(pcm),
|
||||||
m_n_fft(pcm.get_length()),
|
m_n_fft(pcm.get_length()),
|
||||||
m_n_mfcc(MFCC_FILTERS),
|
m_n_mfcc(MFCC_FILTERS),
|
||||||
m_block_size(pcm.get_length()),
|
m_block_size(pcm.get_length()),
|
||||||
m_rate(rate),
|
m_rate(rate),
|
||||||
m_orig_filename(filename),
|
m_orig_filename(filename),
|
||||||
m_usage(0)
|
m_usage(0)
|
||||||
{
|
{
|
||||||
init_fft(m_pcm.get_length());
|
init_fft(m_pcm.get_length());
|
||||||
assert(m_mfcc_proc!=NULL);
|
assert(m_mfcc_proc!=NULL);
|
||||||
assert(m_fftw!=NULL);
|
assert(m_fftw!=NULL);
|
||||||
|
|
||||||
w.run(m_pcm);
|
w.run(m_pcm);
|
||||||
process(m_pcm,m_fft,m_mfcc);
|
process(m_pcm,m_fft,m_mfcc,m_dominant_freq);
|
||||||
|
|
||||||
// rerun the normalised version
|
// rerun the normalised version
|
||||||
normalise(m_n_pcm);
|
normalise(m_n_pcm);
|
||||||
w.run(m_n_pcm);
|
w.run(m_n_pcm);
|
||||||
process(m_n_pcm,m_n_fft,m_n_mfcc);
|
process(m_n_pcm,m_n_fft,m_n_mfcc,m_n_dominant_freq);
|
||||||
|
|
||||||
if (ditchpcm) {
|
if (ditchpcm) {
|
||||||
m_pcm.clear();
|
m_pcm.clear();
|
||||||
m_n_pcm.clear();
|
m_n_pcm.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void block::init_fft(u32 block_size)
|
void block::init_fft(u32 block_size)
|
||||||
{
|
{
|
||||||
if (m_fftw == NULL || m_fftw->m_length!=block_size) {
|
if (m_fftw == NULL || m_fftw->m_length!=block_size) {
|
||||||
if (m_fftw == NULL) delete m_fftw;
|
if (m_fftw == NULL) delete m_fftw;
|
||||||
m_fftw = new FFT(block_size,100);
|
m_fftw = new FFT(block_size,100);
|
||||||
if (m_mfcc_proc == NULL) delete m_mfcc_proc;
|
if (m_mfcc_proc == NULL) delete m_mfcc_proc;
|
||||||
m_mfcc_proc = new Aquila::Mfcc(block_size);
|
m_mfcc_proc = new Aquila::Mfcc(block_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void block::process(const sample &pcm, sample &fft, sample &mfcc) {
|
void block::process(const sample &pcm, sample &fft, sample &mfcc, float &freq) {
|
||||||
m_fftw->impulse2freq(pcm.get_buffer());
|
m_fftw->impulse2freq(pcm.get_buffer());
|
||||||
m_fftw->calculate_bins();
|
m_fftw->calculate_bins();
|
||||||
|
|
||||||
// calculate fft
|
// calculate fft
|
||||||
std::vector<std::complex<double> > mfspec;
|
std::vector<std::complex<double> > mfspec;
|
||||||
for (u32 i=0; i<m_block_size; ++i) {
|
for (u32 i=0; i<m_block_size; ++i) {
|
||||||
mfspec.push_back(std::complex<double>(m_fftw->m_spectrum[i][0],
|
mfspec.push_back(std::complex<double>(m_fftw->m_spectrum[i][0],
|
||||||
m_fftw->m_spectrum[i][1]));
|
m_fftw->m_spectrum[i][1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 fft_size = m_block_size;
|
freq = m_fftw->calculate_dominant_freq();
|
||||||
if (fft_size>100) {
|
|
||||||
fft.crop_to(100);
|
|
||||||
fft_size=100;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (u32 i=0; i<fft_size; ++i) {
|
u32 fft_size = m_block_size;
|
||||||
fft[i]=m_fftw->m_bin[i];
|
if (fft_size>100) {
|
||||||
}
|
fft.crop_to(100);
|
||||||
|
fft_size=100;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (u32 i=0; i<fft_size; ++i) {
|
||||||
|
fft[i]=m_fftw->m_bin[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// calculate mfcc
|
// calculate mfcc
|
||||||
std::vector<double> m = m_mfcc_proc->calculate(mfspec,MFCC_FILTERS);
|
std::vector<double> m = m_mfcc_proc->calculate(mfspec,MFCC_FILTERS);
|
||||||
for (u32 i=0; i<MFCC_FILTERS; ++i) {
|
for (u32 i=0; i<MFCC_FILTERS; ++i) {
|
||||||
mfcc[i] = m[i];
|
mfcc[i] = m[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#define FFT_BIAS 200
|
|
||||||
|
|
||||||
double block::_compare(const sample &fft_a, const sample &mfcc_a,
|
double block::_compare(const sample &fft_a, const sample &mfcc_a,
|
||||||
const sample &fft_b, const sample &mfcc_b,
|
const sample &fft_b, const sample &mfcc_b,
|
||||||
const search_params ¶ms) const
|
const search_params ¶ms) const
|
||||||
{
|
{
|
||||||
double mfcc_acc=0;
|
double mfcc_acc=0;
|
||||||
double fft_acc=0;
|
double fft_acc=0;
|
||||||
|
|
||||||
u32 fft_start = params.m_fft1_start;
|
u32 fft_start = params.m_fft1_start;
|
||||||
u32 fft_end = fmin(params.m_fft1_end,m_fft.get_length());
|
u32 fft_end = fmin(params.m_fft1_end,m_fft.get_length());
|
||||||
|
|
||||||
if (params.m_ratio==0) {
|
if (params.m_ratio==0) {
|
||||||
for (u32 i=fft_start; i<fft_end; ++i) {
|
|
||||||
fft_acc+=square(fft_a[i]-fft_b[i]);
|
|
||||||
}
|
|
||||||
return (fft_acc/(float)fft_a.get_length())*FFT_BIAS;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (params.m_ratio==1) {
|
|
||||||
for (u32 i=0; i<MFCC_FILTERS; ++i) {
|
|
||||||
mfcc_acc+=square(mfcc_a[i]-mfcc_b[i]);
|
|
||||||
}
|
|
||||||
return mfcc_acc/(float)MFCC_FILTERS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculate both
|
|
||||||
for (u32 i=fft_start; i<fft_end; ++i) {
|
for (u32 i=fft_start; i<fft_end; ++i) {
|
||||||
fft_acc+=square(fft_a[i]-fft_b[i]);
|
fft_acc+=square(fft_a[i]-fft_b[i]);
|
||||||
}
|
|
||||||
for (u32 i=0; i<MFCC_FILTERS; ++i) {
|
|
||||||
mfcc_acc+=square(mfcc_a[i]-mfcc_b[i]);
|
|
||||||
}
|
}
|
||||||
|
return (fft_acc/(float)fft_a.get_length())*FFT_BIAS;
|
||||||
|
}
|
||||||
|
|
||||||
return blend(fft_acc/(float)fft_a.get_length(),
|
if (params.m_ratio==1) {
|
||||||
mfcc_acc/(float)MFCC_FILTERS,
|
for (u32 i=0; i<MFCC_FILTERS; ++i) {
|
||||||
params.m_ratio);
|
mfcc_acc+=square(mfcc_a[i]-mfcc_b[i]);
|
||||||
|
}
|
||||||
|
return mfcc_acc/(float)MFCC_FILTERS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate both
|
||||||
|
for (u32 i=fft_start; i<fft_end; ++i) {
|
||||||
|
fft_acc+=square(fft_a[i]-fft_b[i]);
|
||||||
|
}
|
||||||
|
for (u32 i=0; i<MFCC_FILTERS; ++i) {
|
||||||
|
mfcc_acc+=square(mfcc_a[i]-mfcc_b[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return blend(fft_acc/(float)fft_a.get_length(),
|
||||||
|
mfcc_acc/(float)MFCC_FILTERS,
|
||||||
|
params.m_ratio);
|
||||||
}
|
}
|
||||||
|
|
||||||
double block::compare(const block &other, const search_params ¶ms) const {
|
double block::compare(const block &other, const search_params ¶ms) const {
|
||||||
return blend(
|
return blend(
|
||||||
blend(_compare(m_fft, m_mfcc, other.m_fft, other.m_mfcc, params),
|
blend(_compare(m_fft, m_mfcc, other.m_fft, other.m_mfcc, params),
|
||||||
_compare(m_n_fft, m_n_mfcc, other.m_n_fft, other.m_n_mfcc, params),
|
_compare(m_n_fft, m_n_mfcc, other.m_n_fft, other.m_n_mfcc, params),
|
||||||
params.m_n_ratio),
|
params.m_n_ratio),
|
||||||
other.m_usage, params.m_usage_importance);
|
other.m_usage, params.m_usage_importance);
|
||||||
}
|
}
|
||||||
|
|
||||||
ios &spiralcore::operator||(ios &s, block &b) {
|
ios &spiralcore::operator||(ios &s, block &b) {
|
||||||
u32 version=2;
|
u32 version=3;
|
||||||
string id("block");
|
string id("block");
|
||||||
s||id||version;
|
s||id||version;
|
||||||
|
|
||||||
if (version>1) s||b.m_id;
|
if (version>1) s||b.m_id;
|
||||||
|
if (version>2) {
|
||||||
|
s||b.m_dominant_freq;
|
||||||
|
s||b.m_n_dominant_freq;
|
||||||
|
}
|
||||||
|
|
||||||
s||b.m_pcm||b.m_fft||b.m_mfcc;
|
s||b.m_pcm||b.m_fft||b.m_mfcc;
|
||||||
s||b.m_n_pcm||b.m_n_fft||b.m_n_mfcc;
|
s||b.m_n_pcm||b.m_n_fft||b.m_n_mfcc;
|
||||||
|
|
||||||
s||b.m_block_size||b.m_rate||b.m_orig_filename;
|
s||b.m_block_size||b.m_rate||b.m_orig_filename;
|
||||||
stream_vector(s,b.m_synapse);
|
stream_vector(s,b.m_synapse);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool block::unit_test() {
|
bool block::unit_test() {
|
||||||
|
|
||||||
stream_unit_test();
|
stream_unit_test();
|
||||||
|
|
||||||
sample ntest(3);
|
sample ntest(3);
|
||||||
u32 idx=0;
|
u32 idx=0;
|
||||||
ntest[idx++]=-1;
|
ntest[idx++]=-1;
|
||||||
ntest[idx++]=1;
|
ntest[idx++]=1;
|
||||||
ntest[idx++]=-1;
|
ntest[idx++]=-1;
|
||||||
idx=0;
|
idx=0;
|
||||||
normalise(ntest);
|
normalise(ntest);
|
||||||
assert(feq(ntest[idx++],-1));
|
assert(feq(ntest[idx++],-1));
|
||||||
assert(feq(ntest[idx++],1));
|
assert(feq(ntest[idx++],1));
|
||||||
assert(feq(ntest[idx++],-1));
|
assert(feq(ntest[idx++],-1));
|
||||||
idx=0;
|
idx=0;
|
||||||
ntest[idx++]=-2;
|
ntest[idx++]=-2;
|
||||||
ntest[idx++]=2;
|
ntest[idx++]=2;
|
||||||
ntest[idx++]=-2;
|
ntest[idx++]=-2;
|
||||||
normalise(ntest);
|
normalise(ntest);
|
||||||
idx=0;
|
idx=0;
|
||||||
assert(feq(ntest[idx++],-1));
|
assert(feq(ntest[idx++],-1));
|
||||||
assert(feq(ntest[idx++],1));
|
assert(feq(ntest[idx++],1));
|
||||||
assert(feq(ntest[idx++],-1));
|
assert(feq(ntest[idx++],-1));
|
||||||
idx=0;
|
idx=0;
|
||||||
ntest[idx++]=19;
|
ntest[idx++]=19;
|
||||||
ntest[idx++]=20;
|
ntest[idx++]=20;
|
||||||
ntest[idx++]=19;
|
ntest[idx++]=19;
|
||||||
normalise(ntest);
|
normalise(ntest);
|
||||||
idx=0;
|
idx=0;
|
||||||
assert(feq(ntest[idx++],-1));
|
assert(feq(ntest[idx++],-1));
|
||||||
assert(feq(ntest[idx++],1));
|
assert(feq(ntest[idx++],1));
|
||||||
assert(feq(ntest[idx++],-1));
|
assert(feq(ntest[idx++],-1));
|
||||||
|
|
||||||
sample data(200);
|
sample data(200);
|
||||||
for (u32 i=0; i<data.get_length(); i++) {
|
for (u32 i=0; i<data.get_length(); i++) {
|
||||||
data[i]=i/(float)data.get_length();
|
data[i]=i/(float)data.get_length();
|
||||||
}
|
}
|
||||||
window w;
|
window w;
|
||||||
w.init(data.get_length());
|
w.init(data.get_length());
|
||||||
w.set_current_type(window::RECTANGLE);
|
w.set_current_type(window::RECTANGLE);
|
||||||
|
|
||||||
block bb(0,"test",data,44100,w);
|
block bb(0,"test",data,44100,w);
|
||||||
|
|
||||||
assert(bb.m_pcm.get_length()==data.get_length());
|
assert(bb.m_pcm.get_length()==data.get_length());
|
||||||
//assert(bb.m_fft.get_length()==data.get_length());
|
//assert(bb.m_fft.get_length()==data.get_length());
|
||||||
assert(bb.m_mfcc.get_length()==MFCC_FILTERS);
|
assert(bb.m_mfcc.get_length()==MFCC_FILTERS);
|
||||||
assert(bb.m_orig_filename==string("test"));
|
assert(bb.m_orig_filename==string("test"));
|
||||||
assert(bb.m_rate==44100);
|
assert(bb.m_rate==44100);
|
||||||
assert(bb.m_block_size==data.get_length());
|
assert(bb.m_block_size==data.get_length());
|
||||||
|
|
||||||
ofstream of("test_data/blocktest.bin",ios::binary);
|
ofstream of("test_data/blocktest.bin",ios::binary);
|
||||||
of||bb;
|
of||bb;
|
||||||
of.close();
|
of.close();
|
||||||
|
|
||||||
cerr<<"written"<<endl;
|
cerr<<"written"<<endl;
|
||||||
|
|
||||||
ifstream ifs("test_data/blocktest.bin",ios::binary);
|
ifstream ifs("test_data/blocktest.bin",ios::binary);
|
||||||
block bbb;
|
block bbb;
|
||||||
ifs||bbb;
|
ifs||bbb;
|
||||||
ifs.close();
|
ifs.close();
|
||||||
|
|
||||||
assert(bbb.m_pcm.get_length()==data.get_length());
|
assert(bbb.m_pcm.get_length()==data.get_length());
|
||||||
//assert(bb.m_fft.get_length()==data.get_length());
|
//assert(bb.m_fft.get_length()==data.get_length());
|
||||||
assert(bbb.m_mfcc.get_length()==MFCC_FILTERS);
|
assert(bbb.m_mfcc.get_length()==MFCC_FILTERS);
|
||||||
assert(bbb.m_orig_filename==string("test"));
|
assert(bbb.m_orig_filename==string("test"));
|
||||||
assert(bbb.m_rate==44100);
|
assert(bbb.m_rate==44100);
|
||||||
assert(bbb.m_block_size==data.get_length());
|
assert(bbb.m_block_size==data.get_length());
|
||||||
|
|
||||||
|
|
||||||
search_params p(0,0,0,100,0);
|
search_params p(0,0,0,100,0);
|
||||||
block bb2(0,"test",data,44100,w);
|
block bb2(0,"test",data,44100,w);
|
||||||
assert(bb.compare(bb2,p)==0);
|
assert(bb.compare(bb2,p)==0);
|
||||||
p.m_ratio=1;
|
p.m_ratio=1;
|
||||||
assert(bb.compare(bb2,p)==0);
|
assert(bb.compare(bb2,p)==0);
|
||||||
|
p.m_ratio=0.5;
|
||||||
|
assert(bb.compare(bb2,p)==0);
|
||||||
|
|
||||||
|
sample data2(200);
|
||||||
|
for (u32 i=0; i<data.get_length(); i++) {
|
||||||
|
data2[i]=i%10;
|
||||||
|
}
|
||||||
|
|
||||||
|
block cpy(0,"test",data,100,w);
|
||||||
|
{
|
||||||
|
block bb3(0,"test",data2,44100,w);
|
||||||
|
p.m_ratio=0.0;
|
||||||
|
assert(bb.compare(bb3,p)!=0);
|
||||||
|
assert(bb.compare(bb3,p)!=0);
|
||||||
p.m_ratio=0.5;
|
p.m_ratio=0.5;
|
||||||
assert(bb.compare(bb2,p)==0);
|
assert(bb.compare(bb3,p)!=0);
|
||||||
|
cpy=bb3;
|
||||||
|
}
|
||||||
|
|
||||||
sample data2(200);
|
assert(cpy.m_pcm.get_length()==200);
|
||||||
for (u32 i=0; i<data.get_length(); i++) {
|
|
||||||
data2[i]=i%10;
|
|
||||||
}
|
|
||||||
|
|
||||||
block cpy(0,"test",data,100,w);
|
|
||||||
{
|
|
||||||
block bb3(0,"test",data2,44100,w);
|
|
||||||
p.m_ratio=0.0;
|
|
||||||
assert(bb.compare(bb3,p)!=0);
|
|
||||||
assert(bb.compare(bb3,p)!=0);
|
|
||||||
p.m_ratio=0.5;
|
|
||||||
assert(bb.compare(bb3,p)!=0);
|
|
||||||
cpy=bb3;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(cpy.m_pcm.get_length()==200);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -29,8 +29,8 @@
|
|||||||
|
|
||||||
namespace spiralcore {
|
namespace spiralcore {
|
||||||
|
|
||||||
class block {
|
class block {
|
||||||
public:
|
public:
|
||||||
// runs analysis on pcm
|
// runs analysis on pcm
|
||||||
block(u64 id, const std::string &filename, const sample &pcm, u32 rate, const window &w, bool ditchpcm=false);
|
block(u64 id, const std::string &filename, const sample &pcm, u32 rate, const window &w, bool ditchpcm=false);
|
||||||
block() {}
|
block() {}
|
||||||
@ -47,10 +47,12 @@ public:
|
|||||||
std::vector<u32> &get_synapse() { return m_synapse; }
|
std::vector<u32> &get_synapse() { return m_synapse; }
|
||||||
const std::vector<u32> &get_synapse_const() const { return m_synapse; }
|
const std::vector<u32> &get_synapse_const() const { return m_synapse; }
|
||||||
float &get_usage() { return m_usage; }
|
float &get_usage() { return m_usage; }
|
||||||
|
float get_freq() const { return m_dominant_freq; }
|
||||||
|
float get_n_freq() const { return m_n_dominant_freq; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void process(const sample &pcm, sample &fft, sample &mfcc);
|
void process(const sample &pcm, sample &fft, sample &mfcc, float &freq);
|
||||||
|
|
||||||
double _compare(const sample &fft_a, const sample &mfcc_a,
|
double _compare(const sample &fft_a, const sample &mfcc_a,
|
||||||
const sample &fft_b, const sample &mfcc_b,
|
const sample &fft_b, const sample &mfcc_b,
|
||||||
@ -75,11 +77,14 @@ private:
|
|||||||
std::vector<u32> m_synapse;
|
std::vector<u32> m_synapse;
|
||||||
float m_usage;
|
float m_usage;
|
||||||
|
|
||||||
|
float m_dominant_freq;
|
||||||
|
float m_n_dominant_freq;
|
||||||
|
|
||||||
friend ios &operator||(ios &s, block &b);
|
friend ios &operator||(ios &s, block &b);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ios &operator||(ios &s, block &b);
|
ios &operator||(ios &s, block &b);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,19 +71,19 @@ void brain::init(u32 block_size, u32 overlap, window::type t, bool ditchpcm) {
|
|||||||
u32 count=0;
|
u32 count=0;
|
||||||
for (std::list<sound>::iterator i=m_samples.begin(); i!=m_samples.end(); ++i) {
|
for (std::list<sound>::iterator i=m_samples.begin(); i!=m_samples.end(); ++i) {
|
||||||
count++;
|
count++;
|
||||||
chop_and_add(i->m_sample, count, ditchpcm);
|
chop_and_add(*i, count, ditchpcm);
|
||||||
}
|
}
|
||||||
status::update("all samples processed");
|
status::update("all samples processed");
|
||||||
}
|
}
|
||||||
|
|
||||||
void brain::chop_and_add(const sample &s, u32 count, bool ditchpcm) {
|
void brain::chop_and_add(const sound &s, u32 count, bool ditchpcm) {
|
||||||
u32 pos=0;
|
u32 pos=0;
|
||||||
if (m_overlap>=m_block_size) m_overlap=0;
|
if (m_overlap>=m_block_size) m_overlap=0;
|
||||||
while (pos+m_block_size-1<s.get_length()) {
|
while (pos+m_block_size-1<s.m_sample.get_length()) {
|
||||||
status::update("processing sample %d: %d%%",count,(int)(pos/(float)s.get_length()*100));
|
status::update("processing sample %d: %d%%",count,(int)(pos/(float)s.m_sample.get_length()*100));
|
||||||
sample region;
|
sample region;
|
||||||
s.get_region(region,pos,pos+m_block_size-1);
|
s.m_sample.get_region(region,pos,pos+m_block_size-1);
|
||||||
m_blocks.push_back(block(m_blocks.size(),"",region,44100,m_window,ditchpcm));
|
m_blocks.push_back(block(m_blocks.size(),s.m_filename,region,44100,m_window,ditchpcm));
|
||||||
pos += (m_block_size-m_overlap);
|
pos += (m_block_size-m_overlap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,12 +84,13 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void chop_and_add(const sample &s, u32 count, bool ditchpcm=false);
|
void chop_and_add(const sound &s, u32 count, bool ditchpcm=false);
|
||||||
void deplete_usage();
|
void deplete_usage();
|
||||||
u32 stickify(const block &target, u32 closest_index, f32 dist, const search_params ¶ms);
|
u32 stickify(const block &target, u32 closest_index, f32 dist, const search_params ¶ms);
|
||||||
|
|
||||||
vector<block> m_blocks;
|
vector<block> m_blocks;
|
||||||
std::list<sound> m_samples;
|
std::list<sound> m_samples;
|
||||||
|
vector<string> m_active_sounds;
|
||||||
|
|
||||||
u32 m_block_size;
|
u32 m_block_size;
|
||||||
u32 m_overlap;
|
u32 m_overlap;
|
||||||
|
@ -25,58 +25,73 @@ using namespace std;
|
|||||||
static const int MAX_FFT_LENGTH = 4096;
|
static const int MAX_FFT_LENGTH = 4096;
|
||||||
|
|
||||||
FFT::FFT(u32 length, u32 bins) :
|
FFT::FFT(u32 length, u32 bins) :
|
||||||
m_length(length),
|
m_length(length),
|
||||||
m_num_bins(bins),
|
m_num_bins(bins),
|
||||||
m_in(new double[length]),
|
m_in(new double[length]),
|
||||||
m_spectrum(new fftw_complex[length]),
|
m_spectrum(new fftw_complex[length]),
|
||||||
m_bin(new float[bins])
|
m_bin(new float[bins])
|
||||||
{
|
{
|
||||||
memset(m_spectrum,0,sizeof(fftw_complex)*length);
|
memset(m_spectrum,0,sizeof(fftw_complex)*length);
|
||||||
m_plan = fftw_plan_dft_r2c_1d(m_length, m_in, m_spectrum, FFTW_ESTIMATE);
|
m_plan = fftw_plan_dft_r2c_1d(m_length, m_in, m_spectrum, FFTW_ESTIMATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
FFT::~FFT()
|
FFT::~FFT()
|
||||||
{
|
{
|
||||||
delete[] m_in;
|
delete[] m_in;
|
||||||
fftw_destroy_plan(m_plan);
|
fftw_destroy_plan(m_plan);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FFT::impulse2freq(const float *imp)
|
void FFT::impulse2freq(const float *imp)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
for (i=0; i<m_length; i++)
|
for (i=0; i<m_length; i++)
|
||||||
{
|
{
|
||||||
m_in[i] = imp[i];
|
m_in[i] = imp[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
fftw_execute(m_plan);
|
fftw_execute(m_plan);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const float SRATE = 44100;
|
||||||
|
|
||||||
|
float FFT::calculate_dominant_freq() {
|
||||||
|
double highest = 0;
|
||||||
|
u32 index = 0;
|
||||||
|
for (u32 i=0; i<m_length/2; ++i) {
|
||||||
|
double t = m_spectrum[i][0]*m_spectrum[i][0];
|
||||||
|
if (t>highest) {
|
||||||
|
index=i;
|
||||||
|
highest=t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return index * (SRATE/(float)m_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FFT::calculate_bins() {
|
void FFT::calculate_bins() {
|
||||||
float useful_area = m_length/2;
|
float useful_area = m_length/2;
|
||||||
|
|
||||||
for (unsigned int n=0; n<m_num_bins; n++) {
|
for (unsigned int n=0; n<m_num_bins; n++) {
|
||||||
float value = 0;
|
float value = 0;
|
||||||
|
|
||||||
float f = n/(float)m_num_bins;
|
float f = n/(float)m_num_bins;
|
||||||
float t = (n+1)/(float)m_num_bins;
|
float t = (n+1)/(float)m_num_bins;
|
||||||
//f*=f;
|
//f*=f;
|
||||||
//t*=t;
|
//t*=t;
|
||||||
u32 from = f*useful_area;
|
u32 from = f*useful_area;
|
||||||
u32 to = t*useful_area;
|
u32 to = t*useful_area;
|
||||||
|
|
||||||
//cerr<<"fft bin:"<<from<<" "<<to<<" - "<<m_length<<endl;
|
//cerr<<"fft bin:"<<from<<" "<<to<<" - "<<m_length<<endl;
|
||||||
|
|
||||||
for (u32 i=from; i<=to; i++) {
|
for (u32 i=from; i<=to; i++) {
|
||||||
if (i<m_length) {
|
if (i<m_length) {
|
||||||
value += m_spectrum[i][0];
|
value += m_spectrum[i][0]*m_spectrum[i][0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value<0) value=-value;
|
if (value<0) value=-value;
|
||||||
m_bin[n]=value;
|
m_bin[n]=value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -24,13 +24,14 @@
|
|||||||
|
|
||||||
namespace spiralcore {
|
namespace spiralcore {
|
||||||
|
|
||||||
class FFT
|
class FFT
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FFT(u32 length, u32 num_bins);
|
FFT(u32 length, u32 num_bins);
|
||||||
~FFT();
|
~FFT();
|
||||||
void impulse2freq(const float *imp);
|
void impulse2freq(const float *imp);
|
||||||
void calculate_bins();
|
void calculate_bins();
|
||||||
|
float calculate_dominant_freq();
|
||||||
|
|
||||||
fftw_plan m_plan;
|
fftw_plan m_plan;
|
||||||
u32 m_length;
|
u32 m_length;
|
||||||
@ -38,7 +39,7 @@ public:
|
|||||||
double *m_in;
|
double *m_in;
|
||||||
fftw_complex *m_spectrum;
|
fftw_complex *m_spectrum;
|
||||||
float *m_bin;
|
float *m_bin;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
39
samplebrain/src/pitchshift.cpp
Normal file
39
samplebrain/src/pitchshift.cpp
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// Copyright (C) 2015 Foam Kernow
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation; either version 2 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// 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 for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
|
||||||
|
#include <pitchshift.h>
|
||||||
|
|
||||||
|
using namespace spiralcore;
|
||||||
|
using namespace std;
|
||||||
|
using namespace RubberBand;
|
||||||
|
|
||||||
|
RubberBandStretcher *pitchshift::m_stretcher = NULL;
|
||||||
|
|
||||||
|
void pitchshift::init(u32 srate) {
|
||||||
|
if (m_stretcher!=NULL) delete m_stretcher;
|
||||||
|
m_stretcher = new RubberBandStretcher(srate,1,
|
||||||
|
RubberBandStretcher::OptionProcessRealTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pitchshift::process(const sample &in, float freq_scale, sample &out) {
|
||||||
|
if (freq_scale<1/256) freq_scale=1;
|
||||||
|
if (freq_scale>255) freq_scale=255;
|
||||||
|
m_stretcher->setPitchScale(freq_scale);
|
||||||
|
const float *in_ptr = in.get_buffer();
|
||||||
|
m_stretcher->process(&in_ptr, in.get_length(), false);
|
||||||
|
float *out_ptr = out.get_non_const_buffer();
|
||||||
|
m_stretcher->retrieve(&out_ptr, out.get_length());
|
||||||
|
}
|
35
samplebrain/src/pitchshift.h
Normal file
35
samplebrain/src/pitchshift.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Copyright (C) 2015 Foam Kernow
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation; either version 2 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// 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 for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
|
||||||
|
#ifndef SPIRALCORE_PITCHSHIFT
|
||||||
|
#define SPIRALCORE_PITCHSHIFT
|
||||||
|
|
||||||
|
#include <jellyfish/core/types.h>
|
||||||
|
#include <jellyfish/fluxa/sample.h>
|
||||||
|
#include <rubberband/RubberBandStretcher.h>
|
||||||
|
|
||||||
|
namespace spiralcore {
|
||||||
|
|
||||||
|
class pitchshift {
|
||||||
|
public:
|
||||||
|
static void init(u32 srate);
|
||||||
|
static void process(const sample &in, float freq_change, sample &out);
|
||||||
|
|
||||||
|
static RubberBand::RubberBandStretcher *m_stretcher;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
@ -16,76 +16,78 @@
|
|||||||
|
|
||||||
#include "renderer.h"
|
#include "renderer.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include "pitchshift.h"
|
||||||
|
|
||||||
using namespace spiralcore;
|
using namespace spiralcore;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
void renderer::init(brain &source, brain &target) {
|
void renderer::init(brain &source, brain &target) {
|
||||||
m_volume=1;
|
m_volume=1;
|
||||||
m_playing=false;
|
m_playing=false;
|
||||||
m_source=source;
|
m_source=source;
|
||||||
m_target=target;
|
m_target=target;
|
||||||
m_target_time=0;
|
m_target_time=0;
|
||||||
m_render_time=0;
|
m_render_time=0;
|
||||||
m_n_mix=0;
|
m_n_mix=0;
|
||||||
m_target_mix=0;
|
m_target_mix=0;
|
||||||
m_render_blocks.clear();
|
m_render_blocks.clear();
|
||||||
m_search_algo=BASIC;
|
m_search_algo=BASIC;
|
||||||
m_slide_error=1;
|
m_slide_error=1;
|
||||||
m_target_index=0;
|
m_target_index=0;
|
||||||
m_render_index=0;
|
m_render_index=0;
|
||||||
m_stretch=1;
|
m_stretch=1;
|
||||||
m_last_tgt_shift=0;
|
m_last_tgt_shift=0;
|
||||||
|
m_autotune=0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ratio_time = 0;
|
static int ratio_time = 0;
|
||||||
|
|
||||||
void renderer::reset() {
|
void renderer::reset() {
|
||||||
m_target_time=0;
|
m_target_time=0;
|
||||||
m_render_time=0;
|
m_render_time=0;
|
||||||
m_target_index=0;
|
m_target_index=0;
|
||||||
m_render_index=0;
|
m_render_index=0;
|
||||||
m_render_blocks.clear();
|
m_render_blocks.clear();
|
||||||
m_source.jiggle();
|
m_source.jiggle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void renderer::process(u32 nframes, float *buf) {
|
void renderer::process(u32 nframes, float *buf) {
|
||||||
if (!m_playing) return;
|
if (!m_playing) return;
|
||||||
if (!find_render_blocks(nframes)) return;
|
if (!find_render_blocks(nframes)) return;
|
||||||
|
|
||||||
render(nframes,buf);
|
render(nframes,buf);
|
||||||
|
|
||||||
clean_up();
|
clean_up();
|
||||||
|
|
||||||
m_render_time+=nframes;
|
m_render_time+=nframes;
|
||||||
//m_target_time+=nframes/(float)m_stretch;
|
//m_target_time+=nframes/(float)m_stretch;
|
||||||
}
|
}
|
||||||
|
|
||||||
// target_time = samples time into target stream
|
// target_time = samples time into target stream
|
||||||
// render_time = position in output stream, updated per process - used for offsets
|
// render_time = position in output stream, updated per process - used for offsets
|
||||||
|
|
||||||
bool renderer::find_render_blocks(u32 nframes) {
|
bool renderer::find_render_blocks(u32 nframes) {
|
||||||
// get new blocks from source for the current buffer
|
// get new blocks from source for the current buffer
|
||||||
|
|
||||||
// where are we phase?
|
// where are we phase?
|
||||||
u32 tgt_shift = m_target.get_block_size()-m_target.get_overlap();
|
u32 tgt_shift = m_target.get_block_size()-m_target.get_overlap();
|
||||||
m_target_index = m_target_time/(float)tgt_shift;
|
m_target_index = m_target_time/(float)tgt_shift;
|
||||||
u32 tgt_end = (m_target_time+nframes)/(float)tgt_shift;
|
u32 tgt_end = (m_target_time+nframes)/(float)tgt_shift;
|
||||||
u32 rnd_end = (m_render_time+nframes)/(float)tgt_shift;
|
u32 rnd_end = (m_render_time+nframes)/(float)tgt_shift;
|
||||||
|
|
||||||
|
|
||||||
// stuff has changed - recompute and abort
|
// stuff has changed - recompute and abort
|
||||||
if (tgt_shift!=m_last_tgt_shift ||
|
if (tgt_shift!=m_last_tgt_shift ||
|
||||||
tgt_end>=m_target.get_num_blocks() ||
|
tgt_end>=m_target.get_num_blocks() ||
|
||||||
m_source.get_num_blocks()==0) {
|
m_source.get_num_blocks()==0) {
|
||||||
reset();
|
reset();
|
||||||
m_last_tgt_shift = tgt_shift;
|
m_last_tgt_shift = tgt_shift;
|
||||||
// next time...
|
// next time...
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
cerr<<"-----------------"<<endl;
|
cerr<<"-----------------"<<endl;
|
||||||
cerr<<"tgt start:"<<m_target_index<<endl;
|
cerr<<"tgt start:"<<m_target_index<<endl;
|
||||||
cerr<<"tgt end:"<<tgt_end<<endl;
|
cerr<<"tgt end:"<<tgt_end<<endl;
|
||||||
@ -97,280 +99,161 @@ bool renderer::find_render_blocks(u32 nframes) {
|
|||||||
cerr<<"render time (index) "<<m_render_index*tgt_shift<<endl;
|
cerr<<"render time (index) "<<m_render_index*tgt_shift<<endl;
|
||||||
cerr<<"real vs index = "<<(s32)m_render_time-(s32)(m_render_index*tgt_shift)<<endl;
|
cerr<<"real vs index = "<<(s32)m_render_time-(s32)(m_render_index*tgt_shift)<<endl;
|
||||||
cerr<<m_render_blocks.size()<<endl;
|
cerr<<m_render_blocks.size()<<endl;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// search phase
|
// search phase
|
||||||
// get indices for current buffer
|
// get indices for current buffer
|
||||||
u32 counter = m_render_index;
|
u32 counter = m_render_index;
|
||||||
//u32 cur_time = m_render_time;
|
//u32 cur_time = m_render_time;
|
||||||
while (counter<=rnd_end) {
|
while (counter<=rnd_end) {
|
||||||
u32 time=m_render_index*tgt_shift;
|
u32 time=m_render_index*tgt_shift;
|
||||||
u32 src_index=0;
|
u32 src_index=0;
|
||||||
|
|
||||||
switch (m_search_algo) {
|
switch (m_search_algo) {
|
||||||
case BASIC:
|
case BASIC:
|
||||||
src_index = m_source.search(m_target.get_block(m_target_index), m_search_params);
|
src_index = m_source.search(m_target.get_block(m_target_index), m_search_params);
|
||||||
break;
|
break;
|
||||||
case REV_BASIC:
|
case REV_BASIC:
|
||||||
src_index = m_source.rev_search(m_target.get_block(m_target_index), m_search_params);
|
src_index = m_source.rev_search(m_target.get_block(m_target_index), m_search_params);
|
||||||
break;
|
break;
|
||||||
case SYNAPTIC:
|
case SYNAPTIC:
|
||||||
case SYNAPTIC_SLIDE:
|
case SYNAPTIC_SLIDE:
|
||||||
src_index = m_source.search_synapses(m_target.get_block(m_target_index), m_search_params);
|
src_index = m_source.search_synapses(m_target.get_block(m_target_index), m_search_params);
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
if (m_search_algo==SYNAPTIC_SLIDE) {
|
|
||||||
m_render_blocks.push_back(render_block(src_index,m_target_index,time));
|
|
||||||
|
|
||||||
if (m_source.get_current_error()<m_slide_error &&
|
|
||||||
m_render_index%m_stretch==0) {
|
|
||||||
m_target_index++;
|
|
||||||
m_target_time+=tgt_shift;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// put them in the index list
|
|
||||||
m_render_blocks.push_back(render_block(src_index,m_target_index,time));
|
|
||||||
|
|
||||||
if (m_render_index%m_stretch==0) {
|
|
||||||
m_target_index++;
|
|
||||||
m_target_time+=tgt_shift;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
counter++;
|
|
||||||
m_render_index++;
|
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
|
if (m_search_algo==SYNAPTIC_SLIDE) {
|
||||||
|
m_render_blocks.push_back(render_block(src_index,m_target_index,time));
|
||||||
|
|
||||||
|
if (m_source.get_current_error()<m_slide_error &&
|
||||||
|
m_render_index%m_stretch==0) {
|
||||||
|
m_target_index++;
|
||||||
|
m_target_time+=tgt_shift;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// put them in the index list
|
||||||
|
m_render_blocks.push_back(render_block(src_index,m_target_index,time));
|
||||||
|
|
||||||
|
if (m_render_index%m_stretch==0) {
|
||||||
|
m_target_index++;
|
||||||
|
m_target_time+=tgt_shift;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
counter++;
|
||||||
|
m_render_index++;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderer::render(u32 nframes, float *buf) {
|
void renderer::render(u32 nframes, float *buf) {
|
||||||
// render phase
|
sample render_pcm(m_source.get_block_size());
|
||||||
// render all blocks in list
|
|
||||||
for (std::list<render_block>::iterator i=m_render_blocks.begin(); i!=m_render_blocks.end(); ++i) {
|
|
||||||
const sample &pcm=m_source.get_block(i->m_index).get_pcm();
|
|
||||||
const sample &n_pcm=m_source.get_block(i->m_index).get_n_pcm();
|
|
||||||
const sample &target_pcm=m_target.get_block(i->m_tgt_index).get_pcm();
|
|
||||||
// get the sample offset into the buffer
|
|
||||||
s32 offset = i->m_time-m_render_time;
|
|
||||||
|
|
||||||
// assume midway through block
|
// render phase
|
||||||
u32 block_start = offset;
|
// render all blocks in list
|
||||||
u32 buffer_start = 0;
|
for (std::list<render_block>::iterator i=m_render_blocks.begin(); i!=m_render_blocks.end(); ++i) {
|
||||||
if (offset<0) {
|
const sample &pcm=m_source.get_block(i->m_index).get_pcm();
|
||||||
block_start=-offset;
|
const sample &n_pcm=m_source.get_block(i->m_index).get_n_pcm();
|
||||||
if (block_start>=pcm.get_length()) i->m_finished=true;
|
const sample &target_pcm=m_target.get_block(i->m_tgt_index).get_pcm();
|
||||||
} else { // block is midway through buffer
|
// get the sample offset into the buffer
|
||||||
block_start=0;
|
s32 offset = i->m_time-m_render_time;
|
||||||
buffer_start=offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
// cerr<<"-----------------"<<endl;
|
|
||||||
// cerr<<"block start:"<<block_start<<endl;
|
|
||||||
// cerr<<"buffer start:"<<buffer_start<<endl;
|
|
||||||
|
|
||||||
if (!i->m_finished) {
|
|
||||||
// mix in
|
|
||||||
u32 buffer_pos = buffer_start;
|
|
||||||
u32 block_pos = block_start;
|
|
||||||
u32 block_end = pcm.get_length();
|
|
||||||
|
|
||||||
|
|
||||||
while (block_pos<block_end && buffer_pos<nframes) {
|
// assume midway through block
|
||||||
// mix with normalised version
|
u32 block_start = offset;
|
||||||
float brain_sample = (pcm[block_pos]*(1-m_n_mix)+
|
u32 buffer_start = 0;
|
||||||
n_pcm[block_pos]*m_n_mix);
|
if (offset<0) {
|
||||||
|
block_start=-offset;
|
||||||
// for mixing with target audio
|
if (block_start>=pcm.get_length() ||
|
||||||
float target_sample = target_pcm[block_pos];
|
i->m_position>=pcm.get_length()) i->m_finished=true;
|
||||||
|
} else { // block is midway through buffer
|
||||||
buf[buffer_pos]+=(brain_sample*(1-m_target_mix) +
|
block_start=0;
|
||||||
target_sample*m_target_mix)*0.2*m_volume;
|
buffer_start=offset;
|
||||||
++buffer_pos;
|
|
||||||
++block_pos;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cerr<<"-----------------"<<endl;
|
||||||
|
// cerr<<"block start:"<<block_start<<endl;
|
||||||
|
// cerr<<"buffer start:"<<buffer_start<<endl;
|
||||||
|
|
||||||
|
float pitch_scale = m_target.get_block(i->m_tgt_index).get_freq() /
|
||||||
|
m_source.get_block(i->m_index).get_freq();
|
||||||
|
|
||||||
|
// fade in/out autotune
|
||||||
|
pitch_scale = pitch_scale*m_autotune + 1.0f*(1-m_autotune);
|
||||||
|
|
||||||
|
//pitchshift::process(pcm,pitch_scale,render_pcm);
|
||||||
|
|
||||||
|
|
||||||
|
if (!i->m_finished) {
|
||||||
|
// mix in
|
||||||
|
u32 buffer_pos = buffer_start;
|
||||||
|
u32 block_pos = block_start;
|
||||||
|
u32 block_end = pcm.get_length();
|
||||||
|
|
||||||
|
|
||||||
|
while (block_pos<block_end && buffer_pos<nframes) {
|
||||||
|
// mix with normalised version
|
||||||
|
float brain_sample = (pcm[i->m_position]*(1-m_n_mix)+
|
||||||
|
n_pcm[block_pos]*m_n_mix);
|
||||||
|
|
||||||
|
// for mixing with target audio
|
||||||
|
float target_sample = target_pcm[block_pos];
|
||||||
|
|
||||||
|
buf[buffer_pos]+=(brain_sample*(1-m_target_mix) +
|
||||||
|
target_sample*m_target_mix)*0.2*m_volume;
|
||||||
|
|
||||||
|
i->m_position+=pitch_scale;
|
||||||
|
|
||||||
|
++buffer_pos;
|
||||||
|
++block_pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderer::clean_up() {
|
void renderer::clean_up() {
|
||||||
// cleanup phase
|
// cleanup phase
|
||||||
// delete old ones
|
// delete old ones
|
||||||
std::list<render_block>::iterator i=m_render_blocks.begin();
|
std::list<render_block>::iterator i=m_render_blocks.begin();
|
||||||
std::list<render_block>::iterator ni=m_render_blocks.begin();
|
std::list<render_block>::iterator ni=m_render_blocks.begin();
|
||||||
while(i!=m_render_blocks.end()) {
|
while(i!=m_render_blocks.end()) {
|
||||||
ni++;
|
ni++;
|
||||||
if (i->m_finished) m_render_blocks.erase(i);
|
if (i->m_finished) m_render_blocks.erase(i);
|
||||||
i=ni;
|
i=ni;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void renderer::old_process(u32 nframes, float *buf) {
|
|
||||||
if (!m_playing) return;
|
|
||||||
|
|
||||||
// get new blocks from source for the current buffer
|
|
||||||
u32 tgt_shift = m_target.get_block_size()-m_target.get_overlap();
|
|
||||||
u32 tgt_end = (m_target_time+nframes)/(float)tgt_shift;
|
|
||||||
|
|
||||||
if (tgt_shift!=m_last_tgt_shift ||
|
|
||||||
tgt_end>=m_target.get_num_blocks() || m_source.get_num_blocks()==0) {
|
|
||||||
reset();
|
|
||||||
m_last_tgt_shift = tgt_shift;
|
|
||||||
// next time...
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
cerr<<"-----------------"<<endl;
|
|
||||||
cerr<<"tgt start:"<<m_target_index<<endl;
|
|
||||||
cerr<<"tgt end:"<<tgt_end<<endl;
|
|
||||||
cerr<<":"<<tgt_end-m_target_index<<endl;
|
|
||||||
cerr<<"block time "<<m_target_index*tgt_shift<<endl;
|
|
||||||
cerr<<"render time "<<m_render_time<<endl;
|
|
||||||
cerr<<": "<<(s32)m_render_time-(s32)(m_target_index*tgt_shift)<<endl;
|
|
||||||
|
|
||||||
|
|
||||||
// cerr<<"-----------------"<<endl;
|
|
||||||
// cerr<<"tgt start:"<<m_target_index<<endl;
|
|
||||||
// cerr<<"tgt end:"<<tgt_end<<endl;
|
|
||||||
|
|
||||||
// get indices for current buffer
|
|
||||||
u32 counter = m_target_index;
|
|
||||||
//u32 cur_time = m_render_time;
|
|
||||||
while (counter<=tgt_end) {
|
|
||||||
u32 time=m_render_index*tgt_shift;
|
|
||||||
u32 src_index=0;
|
|
||||||
|
|
||||||
switch (m_search_algo) {
|
|
||||||
case BASIC:
|
|
||||||
src_index = m_source.search(m_target.get_block(m_target_index), m_search_params);
|
|
||||||
break;
|
|
||||||
case REV_BASIC:
|
|
||||||
src_index = m_source.rev_search(m_target.get_block(m_target_index), m_search_params);
|
|
||||||
break;
|
|
||||||
case SYNAPTIC:
|
|
||||||
case SYNAPTIC_SLIDE:
|
|
||||||
src_index = m_source.search_synapses(m_target.get_block(m_target_index), m_search_params);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_search_algo==SYNAPTIC_SLIDE) {
|
|
||||||
m_render_blocks.push_back(render_block(src_index,m_target_index,time));
|
|
||||||
|
|
||||||
if (m_source.get_current_error()<m_slide_error) {
|
|
||||||
m_target_index++;
|
|
||||||
}
|
|
||||||
else{ cerr<<"skip"<<endl; }
|
|
||||||
} else {
|
|
||||||
// put them in the index list
|
|
||||||
m_render_blocks.push_back(render_block(src_index,m_target_index,time));
|
|
||||||
m_target_index++;
|
|
||||||
}
|
|
||||||
counter++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// render all blocks in list
|
|
||||||
for (std::list<render_block>::iterator i=m_render_blocks.begin(); i!=m_render_blocks.end(); ++i) {
|
|
||||||
const sample &pcm=m_source.get_block(i->m_index).get_pcm();
|
|
||||||
const sample &n_pcm=m_source.get_block(i->m_index).get_n_pcm();
|
|
||||||
const sample &target_pcm=m_target.get_block(i->m_tgt_index).get_pcm();
|
|
||||||
// get the sample offset into the buffer
|
|
||||||
s32 offset = i->m_time-m_render_time;
|
|
||||||
|
|
||||||
// assume midway through block
|
|
||||||
u32 block_start = offset;
|
|
||||||
u32 buffer_start = 0;
|
|
||||||
if (offset<0) {
|
|
||||||
block_start=-offset;
|
|
||||||
if (block_start>=pcm.get_length()) i->m_finished=true;
|
|
||||||
} else { // block is midway through buffer
|
|
||||||
block_start=0;
|
|
||||||
buffer_start=offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
// cerr<<"-----------------"<<endl;
|
|
||||||
// cerr<<"block start:"<<block_start<<endl;
|
|
||||||
// cerr<<"buffer start:"<<buffer_start<<endl;
|
|
||||||
|
|
||||||
if (!i->m_finished) {
|
|
||||||
// mix in
|
|
||||||
u32 buffer_pos = buffer_start;
|
|
||||||
u32 block_pos = block_start;
|
|
||||||
u32 block_end = pcm.get_length();
|
|
||||||
|
|
||||||
|
|
||||||
while (block_pos<block_end && buffer_pos<nframes) {
|
|
||||||
// mix with normalised version
|
|
||||||
float brain_sample = (pcm[block_pos]*(1-m_n_mix)+
|
|
||||||
n_pcm[block_pos]*m_n_mix);
|
|
||||||
|
|
||||||
// for mixing with target audio
|
|
||||||
float target_sample = target_pcm[block_pos];
|
|
||||||
|
|
||||||
buf[buffer_pos]+=(brain_sample*(1-m_target_mix) +
|
|
||||||
target_sample*m_target_mix)*0.2*m_volume;
|
|
||||||
++buffer_pos;
|
|
||||||
++block_pos;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// delete old ones
|
|
||||||
std::list<render_block>::iterator i=m_render_blocks.begin();
|
|
||||||
std::list<render_block>::iterator ni=m_render_blocks.begin();
|
|
||||||
while(i!=m_render_blocks.end()) {
|
|
||||||
ni++;
|
|
||||||
if (i->m_finished) m_render_blocks.erase(i);
|
|
||||||
i=ni;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_render_time+=nframes;
|
|
||||||
m_target_time+=nframes;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool renderer::unit_test() {
|
bool renderer::unit_test() {
|
||||||
brain source;
|
brain source;
|
||||||
source.load_sound("test_data/up.wav");
|
source.load_sound("test_data/up.wav");
|
||||||
source.init(10,0,window::RECTANGLE);
|
source.init(10,0,window::RECTANGLE);
|
||||||
|
|
||||||
brain target;
|
brain target;
|
||||||
target.load_sound("test_data/up.wav");
|
target.load_sound("test_data/up.wav");
|
||||||
target.init(10,0,window::RECTANGLE);
|
target.init(10,0,window::RECTANGLE);
|
||||||
|
|
||||||
renderer rr(source,target);
|
renderer rr(source,target);
|
||||||
rr.set_playing(true);
|
rr.set_playing(true);
|
||||||
float *buf=new float[400];
|
float *buf=new float[400];
|
||||||
rr.process(10,buf);
|
rr.process(10,buf);
|
||||||
rr.process(10,buf);
|
rr.process(10,buf);
|
||||||
|
|
||||||
assert(rr.m_render_blocks.size()==2);
|
assert(rr.m_render_blocks.size()==2);
|
||||||
rr.process(10,buf);
|
rr.process(10,buf);
|
||||||
assert(rr.m_render_blocks.size()==2);
|
assert(rr.m_render_blocks.size()==2);
|
||||||
delete[] buf;
|
delete[] buf;
|
||||||
buf=new float[20];
|
buf=new float[20];
|
||||||
rr.process(20,buf);
|
rr.process(20,buf);
|
||||||
assert(rr.m_render_blocks.size()==3);
|
assert(rr.m_render_blocks.size()==3);
|
||||||
rr.process(5,buf);
|
rr.process(5,buf);
|
||||||
assert(rr.m_render_blocks.size()==1);
|
assert(rr.m_render_blocks.size()==1);
|
||||||
|
|
||||||
target.init(10,5,window::RECTANGLE);
|
target.init(10,5,window::RECTANGLE);
|
||||||
rr.process(10,buf);
|
rr.process(10,buf);
|
||||||
rr.process(10,buf);
|
rr.process(10,buf);
|
||||||
rr.process(10,buf);
|
rr.process(10,buf);
|
||||||
rr.process(10,buf);
|
rr.process(10,buf);
|
||||||
assert(rr.m_render_blocks.size()==4);
|
assert(rr.m_render_blocks.size()==4);
|
||||||
delete[] buf;
|
delete[] buf;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -23,19 +23,19 @@
|
|||||||
|
|
||||||
namespace spiralcore {
|
namespace spiralcore {
|
||||||
|
|
||||||
class renderer {
|
class renderer {
|
||||||
public:
|
public:
|
||||||
renderer(brain &source, brain &target) :
|
renderer(brain &source, brain &target) :
|
||||||
m_source(source),
|
m_source(source),
|
||||||
m_target(target),
|
m_target(target),
|
||||||
m_search_params(0,0,0,100,0)
|
m_search_params(0,0,0,100,0)
|
||||||
{ init(source,target); }
|
{ init(source,target); }
|
||||||
|
|
||||||
enum search_algo {
|
enum search_algo {
|
||||||
BASIC = 0,
|
BASIC = 0,
|
||||||
REV_BASIC,
|
REV_BASIC,
|
||||||
SYNAPTIC,
|
SYNAPTIC,
|
||||||
SYNAPTIC_SLIDE
|
SYNAPTIC_SLIDE
|
||||||
};
|
};
|
||||||
|
|
||||||
void init(brain &source, brain &target);
|
void init(brain &source, brain &target);
|
||||||
@ -58,7 +58,7 @@ renderer(brain &source, brain &target) :
|
|||||||
|
|
||||||
static bool unit_test();
|
static bool unit_test();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
bool find_render_blocks(u32 nframes);
|
bool find_render_blocks(u32 nframes);
|
||||||
void render(u32 nframes, float *buf);
|
void render(u32 nframes, float *buf);
|
||||||
@ -68,12 +68,17 @@ private:
|
|||||||
// realtime stuff
|
// realtime stuff
|
||||||
class render_block {
|
class render_block {
|
||||||
public:
|
public:
|
||||||
render_block(u32 index, u32 tgt_index, u32 time) :
|
render_block(u32 index, u32 tgt_index, u32 time) :
|
||||||
m_index(index), m_tgt_index(tgt_index), m_time(time), m_finished(false) {}
|
m_index(index),
|
||||||
u32 m_index;
|
m_tgt_index(tgt_index),
|
||||||
u32 m_tgt_index; // original target block
|
m_time(time),
|
||||||
u32 m_time; // in samples
|
m_finished(false),
|
||||||
bool m_finished;
|
m_position(0) {}
|
||||||
|
u32 m_index;
|
||||||
|
u32 m_tgt_index; // original target block
|
||||||
|
u32 m_time; // in samples
|
||||||
|
bool m_finished;
|
||||||
|
float m_position; // in samples
|
||||||
};
|
};
|
||||||
|
|
||||||
brain &m_source;
|
brain &m_source;
|
||||||
@ -89,13 +94,14 @@ private:
|
|||||||
u32 m_stretch;
|
u32 m_stretch;
|
||||||
float m_n_mix;
|
float m_n_mix;
|
||||||
float m_target_mix;
|
float m_target_mix;
|
||||||
|
float m_autotune;
|
||||||
|
|
||||||
search_algo m_search_algo;
|
search_algo m_search_algo;
|
||||||
double m_slide_error;
|
double m_slide_error;
|
||||||
u32 m_last_tgt_shift;
|
u32 m_last_tgt_shift;
|
||||||
|
|
||||||
std::list<render_block> m_render_blocks;
|
std::list<render_block> m_render_blocks;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user