samplebrain/samplebrain/src/block.cpp

248 lines
6.3 KiB
C++
Raw Normal View History

2015-07-21 10:58:13 -03:00
// 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.
2015-07-09 17:50:03 -03:00
#include <assert.h>
2015-07-21 14:13:39 -03:00
#include <float.h>
2015-07-09 17:50:03 -03:00
#include <iostream>
#include "block.h"
using namespace spiralcore;
FFT *block::m_fftw;
Aquila::Mfcc *block::m_mfcc_proc;
static const int MFCC_FILTERS=12;
2015-07-21 14:13:39 -03:00
void normalise(sample &in) {
// find min/max
float max = 0;
float min = FLT_MAX;
for (u32 i=0; i<in.get_length(); ++i) {
if (in[i]<min) min=in[i];
if (in[i]>max) max=in[i];
}
float mid = min+(max-min)/2.0f;
// remove dc
for (u32 i=0; i<in.get_length(); ++i) {
in[i]-=mid;
}
min-=mid;
max-=mid;
// squash so biggest value is 1 or -1
float div = fabs(min);
if (div<max) div=max;
div=1/div;
for (u32 i=0; i<in.get_length(); ++i) {
in[i]*=div;
}
}
2015-07-09 17:50:03 -03:00
2015-07-18 20:34:56 -03:00
block::block(const string &filename, const sample &pcm, u32 rate, const window &w, bool ditchpcm) :
2015-07-09 17:50:03 -03:00
m_pcm(pcm),
m_fft(pcm.get_length()),
m_mfcc(MFCC_FILTERS),
2015-07-21 14:13:39 -03:00
m_n_pcm(pcm),
m_n_fft(pcm.get_length()),
m_n_mfcc(MFCC_FILTERS),
2015-07-09 17:50:03 -03:00
m_block_size(pcm.get_length()),
m_rate(rate),
m_orig_filename(filename)
{
init_fft(m_pcm.get_length());
assert(m_mfcc_proc!=NULL);
assert(m_fftw!=NULL);
2015-07-18 20:34:56 -03:00
w.run(m_pcm);
2015-07-21 14:13:39 -03:00
process(m_pcm,m_fft,m_mfcc);
2015-07-09 17:50:03 -03:00
2015-07-21 14:13:39 -03:00
// rerun the normalised version
normalise(m_n_pcm);
process(m_n_pcm,m_n_fft,m_n_mfcc);
2015-07-09 17:50:03 -03:00
2015-07-21 14:13:39 -03:00
if (ditchpcm) {
m_pcm.clear();
m_n_pcm.clear();
}
}
2015-07-09 17:50:03 -03:00
2015-07-21 14:13:39 -03:00
void block::init_fft(u32 block_size)
{
if (m_fftw == NULL || m_fftw->m_length!=block_size) {
if (m_fftw == NULL) delete m_fftw;
m_fftw = new FFT(block_size,100);
2015-07-21 14:13:39 -03:00
if (m_mfcc_proc == NULL) delete m_mfcc_proc;
m_mfcc_proc = new Aquila::Mfcc(block_size);
}
}
void block::process(const sample &pcm, sample &fft, sample &mfcc) {
m_fftw->impulse2freq(pcm.get_buffer());
m_fftw->calculate_bins();
2015-07-21 14:13:39 -03:00
// calculate fft
std::vector<std::complex<double> > mfspec;
for (u32 i=0; i<m_block_size; ++i) {
2015-07-09 17:50:03 -03:00
mfspec.push_back(std::complex<double>(m_fftw->m_spectrum[i][0],
m_fftw->m_spectrum[i][1]));
}
u32 fft_size = m_block_size;
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];
}
2015-07-09 17:50:03 -03:00
// calculate mfcc
std::vector<double> m = m_mfcc_proc->calculate(mfspec,MFCC_FILTERS);
for (u32 i=0; i<MFCC_FILTERS; ++i) {
2015-07-21 14:13:39 -03:00
mfcc[i] = m[i];
2015-07-09 17:50:03 -03:00
}
}
2015-07-09 19:54:05 -03:00
#define FFT_BIAS 200
2015-07-21 14:13:39 -03:00
double block::_compare(const sample &fft_a, const sample &mfcc_a,
const sample &fft_b, const sample &mfcc_b,
const search_params &params) const
{
2015-07-09 17:50:03 -03:00
double mfcc_acc=0;
double fft_acc=0;
2015-07-11 08:29:51 -03:00
s32 fft_start = params.m_fft1_start;
s32 fft_end = fmin(params.m_fft1_end,m_fft.get_length());
2015-07-09 20:11:12 -03:00
2015-07-11 08:29:51 -03:00
if (params.m_ratio==0) {
2015-07-09 20:11:12 -03:00
for (u32 i=fft_start; i<fft_end; ++i) {
2015-07-21 14:13:39 -03:00
fft_acc+=(fft_a[i]-fft_b[i]) * (fft_a[i]-fft_b[i]);
2015-07-09 17:50:03 -03:00
}
2015-07-21 14:13:39 -03:00
return (fft_acc/(float)fft_a.get_length())*FFT_BIAS;
2015-07-09 17:50:03 -03:00
}
2015-07-11 08:29:51 -03:00
if (params.m_ratio==1) {
2015-07-09 17:50:03 -03:00
for (u32 i=0; i<MFCC_FILTERS; ++i) {
2015-07-21 14:13:39 -03:00
mfcc_acc+=(mfcc_a[i]-mfcc_b[i]) * (mfcc_a[i]-mfcc_b[i]);
2015-07-09 17:50:03 -03:00
}
return mfcc_acc/(float)MFCC_FILTERS;
}
// calculate both
2015-07-09 20:11:12 -03:00
for (u32 i=fft_start; i<fft_end; ++i) {
2015-07-21 14:13:39 -03:00
fft_acc+=(fft_a[i]-fft_b[i]) * (fft_a[i]-fft_b[i]);
2015-07-09 17:50:03 -03:00
}
for (u32 i=0; i<MFCC_FILTERS; ++i) {
2015-07-21 14:13:39 -03:00
mfcc_acc+=(mfcc_a[i]-mfcc_b[i]) * (mfcc_a[i]-mfcc_b[i]);
2015-07-09 17:50:03 -03:00
}
2015-07-21 14:13:39 -03:00
return (fft_acc/(float)fft_a.get_length())*(1-params.m_ratio)*FFT_BIAS +
2015-07-11 08:29:51 -03:00
(mfcc_acc/(float)MFCC_FILTERS)*params.m_ratio;
2015-07-09 17:50:03 -03:00
}
2015-07-21 14:13:39 -03:00
double block::compare(const block &other, const search_params &params) const {
return _compare(m_fft, m_mfcc, other.m_fft, other.m_mfcc, params) * (1-params.m_n_ratio) +
_compare(m_n_fft, m_n_mfcc, other.m_n_fft, other.m_n_mfcc, params) * params.m_n_ratio;
}
2015-07-09 17:50:03 -03:00
bool block::unit_test() {
2015-07-21 14:13:39 -03:00
sample ntest(3);
u32 idx=0;
ntest[idx++]=-1;
ntest[idx++]=1;
ntest[idx++]=-1;
idx=0;
normalise(ntest);
assert(feq(ntest[idx++],-1));
assert(feq(ntest[idx++],1));
assert(feq(ntest[idx++],-1));
idx=0;
ntest[idx++]=-2;
ntest[idx++]=2;
ntest[idx++]=-2;
normalise(ntest);
idx=0;
assert(feq(ntest[idx++],-1));
assert(feq(ntest[idx++],1));
assert(feq(ntest[idx++],-1));
idx=0;
ntest[idx++]=19;
ntest[idx++]=20;
ntest[idx++]=19;
normalise(ntest);
idx=0;
assert(feq(ntest[idx++],-1));
assert(feq(ntest[idx++],1));
assert(feq(ntest[idx++],-1));
2015-07-09 17:50:03 -03:00
sample data(200);
for (u32 i=0; i<data.get_length(); i++) {
data[i]=i/(float)data.get_length();
}
2015-07-18 20:34:56 -03:00
window w;
w.init(data.get_length());
w.set_current_type(window::RECTANGLE);
2015-07-09 17:50:03 -03:00
2015-07-18 20:34:56 -03:00
block bb("test",data,44100,w);
2015-07-09 17:50:03 -03:00
assert(bb.m_pcm.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_orig_filename==string("test"));
assert(bb.m_rate==44100);
assert(bb.m_block_size==data.get_length());
2015-07-21 14:13:39 -03:00
search_params p(0,0,0,100,0,100);
2015-07-18 20:34:56 -03:00
block bb2("test",data,44100,w);
2015-07-11 08:29:51 -03:00
assert(bb.compare(bb2,p)==0);
p.m_ratio=1;
assert(bb.compare(bb2,p)==0);
p.m_ratio=0.5;
assert(bb.compare(bb2,p)==0);
2015-07-09 17:50:03 -03:00
sample data2(200);
for (u32 i=0; i<data.get_length(); i++) {
data[i]=i%10;
}
2015-07-18 20:34:56 -03:00
block cpy("test",data,100,w);
2015-07-09 17:50:03 -03:00
{
2015-07-18 20:34:56 -03:00
block bb3("test",data2,44100,w);
2015-07-11 08:29:51 -03:00
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);
2015-07-09 18:59:44 -03:00
cpy=bb3;
2015-07-09 17:50:03 -03:00
}
assert(cpy.m_pcm.get_length()==200);
2015-07-21 14:13:39 -03:00
2015-07-09 17:50:03 -03:00
return true;
}