colorchord/colorchord2/notefinder.c

383 lines
14 KiB
C
Raw Normal View History

2015-07-29 07:56:18 +02:00
//Copyright 2015 <>< Charles Lohr under the ColorChord License.
2015-01-07 04:51:39 +01:00
#include "notefinder.h"
#include "parameters.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "util.h"
#include "dft.h"
#include "filter.h"
#include "decompose.h"
#include "sort.h"
#include "DFT32.h"
2015-01-07 04:51:39 +01:00
struct NoteFinder * CreateNoteFinder( int spsRec )
{
struct NoteFinder * ret = malloc( sizeof( struct NoteFinder ) );
memset( ret, 0, sizeof( struct NoteFinder ) );
ret->sps_rec = spsRec;
//Set all default values before passing off to the parameter changer.
ret->octaves = 5;
ret->freqbins = 24;
ret->base_hz = 55;
ret->filter_strength = .5;
ret->filter_iter = 1;
ret->decompose_iterations = 1000;
ret->dft_speedup = 300;
ret->dft_q = 16;
2015-02-11 07:10:16 +01:00
ret->slope = 0.0;
ret->do_progressive_dft = 0;
2015-01-07 04:51:39 +01:00
ret->default_sigma = 1.4;
ret->note_jumpability = 2.5;
ret->note_combine_distance = 0.5;
ret->note_attach_freq_iir = 0.4;
ret->note_attach_amp_iir = 0.2;
ret->note_attach_amp_iir2 = 0.05;
ret->note_min_amplitude = 0.001;
ret->note_minimum_new_distribution_value = 0.02;
ret->note_out_chop = 0.1;
ret->dft_iir = 0.0;
ret->current_note_id = 1;
ret->amplify = 1;
ret->ofreqs = 0; //force a re-init.
RegisterValue( "octaves", PAINT, &ret->octaves, sizeof( ret->octaves ) );
RegisterValue( "freqbins", PAINT, &ret->freqbins, sizeof( ret->freqbins ) );
RegisterValue( "base_hz", PAFLOAT, &ret->base_hz, sizeof( ret->base_hz ) );
RegisterValue( "filter_strength", PAFLOAT, &ret->filter_strength, sizeof( ret->filter_strength ) );
RegisterValue( "filter_iter", PAINT, &ret->filter_iter, sizeof( ret->filter_iter ) );
RegisterValue( "decompose_iterations", PAINT, &ret->decompose_iterations, sizeof( ret->decompose_iterations ) );
RegisterValue( "amplify", PAFLOAT, &ret->amplify, sizeof( ret->amplify ) );
RegisterValue( "dft_speedup", PAFLOAT, &ret->dft_speedup, sizeof( ret->dft_speedup ) );
RegisterValue( "dft_q", PAFLOAT, &ret->dft_q, sizeof( ret->dft_q ) );
RegisterValue( "default_sigma", PAFLOAT, &ret->default_sigma, sizeof( ret->default_sigma ) );
RegisterValue( "note_jumpability", PAFLOAT, &ret->note_jumpability, sizeof( ret->note_jumpability ) );
RegisterValue( "note_combine_distance", PAFLOAT, &ret->note_combine_distance, sizeof( ret->note_combine_distance ) );
2015-02-11 07:10:16 +01:00
RegisterValue( "slope", PAFLOAT, &ret->slope, sizeof( ret->slope ) );
RegisterValue( "note_attach_freq_iir", PAFLOAT, &ret->note_attach_freq_iir, sizeof( ret->note_attach_freq_iir ) );
RegisterValue( "note_attach_amp_iir", PAFLOAT, &ret->note_attach_amp_iir, sizeof( ret->note_attach_amp_iir ) );
RegisterValue( "note_attach_amp_iir2", PAFLOAT, &ret->note_attach_amp_iir2, sizeof( ret->note_attach_amp_iir2 ) );
RegisterValue( "note_minimum_new_distribution_value", PAFLOAT, &ret->note_minimum_new_distribution_value, sizeof( ret->note_minimum_new_distribution_value ) );
RegisterValue( "note_out_chop", PAFLOAT, &ret->note_out_chop, sizeof( ret->note_out_chop ) );
RegisterValue( "dft_iir", PAFLOAT, &ret->dft_iir, sizeof( ret->dft_iir ) );
RegisterValue( "do_progressive_dft", PAINT, &ret->do_progressive_dft, sizeof( ret->do_progressive_dft ) );
2015-01-07 04:51:39 +01:00
AddCallback( "freqbins", ChangeNFParameters, ret );
AddCallback( "octaves", ChangeNFParameters, ret );
AddCallback( "base_hz", ChangeNFParameters, ret );
ChangeNFParameters( ret );
return ret;
}
void ChangeNFParameters( void * v )
{
//int ofreqs = ret->freqbins * ret->octaves;
int i;
struct NoteFinder * ret = (struct NoteFinder*)v;
/*
char t[128];
ret->octaves = atoi_del( GetParamStr( parameters, "octaves", setstr( ret->octaves, t ) ) );
ret->freqbins = atoi_del( GetParamStr( parameters, "freqbins", setstr( ret->freqbins, t ) ) );
ret->base_hz = atof_del( GetParamStr( parameters, "base_hz", setstr( ret->base_hz, t ) ) );
ret->filter_strength = atof_del( GetParamStr( parameters, "filter_strength", setstr( ret->filter_strength, t ) ) );
ret->filter_iter = atoi_del( GetParamStr( parameters, "filter_iter", setstr( ret->filter_iter, t ) ) );
ret->decompose_iterations = atoi_del( GetParamStr( parameters, "decompose_iterations", setstr( ret->decompose_iterations, t ) ) );
ret->dft_speedup = atof_del( GetParamStr( parameters, "dft_speedup", setstr( ret->dft_speedup, t ) ) );
ret->dft_q = atof_del( GetParamStr( parameters, "dft_q", setstr( ret->dft_q, t ) ) );
ret->default_sigma = atof_del( GetParamStr( parameters, "default_sigma", setstr( ret->default_sigma, t ) ) );
ret->note_jumpability = atof_del( GetParamStr( parameters, "note_jumpability", setstr( ret->note_jumpability, t ) ) );
ret->note_combine_distance = atof_del( GetParamStr( parameters, "note_combine_distance", setstr( ret->note_combine_distance, t ) ) );
ret->note_attach_freq_iir = atof_del( GetParamStr( parameters, "note_attach_freq_iir", setstr( ret->note_attach_freq_iir, t ) ) );
ret->note_attach_amp_iir = atof_del( GetParamStr( parameters, "note_attach_amp_iir", setstr( ret->note_attach_amp_iir, t ) ) );
ret->note_attach_amp_iir2 = atof_del( GetParamStr( parameters, "note_attach_amp_iir2", setstr( ret->note_attach_amp_iir2, t ) ) );
ret->note_min_amplitude = atof_del( GetParamStr( parameters, "note_min_amplitude", setstr( ret->note_min_amplitude, t ) ) );
ret->note_minimum_new_distribution_value = atof_del( GetParamStr( parameters, "note_minimum_new_distribution_value", setstr( ret->note_minimum_new_distribution_value, t ) ) );
ret->note_out_chop = atof_del( GetParamStr( parameters, "note_out_chop", setstr( ret->note_out_chop, t ) ) );
ret->dft_iir = atof_del( GetParamStr( parameters, "dft_iir", setstr( ret->dft_iir, t ) ) );
ret->sps_rec = atof_del( GetParamStr( parameters, "sps_rec", setstr( ret->sps_rec, t ) ) );
ret->amplify = atof_del( GetParamStr( parameters, "amplify", setstr( ret->amplify, t ) ) );
*/
printf( "%d %d %f %f %f\n", ret->freqbins, ret->octaves, ret->base_hz, ret->dft_q, ret->amplify );
int freqs = ret->freqbins * ret->octaves;
if( freqs != ret->ofreqs )
{
int note_peaks = ret->freqbins/2;
int maxdists = ret->freqbins/2;
ret->note_peaks = note_peaks;
if( ret->note_positions ) free( ret->note_positions );
ret->note_positions = calloc( 1, sizeof( float ) * note_peaks );
if( ret->note_amplitudes ) free( ret->note_amplitudes );
ret->note_amplitudes = calloc( 1, sizeof( float ) * note_peaks );
if( ret->note_amplitudes_out ) free( ret->note_amplitudes_out );
ret->note_amplitudes_out = calloc( 1, sizeof( float ) * note_peaks );
if( ret->note_amplitudes2 ) free( ret->note_amplitudes2 );
ret->note_amplitudes2 = calloc( 1, sizeof( float ) * note_peaks );
if( ret->note_peaks_to_dists_mapping ) free( ret->note_peaks_to_dists_mapping );
ret->note_peaks_to_dists_mapping = calloc( 1, sizeof( char ) * note_peaks );
if( ret->enduring_note_id ) free( ret->enduring_note_id );
ret->enduring_note_id = calloc( 1, sizeof( int ) * note_peaks );
if( ret->note_founds ) free( ret->note_founds );
ret->note_founds = calloc( 1, sizeof( unsigned char ) * note_peaks );
if( ret->frequencies ) free( ret->frequencies );
ret->frequencies = calloc( 1, sizeof( float ) * freqs );
if( ret->outbins ) free( ret->outbins );
ret->outbins = calloc( 1, sizeof( float ) * freqs );
if( ret->folded_bins ) free( ret->folded_bins );
ret->folded_bins = calloc( 1, sizeof( float ) * ret->freqbins );
if( ret->dist_amps ) free( ret->dist_amps );
ret->dist_amps = calloc( 1, sizeof( float ) * maxdists );
if( ret->dist_means ) free( ret->dist_means );
ret->dist_means = calloc( 1, sizeof( float ) * maxdists );
if( ret->dist_sigmas ) free( ret->dist_sigmas );
ret->dist_sigmas = calloc( 1, sizeof( float ) * maxdists );
if( ret->dist_takens ) free( ret->dist_takens );
ret->dist_takens = calloc( 1, sizeof( unsigned char ) * maxdists );
ret->ofreqs = freqs;
}
for( i = 0; i < freqs; i++ )
{
ret->frequencies[i] = ( ret->sps_rec / ret->base_hz ) / pow( 2, (float)i / ret->freqbins );
}
}
void RunNoteFinder( struct NoteFinder * nf, const float * audio_stream, int head, int buffersize )
{
int i, j;
int freqbins = nf->freqbins;
int note_peaks = freqbins/2;
int freqs = freqbins * nf->octaves;
int maxdists = freqbins/2;
float dftbins[freqs];
//Now, march onto the DFT, this pulls out the bins we're after.
//This DFT function does not wavelet or anything.
nf->StartTime = OGGetAbsoluteTime();
switch( nf->do_progressive_dft )
{
case 0:
DoDFTQuick( dftbins, nf->frequencies, freqs, audio_stream, head, buffersize, nf->dft_q, nf->dft_speedup );
break;
case 1:
DoDFTProgressive( dftbins, nf->frequencies, freqs, audio_stream, head, buffersize, nf->dft_q, nf->dft_speedup );
break;
case 2:
DoDFTProgressiveInteger( dftbins, nf->frequencies, freqs, audio_stream, head, buffersize, nf->dft_q, nf->dft_speedup );
break;
2015-02-11 07:10:16 +01:00
case 3:
DoDFTProgressiveIntegerSkippy( dftbins, nf->frequencies, freqs, audio_stream, head, buffersize, nf->dft_q, nf->dft_speedup );
break;
case 4:
DoDFTProgressive32( dftbins, nf->frequencies, freqs, audio_stream, head, buffersize, nf->dft_q, nf->dft_speedup );
break;
default:
fprintf( stderr, "Error: No DFT Seleced\n" );
}
2015-01-07 04:51:39 +01:00
nf->DFTTime = OGGetAbsoluteTime();
for( i = 0; i < freqs; i++ )
{
2015-02-11 07:10:16 +01:00
nf->outbins[i] = (nf->outbins[i] * (nf->dft_iir) + (dftbins[i] * (1.-nf->dft_iir) * nf->amplify * ( 1. + nf->slope * i )));
2015-01-07 04:51:39 +01:00
}
//Taper the first and last octaves.
for( i = 0; i < freqbins; i++ )
{
nf->outbins[i] *= (i+1.0)/nf->freqbins;
}
for( i = 0; i < freqbins; i++ )
{
nf->outbins[freqs-i-1] *= (i+1.0)/nf->freqbins;
}
//Combine the bins into folded bins.
for( i = 0; i < freqbins; i++ )
{
float amp = 0;
for( j = 0; j < nf->octaves; j++ )
{
amp += nf->outbins[i+j*freqbins];
}
nf->folded_bins[i] = amp;
}
//This is here to reduce the number of false-positive hits. It helps remove peaks that are meaningless.
2015-01-07 04:51:39 +01:00
FilterFoldedBinsBlob( nf->folded_bins, freqbins, nf->filter_strength, nf->filter_iter );
nf->FilterTime = OGGetAbsoluteTime();
memset( nf->dist_takens, 0, sizeof( unsigned char ) * maxdists );
nf->dists = DecomposeHistogram( nf->folded_bins, freqbins, nf->dist_means, nf->dist_amps, nf->dist_sigmas, maxdists, nf->default_sigma, nf->decompose_iterations );
{
int dist_sorts[nf->dists];
SortFloats( dist_sorts, nf->dist_amps, nf->dists );
RemapFloats( dist_sorts, nf->dist_amps, nf->dists );
RemapFloats( dist_sorts, nf->dist_means, nf->dists );
RemapFloats( dist_sorts, nf->dist_sigmas, nf->dists );
}
nf->DecomposeTime = OGGetAbsoluteTime();
//We now have the positions and amplitudes of the normal distributions that comprise our spectrum. IN SORTED ORDER!
//dists = # of distributions
//dist_amps[] = amplitudes of the normal distributions
//dist_means[] = positions of the normal distributions
//We need to use this in a filtered manner to obtain the "note" peaks
//note_peaks = total number of peaks.
//note_positions[] = position of the note on the scale.
//note_amplitudes[] = amplitudes of these note peaks.
memset( nf->note_founds, 0, sizeof( unsigned char ) * note_peaks );
//First try to find any close peaks.
for( i = 0; i < note_peaks; i++ )
{
for( j = 0; j < nf->dists; j++ )
{
2015-01-07 07:22:37 +01:00
if( !nf->dist_takens[j] && !nf->note_founds[i] && fabsloop( nf->note_positions[i], nf->dist_means[j], freqbins ) < nf->note_jumpability && nf->dist_amps[j] > 0.00001 ) //0.00001 for stability.
2015-01-07 04:51:39 +01:00
{
//Attach ourselves to this bin.
nf->note_peaks_to_dists_mapping[i] = j;
nf->dist_takens[j] = 1;
if( nf->enduring_note_id[i] == 0 )
nf->enduring_note_id[i] = nf->current_note_id++;
nf->note_founds[i] = 1;
2015-01-07 07:22:37 +01:00
2015-01-07 04:51:39 +01:00
nf->note_positions[i] = avgloop( nf->note_positions[i], (1.-nf->note_attach_freq_iir), nf->dist_means[j], nf->note_attach_freq_iir, nf->freqbins);
2015-01-07 07:22:37 +01:00
2015-01-07 04:51:39 +01:00
//I guess you can't IIR this like normal.
////note_positions[i] * (1.-note_attach_freq_iir) + dist_means[j] * note_attach_freq_iir;
nf->note_amplitudes[i] = nf->note_amplitudes[i] * (1.-nf->note_attach_amp_iir) + nf->dist_amps[j] * nf->note_attach_amp_iir;
//XXX TODO: Consider: Always boost power, never reduce?
// if( dist_amps[i] > note_amplitudes[i] )
// note_amplitudes[i] = dist_amps[i];
}
}
}
//Combine like-notes.
for( i = 0; i < note_peaks; i++ )
{
2015-01-07 07:22:37 +01:00
// printf( "%f %f %d\n", nf->note_amplitudes[i], nf->note_positions[i], nf->enduring_note_id[i] );
2015-01-07 04:51:39 +01:00
for( j = 0; j < note_peaks; j++ )
{
if( i == j ) continue;
if( fabsloop( nf->note_positions[i], nf->note_positions[j], nf->freqbins ) < nf->note_combine_distance &&
nf->note_amplitudes[i] > 0.0 &&
nf->note_amplitudes[j] > 0.0 )
{
int a;
int b;
if( nf->note_amplitudes[i] > nf->note_amplitudes[j] )
{
a = i;
b = j;
}
else
{
b = i;
a = j;
}
2015-01-07 07:22:37 +01:00
float newp = avgloop( nf->note_positions[a], nf->note_amplitudes[a], nf->note_positions[b], nf->note_amplitudes[b], freqbins );
2015-01-07 04:51:39 +01:00
//Combine B into A.
nf->note_amplitudes[a] += nf->note_amplitudes[b];
2015-01-07 07:22:37 +01:00
nf->note_positions[a] = newp;
2015-01-07 04:51:39 +01:00
nf->note_amplitudes[b] = 0;
nf->note_positions[b] = -100;
nf->enduring_note_id[b] = 0;
}
}
}
//Assign dead or decayed notes to new peaks.
for( i = 0; i < note_peaks; i++ )
{
if( nf->note_amplitudes[i] < nf->note_min_amplitude )
{
nf->enduring_note_id[i] = 0;
//Find a new peak for this note.
for( j = 0; j < nf->dists; j++ )
{
if( !nf->dist_takens[j] && nf->dist_amps[j] > nf->note_minimum_new_distribution_value )
{
nf->enduring_note_id[i] = nf->current_note_id++;
nf->dist_takens[j] = 1;
nf->note_amplitudes[i] = nf->dist_amps[j];//min_note_amplitude + dist_amps[j] * note_attach_amp_iir; //TODO: Should this jump?
nf->note_positions[i] = nf->dist_means[j];
nf->note_founds[i] = 1;
nf->dist_takens[j] = 1;
}
}
}
}
//Any remaining notes that could not find a peak good enough must be decayed to oblivion.
for( i = 0; i < note_peaks; i++ )
{
if( !nf->note_founds[i] )
{
nf->note_amplitudes[i] = nf->note_amplitudes[i] * (1.-nf->note_attach_amp_iir);
}
nf->note_amplitudes2[i] = nf->note_amplitudes2[i] * (1.-nf->note_attach_amp_iir2) + nf->note_amplitudes[i] * nf->note_attach_amp_iir2;
if( nf->note_amplitudes2[i] < nf->note_min_amplitude )
2015-01-07 04:51:39 +01:00
{
nf->note_amplitudes[i] = 0;
nf->note_amplitudes2[i] = 0;
}
}
for( i = 0; i < note_peaks; i++ )
{
nf->note_amplitudes_out[i] = nf->note_amplitudes[i] - nf->note_out_chop;
if( nf->note_amplitudes_out[i] < 0 )
{
nf->note_amplitudes_out[i] = 0;
}
}
//We now have our "notes"
//Notes are made of "note_amplitudes" and "note_positions" in the scale to 0..freqbins
//They are stable but in an arbitrary order.
nf->FinalizeTime = OGGetAbsoluteTime();
}