mirror of
https://gitlab.com/then-try-this/samplebrain.git
synced 2025-05-12 18:47:21 +00:00
rename + missing
This commit is contained in:
parent
d2b56e53e2
commit
cb1ca8e0e3
138
samplebrain/src/block.cpp
Normal file
138
samplebrain/src/block.cpp
Normal file
@ -0,0 +1,138 @@
|
||||
#include <assert.h>
|
||||
#include <iostream>
|
||||
|
||||
#include "libmfcc.h"
|
||||
#include "block.h"
|
||||
|
||||
using namespace spiralcore;
|
||||
|
||||
FFT *block::m_fftw;
|
||||
Aquila::Mfcc *block::m_mfcc_proc;
|
||||
|
||||
static const int MFCC_FILTERS=12;
|
||||
|
||||
void enveloper(sample &s, u32 start, u32 end) {
|
||||
for(u32 i=0; i<start; ++i) {
|
||||
s[i]*=i/(float)start;
|
||||
}
|
||||
for(u32 i=0; i<end; ++i) {
|
||||
s[(s.get_length()-1)-i]*=i/(float)end;
|
||||
}
|
||||
}
|
||||
|
||||
block::block(const string &filename, const sample &pcm, u32 rate, bool ditchpcm) :
|
||||
m_pcm(pcm),
|
||||
m_fft(pcm.get_length()),
|
||||
m_mfcc(MFCC_FILTERS),
|
||||
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);
|
||||
|
||||
enveloper(m_pcm,50,50);
|
||||
|
||||
m_fftw->impulse2freq(m_pcm.get_non_const_buffer());
|
||||
|
||||
std::vector<std::complex<double>> mfspec;
|
||||
|
||||
for (u32 i=0; i<m_block_size; ++i) {
|
||||
m_fft[i]=m_fftw->m_spectrum[i][0];
|
||||
|
||||
mfspec.push_back(std::complex<double>(m_fftw->m_spectrum[i][0],
|
||||
m_fftw->m_spectrum[i][1]));
|
||||
}
|
||||
|
||||
if (m_block_size>100) m_fft.crop_to(100);
|
||||
if (ditchpcm) m_pcm.clear();
|
||||
|
||||
// calculate mfcc
|
||||
std::vector<double> m = m_mfcc_proc->calculate(mfspec,MFCC_FILTERS);
|
||||
|
||||
for (u32 i=0; i<MFCC_FILTERS; ++i) {
|
||||
m_mfcc[i] = m[i];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
if (m_mfcc_proc == NULL) delete m_mfcc_proc;
|
||||
m_mfcc_proc = new Aquila::Mfcc(block_size);
|
||||
}
|
||||
}
|
||||
|
||||
double block::compare(const block &other, float ratio) const {
|
||||
double mfcc_acc=0;
|
||||
double fft_acc=0;
|
||||
|
||||
if (ratio==0) {
|
||||
for (u32 i=0; i<m_fft.get_length(); ++i) {
|
||||
fft_acc+=(m_fft[i]-other.m_fft[i]) * (m_fft[i]-other.m_fft[i]);
|
||||
}
|
||||
return fft_acc/(float)m_fft.get_length();
|
||||
}
|
||||
|
||||
if (ratio==1) {
|
||||
for (u32 i=0; i<MFCC_FILTERS; ++i) {
|
||||
mfcc_acc+=(m_mfcc[i]-other.m_mfcc[i]) * (m_mfcc[i]-other.m_mfcc[i]);
|
||||
}
|
||||
return mfcc_acc/(float)MFCC_FILTERS;
|
||||
}
|
||||
|
||||
// calculate both
|
||||
for (u32 i=0; i<m_fft.get_length(); ++i) {
|
||||
fft_acc+=(m_fft[i]-other.m_fft[i]) * (m_fft[i]-other.m_fft[i]);
|
||||
}
|
||||
for (u32 i=0; i<MFCC_FILTERS; ++i) {
|
||||
mfcc_acc+=(m_mfcc[i]-other.m_mfcc[i]) * (m_mfcc[i]-other.m_mfcc[i]);
|
||||
}
|
||||
|
||||
return (fft_acc/(float)m_fft.get_length())*(1-ratio) +
|
||||
(mfcc_acc/(float)MFCC_FILTERS)*ratio;
|
||||
}
|
||||
|
||||
|
||||
bool block::unit_test() {
|
||||
sample data(200);
|
||||
for (u32 i=0; i<data.get_length(); i++) {
|
||||
data[i]=i/(float)data.get_length();
|
||||
}
|
||||
|
||||
block bb("test",data,44100);
|
||||
|
||||
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());
|
||||
|
||||
block bb2("test",data,44100);
|
||||
assert(bb.compare(bb2,1)==0);
|
||||
assert(bb.compare(bb2,0)==0);
|
||||
assert(bb.compare(bb2,0.5)==0);
|
||||
|
||||
sample data2(200);
|
||||
for (u32 i=0; i<data.get_length(); i++) {
|
||||
data[i]=i%10;
|
||||
}
|
||||
|
||||
block cpy("test",data,100);
|
||||
{
|
||||
block bb3("test",data2,44100);
|
||||
assert(bb.compare(bb3,1)!=0);
|
||||
assert(bb.compare(bb3,0)!=0);
|
||||
assert(bb.compare(bb3,0.5)!=0);
|
||||
cpy=bb3;
|
||||
}
|
||||
|
||||
assert(cpy.m_pcm.get_length()==200);
|
||||
|
||||
return true;
|
||||
}
|
40
samplebrain/src/block.h
Normal file
40
samplebrain/src/block.h
Normal file
@ -0,0 +1,40 @@
|
||||
#include <string>
|
||||
#include "jellyfish/fluxa/sample.h"
|
||||
#include "jellyfish/core/types.h"
|
||||
#include "fft.h"
|
||||
#include "mfcc.h"
|
||||
|
||||
#ifndef BLOCK
|
||||
#define BLOCK
|
||||
|
||||
namespace spiralcore {
|
||||
|
||||
class block {
|
||||
public:
|
||||
// runs analysis on pcm
|
||||
block(const std::string &filename, const sample &pcm, u32 rate, bool ditchpcm=false);
|
||||
|
||||
// returns distance based on ratio of fft-mfcc values
|
||||
double compare(const block &other, float ratio) const;
|
||||
|
||||
static void init_fft(u32 block_size);
|
||||
static bool unit_test();
|
||||
|
||||
const sample &get_pcm() const { return m_pcm; }
|
||||
|
||||
private:
|
||||
sample m_pcm;
|
||||
sample m_fft;
|
||||
sample m_mfcc;
|
||||
|
||||
u32 m_block_size;
|
||||
u32 m_rate;
|
||||
std::string m_orig_filename;
|
||||
static FFT *m_fftw;
|
||||
static Aquila::Mfcc *m_mfcc_proc;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
35
samplebrain/src/mfcc.cpp
Normal file
35
samplebrain/src/mfcc.cpp
Normal file
@ -0,0 +1,35 @@
|
||||
/**
|
||||
* @file Mfcc.cpp
|
||||
*
|
||||
* Calculation of MFCC signal features.
|
||||
*
|
||||
* This file is part of the Aquila DSP library.
|
||||
* Aquila is free software, licensed under the MIT/X11 License. A copy of
|
||||
* the license is provided with the library in the LICENSE file.
|
||||
*
|
||||
* @package Aquila
|
||||
* @version 3.0.0-dev
|
||||
* @author Zbigniew Siciarz
|
||||
* @date 2007-2014
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @since 3.0.0
|
||||
*/
|
||||
|
||||
#include "mfcc.h"
|
||||
#include "aquila/transform/Dct.h"
|
||||
#include "aquila/filter/MelFilterBank.h"
|
||||
|
||||
namespace Aquila
|
||||
{
|
||||
std::vector<double> Mfcc::calculate(SpectrumType spectrum,
|
||||
std::size_t numFeatures)
|
||||
{
|
||||
//auto spectrum = m_fft->fft(source);
|
||||
|
||||
Aquila::MelFilterBank bank(44100, m_inputSize);
|
||||
auto filterOutput = bank.applyAll(spectrum);
|
||||
|
||||
Aquila::Dct dct;
|
||||
return dct.dct(filterOutput, numFeatures);
|
||||
}
|
||||
}
|
82
samplebrain/src/mfcc.h
Normal file
82
samplebrain/src/mfcc.h
Normal file
@ -0,0 +1,82 @@
|
||||
/**
|
||||
* @file Mfcc.h
|
||||
*
|
||||
* Calculation of MFCC signal features.
|
||||
*
|
||||
* This file is part of the Aquila DSP library.
|
||||
* Aquila is free software, licensed under the MIT/X11 License. A copy of
|
||||
* the license is provided with the library in the LICENSE file.
|
||||
*
|
||||
* @package Aquila
|
||||
* @version 3.0.0-dev
|
||||
* @author Zbigniew Siciarz
|
||||
* @date 2007-2014
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @since 3.0.0
|
||||
*/
|
||||
|
||||
#ifndef MFCC_H
|
||||
#define MFCC_H
|
||||
|
||||
#include "aquila/global.h"
|
||||
#include "aquila/transform/FftFactory.h"
|
||||
#include <jellyfish/fluxa/sample.h>
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
namespace Aquila
|
||||
{
|
||||
|
||||
/**
|
||||
* The Mfcc class implements calculation of MFCC features from input signal.
|
||||
*
|
||||
* MFCC coefficients are commonly used in speech recognition. The common
|
||||
* workflow is to split input signal in frames of equal length and apply
|
||||
* MFCC calculation to each frame individually. Hence a few assumptions
|
||||
* were made here:
|
||||
*
|
||||
* - a single Mfcc instance can be used only to process signals of equal
|
||||
* length, for example consecutive frames
|
||||
* - if you need to handle signals of various lengths, just create new
|
||||
* Mfcc object per each signal source
|
||||
*
|
||||
* The code below is a simplest possible example of how to calculate MFCC
|
||||
* for each frame of input signal.
|
||||
*
|
||||
* FramesCollection frames(data, FRAME_SIZE);
|
||||
* Mfcc mfcc(FRAME_SIZE);
|
||||
* for (Frame& frame : frames) {
|
||||
* auto mfccValues = mfcc.calculate(frame);
|
||||
* // do something with the calculated values
|
||||
* }
|
||||
*
|
||||
*/
|
||||
class AQUILA_EXPORT Mfcc
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor creates the FFT object to reuse between calculations.
|
||||
*
|
||||
* @param inputSize input length (common to all inputs)
|
||||
*/
|
||||
Mfcc(std::size_t inputSize):
|
||||
m_inputSize(inputSize)//, m_fft(FftFactory::getFft(inputSize))
|
||||
{
|
||||
}
|
||||
|
||||
std::vector<double> calculate(SpectrumType s, std::size_t numFeatures = 12);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Number of samples in each processed input.
|
||||
*/
|
||||
const std::size_t m_inputSize;
|
||||
|
||||
/**
|
||||
* FFT calculator.
|
||||
*/
|
||||
//std::shared_ptr<Fft> m_fft;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // MFCC_H
|
100
samplebrain/src/renderer.cpp
Normal file
100
samplebrain/src/renderer.cpp
Normal file
@ -0,0 +1,100 @@
|
||||
#include "renderer.h"
|
||||
#include <iostream>
|
||||
|
||||
using namespace spiralcore;
|
||||
using namespace std;
|
||||
|
||||
void renderer::init(brain &source, brain &target, float ratio) {
|
||||
m_source=source;
|
||||
m_target=target;
|
||||
m_render_time=0;
|
||||
m_render_blocks.clear();
|
||||
m_ratio = ratio;
|
||||
}
|
||||
|
||||
void renderer::process(u32 nframes, float *buf) {
|
||||
// get blocks from source for the current buffer
|
||||
u32 src_shift = m_source.get_block_size()-m_source.get_overlap();
|
||||
u32 tgt_shift = m_target.get_block_size()-m_target.get_overlap();
|
||||
|
||||
u32 tgt_start = m_render_time/(float)tgt_shift;
|
||||
u32 tgt_end = (m_render_time+nframes)/(float)tgt_shift;
|
||||
tgt_end++;
|
||||
|
||||
// get indices for current buffer
|
||||
for (u32 tgt_index = tgt_start; tgt_index<tgt_end; tgt_index++) {
|
||||
u32 time=tgt_index*tgt_shift;
|
||||
u32 src_index = m_source.search(m_target.get_block(tgt_index), m_ratio);
|
||||
// put them in the index list
|
||||
m_render_blocks.push_back(render_block(src_index,time));
|
||||
}
|
||||
|
||||
// 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_pcm(i->m_index);
|
||||
// get the sample offset into the buffer
|
||||
s32 offset = i->m_time-m_render_time;
|
||||
|
||||
// assume midway through block
|
||||
s32 block_start = offset;
|
||||
u32 buffer_start = 0;
|
||||
if (block_start<0) {
|
||||
block_start=-block_start;
|
||||
if (block_start>=pcm.get_length()) i->m_finished=true;
|
||||
} else { // block is midway through buffer
|
||||
block_start=0;
|
||||
buffer_start=offset;
|
||||
}
|
||||
|
||||
|
||||
if (!i->m_finished) {
|
||||
// mix in
|
||||
u32 buffer_pos = buffer_start;
|
||||
u32 block_pos = block_start;
|
||||
u32 block_end = block_start+pcm.get_length();
|
||||
while (block_pos<block_end && buffer_pos<nframes) {
|
||||
buf[buffer_pos]+=pcm[block_pos];
|
||||
++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;
|
||||
}
|
||||
|
||||
bool renderer::unit_test() {
|
||||
brain source;
|
||||
source.load_sound("test_data/up.wav");
|
||||
source.init(10,0);
|
||||
|
||||
brain target;
|
||||
target.load_sound("test_data/up.wav");
|
||||
target.init(10,0);
|
||||
|
||||
renderer rr(source,target,1);
|
||||
float *buf=new float[10];
|
||||
rr.process(5,buf);
|
||||
assert(rr.m_render_blocks.size()==1);
|
||||
rr.process(10,buf);
|
||||
assert(rr.m_render_blocks.size()==1);
|
||||
rr.process(20,buf);
|
||||
assert(rr.m_render_blocks.size()==2);
|
||||
rr.process(5,buf);
|
||||
assert(rr.m_render_blocks.size()==1);
|
||||
|
||||
target.init(10,5);
|
||||
rr.process(10,buf);
|
||||
assert(rr.m_render_blocks.size()==4);
|
||||
|
||||
|
||||
}
|
38
samplebrain/src/renderer.h
Normal file
38
samplebrain/src/renderer.h
Normal file
@ -0,0 +1,38 @@
|
||||
#include <list>
|
||||
#include <jellyfish/fluxa/sample.h>
|
||||
#include "brain.h"
|
||||
|
||||
namespace spiralcore {
|
||||
|
||||
class renderer {
|
||||
public:
|
||||
renderer(brain &source, brain &target, float ratio) :
|
||||
m_source(source), m_target(target)
|
||||
{ init(source,target,ratio); }
|
||||
|
||||
void init(brain &source, brain &target, float ratio);
|
||||
void process(u32 nframes, float *buf);
|
||||
|
||||
static bool unit_test();
|
||||
|
||||
private:
|
||||
|
||||
// realtime stuff
|
||||
class render_block {
|
||||
public:
|
||||
render_block(u32 index, u32 time) :
|
||||
m_index(index), m_time(time), m_finished(false) {}
|
||||
u32 m_index;
|
||||
u32 m_time; // in samples
|
||||
bool m_finished;
|
||||
};
|
||||
|
||||
brain &m_source;
|
||||
brain &m_target;
|
||||
float m_ratio;
|
||||
|
||||
std::list<render_block> m_render_blocks;
|
||||
u32 m_render_time;
|
||||
};
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user