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-08 06:24:02 -03:00
|
|
|
#include <iostream>
|
2015-07-27 11:36:34 -03:00
|
|
|
#include <algorithm>
|
2015-07-08 06:24:02 -03:00
|
|
|
#include <sndfile.h>
|
2015-07-21 18:28:48 -03:00
|
|
|
#include <float.h>
|
2015-07-18 20:34:56 -03:00
|
|
|
#include <jellyfish/audio.h>
|
2015-07-08 06:24:02 -03:00
|
|
|
#include "brain.h"
|
2015-07-27 11:36:34 -03:00
|
|
|
#include "status.h"
|
2015-07-08 06:24:02 -03:00
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
using namespace spiralcore;
|
|
|
|
|
|
2015-07-27 11:36:34 -03:00
|
|
|
brain::brain() :
|
|
|
|
|
m_current_block_index(0),
|
|
|
|
|
m_average_error(0),
|
|
|
|
|
m_current_error(0),
|
|
|
|
|
m_usage_falloff(0.9)
|
2015-07-08 06:24:02 -03:00
|
|
|
{
|
2015-07-27 11:36:34 -03:00
|
|
|
status::update("brain ready...");
|
2015-07-08 06:24:02 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// load, chop up and add to brain
|
|
|
|
|
// todo: add tags
|
2015-07-11 17:30:16 -03:00
|
|
|
void brain::load_sound(std::string filename) {
|
2015-07-08 06:24:02 -03:00
|
|
|
SF_INFO sfinfo;
|
|
|
|
|
sfinfo.format=0;
|
|
|
|
|
SNDFILE* f=sf_open(filename.c_str(), SFM_READ, &sfinfo);
|
2015-07-11 17:30:16 -03:00
|
|
|
if (f!=NULL) {
|
|
|
|
|
sample s(sfinfo.frames);
|
|
|
|
|
sf_readf_float(f, s.get_non_const_buffer(), s.get_length());
|
|
|
|
|
m_samples.push_back(sound(filename,s));
|
2015-07-27 11:36:34 -03:00
|
|
|
status::update("loaded %s",filename.c_str());
|
2015-07-11 17:30:16 -03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void brain::delete_sound(std::string filename) {
|
|
|
|
|
for (std::list<sound>::iterator i=m_samples.begin(); i!=m_samples.end(); ++i) {
|
|
|
|
|
if (i->m_filename==filename) {
|
|
|
|
|
m_samples.erase(i);
|
2015-07-27 11:36:34 -03:00
|
|
|
status::update("deleted %s",filename.c_str());
|
2015-07-11 17:30:16 -03:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-07-08 06:24:02 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// rewrites whole brain
|
2015-07-18 20:34:56 -03:00
|
|
|
void brain::init(u32 block_size, u32 overlap, window::type t, bool ditchpcm) {
|
2015-07-08 06:24:02 -03:00
|
|
|
m_blocks.clear();
|
2015-07-08 06:52:30 -03:00
|
|
|
m_block_size = block_size;
|
|
|
|
|
m_overlap = overlap;
|
2015-07-18 20:34:56 -03:00
|
|
|
m_window.init(block_size);
|
|
|
|
|
m_window.set_current_type(t);
|
2015-07-27 11:36:34 -03:00
|
|
|
u32 count=0;
|
2015-07-11 17:30:16 -03:00
|
|
|
for (std::list<sound>::iterator i=m_samples.begin(); i!=m_samples.end(); ++i) {
|
2015-07-27 11:36:34 -03:00
|
|
|
count++;
|
|
|
|
|
chop_and_add(i->m_sample, count, ditchpcm);
|
2015-07-08 06:24:02 -03:00
|
|
|
}
|
2015-07-27 11:36:34 -03:00
|
|
|
status::update("all samples processed");
|
2015-07-08 06:24:02 -03:00
|
|
|
}
|
|
|
|
|
|
2015-07-27 11:36:34 -03:00
|
|
|
void brain::chop_and_add(const sample &s, u32 count, bool ditchpcm) {
|
2015-07-08 06:24:02 -03:00
|
|
|
u32 pos=0;
|
2015-07-18 20:34:56 -03:00
|
|
|
if (m_overlap>=m_block_size) m_overlap=0;
|
|
|
|
|
while (pos+m_block_size-1<s.get_length()) {
|
2015-07-27 11:36:34 -03:00
|
|
|
status::update("processing sample %d: %d%%",count,(int)(pos/(float)s.get_length()*100));
|
2015-07-08 06:24:02 -03:00
|
|
|
sample region;
|
2015-07-18 20:34:56 -03:00
|
|
|
s.get_region(region,pos,pos+m_block_size-1);
|
|
|
|
|
m_blocks.push_back(block("",region,44100,m_window,ditchpcm));
|
|
|
|
|
pos += (m_block_size-m_overlap);
|
2015-07-08 06:24:02 -03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-09 17:48:59 -03:00
|
|
|
const block &brain::get_block(u32 index) const {
|
2015-07-09 12:16:36 -03:00
|
|
|
return m_blocks[index];
|
|
|
|
|
}
|
2015-07-08 06:52:30 -03:00
|
|
|
|
2015-07-27 11:36:34 -03:00
|
|
|
static const double usage_factor = 1000000;
|
|
|
|
|
|
2015-07-08 06:24:02 -03:00
|
|
|
// returns index to block
|
2015-07-27 11:36:34 -03:00
|
|
|
u32 brain::search(const block &target, const search_params ¶ms) {
|
2015-07-21 18:28:48 -03:00
|
|
|
double closest = FLT_MAX;
|
2015-07-08 06:24:02 -03:00
|
|
|
u32 closest_index = 0;
|
|
|
|
|
u32 index = 0;
|
2015-07-09 17:48:59 -03:00
|
|
|
for (vector<block>::const_iterator i=m_blocks.begin(); i!=m_blocks.end(); ++i) {
|
2015-07-11 08:29:51 -03:00
|
|
|
double diff = target.compare(*i,params);
|
2015-07-08 06:24:02 -03:00
|
|
|
if (diff<closest) {
|
|
|
|
|
closest=diff;
|
|
|
|
|
closest_index = index;
|
|
|
|
|
}
|
|
|
|
|
++index;
|
|
|
|
|
}
|
2015-07-27 11:36:34 -03:00
|
|
|
deplete_usage();
|
|
|
|
|
m_blocks[closest_index].get_usage()+=usage_factor;
|
2015-07-08 06:24:02 -03:00
|
|
|
return closest_index;
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-21 18:28:48 -03:00
|
|
|
// returns index to block
|
2015-07-27 11:36:34 -03:00
|
|
|
u32 brain::rev_search(const block &target, const search_params ¶ms) {
|
2015-07-21 18:28:48 -03:00
|
|
|
double furthest = 0;
|
|
|
|
|
u32 furthest_index = 0;
|
|
|
|
|
u32 index = 0;
|
|
|
|
|
for (vector<block>::const_iterator i=m_blocks.begin(); i!=m_blocks.end(); ++i) {
|
|
|
|
|
double diff = target.compare(*i,params);
|
|
|
|
|
if (diff>furthest) {
|
|
|
|
|
furthest=diff;
|
|
|
|
|
furthest_index = index;
|
|
|
|
|
}
|
|
|
|
|
++index;
|
|
|
|
|
}
|
2015-07-27 11:36:34 -03:00
|
|
|
|
|
|
|
|
deplete_usage();
|
|
|
|
|
m_blocks[furthest_index].get_usage()+=usage_factor;
|
|
|
|
|
|
2015-07-21 18:28:48 -03:00
|
|
|
return furthest_index;
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-27 11:36:34 -03:00
|
|
|
double brain::calc_average_diff(search_params ¶ms) {
|
|
|
|
|
double diff=0;
|
|
|
|
|
for (vector<block>::const_iterator i=m_blocks.begin(); i!=m_blocks.end(); ++i) {
|
|
|
|
|
for (vector<block>::const_iterator j=m_blocks.begin(); j!=m_blocks.end(); ++j) {
|
|
|
|
|
diff += j->compare(*i,params);
|
|
|
|
|
}
|
|
|
|
|
diff/=(double)m_blocks.size();
|
|
|
|
|
}
|
|
|
|
|
return diff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void brain::build_synapses(search_params ¶ms, double thresh) {
|
|
|
|
|
m_average_error = calc_average_diff(params)*thresh;
|
|
|
|
|
double err=m_average_error*thresh;
|
|
|
|
|
u32 brain_size = m_blocks.size();
|
|
|
|
|
u32 outer_index=0;
|
|
|
|
|
for (vector<block>::iterator i=m_blocks.begin(); i!=m_blocks.end(); ++i) {
|
|
|
|
|
u32 index = 0;
|
|
|
|
|
status::update("building synapses %d%%",(int)(outer_index/(float)brain_size*100));
|
|
|
|
|
for (vector<block>::const_iterator j=m_blocks.begin(); j!=m_blocks.end(); ++j) {
|
|
|
|
|
if (index!=outer_index) {
|
|
|
|
|
double diff = i->compare(*j,params);
|
|
|
|
|
if (diff<err) {
|
|
|
|
|
i->get_synapse().push_back(index);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
++index;
|
|
|
|
|
}
|
|
|
|
|
++outer_index;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
u32 brain::search_synapses(const block &target, search_params ¶ms) {
|
|
|
|
|
const block ¤t = get_block(m_current_block_index);
|
|
|
|
|
double closest = DBL_MAX;
|
|
|
|
|
u32 closest_index = 0;
|
|
|
|
|
// find nearest in synaptic connections
|
|
|
|
|
|
|
|
|
|
// cerr<<"searching "<<current.get_synapse_const().size()<<" connections"<<endl;
|
|
|
|
|
for (vector<u32>::const_iterator i=current.get_synapse_const().begin();
|
|
|
|
|
i!=current.get_synapse_const().end(); ++i) {
|
|
|
|
|
const block &other = get_block(*i);
|
|
|
|
|
double diff = target.compare(other,params);
|
|
|
|
|
if (diff<closest) {
|
|
|
|
|
closest=diff;
|
|
|
|
|
closest_index = *i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
deplete_usage();
|
|
|
|
|
m_blocks[m_current_block_index].get_usage()+=usage_factor;
|
|
|
|
|
m_current_error = closest;
|
|
|
|
|
|
|
|
|
|
if (closest_index!=0) {
|
|
|
|
|
//cerr<<"usage:"<<m_blocks[closest_index].get_usage()<<endl;
|
|
|
|
|
m_current_block_index = closest_index;
|
|
|
|
|
}
|
|
|
|
|
return m_current_block_index;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void brain::deplete_usage() {
|
|
|
|
|
for (vector<block>::iterator i=m_blocks.begin(); i!=m_blocks.end(); ++i) {
|
|
|
|
|
i->get_usage()*=m_usage_falloff;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2015-07-08 06:24:02 -03:00
|
|
|
// take another brain and rebuild this brain from bits of that one
|
|
|
|
|
// (presumably this one is made from a single sample)
|
2015-07-22 11:32:36 -03:00
|
|
|
/*void brain::resynth(const string &filename, const brain &other, const search_params ¶ms){
|
2015-07-08 11:06:08 -03:00
|
|
|
sample out((m_block_size-m_overlap)*m_blocks.size());
|
|
|
|
|
out.zero();
|
|
|
|
|
u32 pos = 0;
|
|
|
|
|
u32 count = 0;
|
2015-07-09 12:16:36 -03:00
|
|
|
cerr<<other.m_blocks.size()<<" brain blocks..."<<endl;
|
2015-07-09 19:54:05 -03:00
|
|
|
cerr<<endl;
|
2015-07-09 17:48:59 -03:00
|
|
|
for (vector<block>::iterator i=m_blocks.begin(); i!=m_blocks.end(); ++i) {
|
2015-07-09 12:16:36 -03:00
|
|
|
cerr<<'\r';
|
|
|
|
|
cerr<<"searching: "<<count/float(m_blocks.size())*100;
|
2015-07-11 08:29:51 -03:00
|
|
|
u32 index = other.search(*i, params);
|
2015-07-09 12:16:36 -03:00
|
|
|
//cerr<<index<<endl;
|
|
|
|
|
out.mul_mix(other.get_block_pcm(index),pos,0.2);
|
2015-07-08 11:06:08 -03:00
|
|
|
|
|
|
|
|
if (count%1000==0) {
|
2015-07-18 20:34:56 -03:00
|
|
|
audio_device::save_sample(filename,out);
|
2015-07-08 11:06:08 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
++count;
|
|
|
|
|
pos += (m_block_size-m_overlap);
|
2015-07-08 06:52:30 -03:00
|
|
|
}
|
2015-07-18 20:34:56 -03:00
|
|
|
audio_device::save_sample(filename,out);
|
2015-07-08 06:24:02 -03:00
|
|
|
}
|
2015-07-22 11:32:36 -03:00
|
|
|
*/
|
2015-07-08 06:24:02 -03:00
|
|
|
|
|
|
|
|
bool brain::unit_test() {
|
|
|
|
|
brain b;
|
|
|
|
|
assert(b.m_samples.size()==0);
|
|
|
|
|
assert(b.m_blocks.size()==0);
|
|
|
|
|
|
2015-07-11 17:30:16 -03:00
|
|
|
b.load_sound("test_data/100f32.wav");
|
|
|
|
|
b.load_sound("test_data/100i16.wav");
|
2015-07-08 06:24:02 -03:00
|
|
|
assert(b.m_samples.size()==2);
|
2015-07-18 20:34:56 -03:00
|
|
|
|
|
|
|
|
b.init(10, 0, window::RECTANGLE);
|
2015-07-08 06:24:02 -03:00
|
|
|
assert(b.m_blocks.size()==20);
|
2015-07-18 20:34:56 -03:00
|
|
|
b.init(10, 5, window::RECTANGLE);
|
2015-07-08 06:24:02 -03:00
|
|
|
assert(b.m_samples.size()==2);
|
|
|
|
|
assert(b.m_blocks.size()==38);
|
2015-07-18 20:34:56 -03:00
|
|
|
b.init(20, 5, window::RECTANGLE);
|
2015-07-08 06:24:02 -03:00
|
|
|
assert(b.m_samples.size()==2);
|
|
|
|
|
assert(b.m_blocks.size()==12);
|
|
|
|
|
|
2015-07-18 20:34:56 -03:00
|
|
|
|
2015-07-08 11:06:08 -03:00
|
|
|
// replicate brains
|
2015-07-08 06:24:02 -03:00
|
|
|
brain b2;
|
2015-07-08 11:06:08 -03:00
|
|
|
b2.load_sound("test_data/up.wav");
|
|
|
|
|
brain b3;
|
|
|
|
|
b3.load_sound("test_data/up.wav");
|
|
|
|
|
|
2015-07-18 20:34:56 -03:00
|
|
|
b2.init(512, 0, window::BLACKMAN);
|
|
|
|
|
b3.init(512, 0, window::BLACKMAN);
|
2015-07-11 08:29:51 -03:00
|
|
|
|
2015-07-27 11:36:34 -03:00
|
|
|
search_params p(1,0,0,100,0);
|
2015-07-11 08:29:51 -03:00
|
|
|
|
|
|
|
|
assert(b3.search(b2.m_blocks[0],p)==0);
|
|
|
|
|
assert(b3.search(b2.m_blocks[9],p)==9);
|
|
|
|
|
assert(b3.search(b2.m_blocks[19],p)==19);
|
|
|
|
|
assert(b3.search(b2.m_blocks[29],p)==29);
|
2015-07-08 11:06:08 -03:00
|
|
|
|
|
|
|
|
// sample r = b2.resynth(b,1);
|
|
|
|
|
// assert(r.get_length()==200);
|
2015-07-08 06:24:02 -03:00
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|