Files
projectm/src/libprojectM/Renderer/BeatDetect.cpp
milkdropper 758348f25a Beat sensitivity implemented (#348)
* Beat detect sensitivity fix.

* Default: 1.0

* ReadMe beat sensitivity min/max.

* Beat sensitivity from config or settings isn't initialized.

* Smarter position.

* Make PCMScale (vol_history) also impacted by beat_sensitivity.

* Enhance and clarify Hard Cuts and better separate beat sensitivity.

* Forgot one config.

* Consistency with defaults.

* Even more clear about the default for Beat Sensitivity.
2020-05-15 17:49:28 +03:00

218 lines
6.4 KiB
C++
Executable File

/**
* projectM -- Milkdrop-esque visualisation SDK
* Copyright (C)2003-2004 projectM Team
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* See 'LICENSE.txt' included within this release
*
*/
/**
* Takes sound data from wherever and returns beat detection values
* Uses statistical Energy-Based methods. Very simple
*
* Some stuff was taken from Frederic Patin's beat-detection article,
* you'll find it online
*/
#include <stdlib.h>
#include <stdio.h>
#include "wipemalloc.h"
#include "Common.hpp"
#include "PCM.hpp"
#include <cmath>
#include "BeatDetect.hpp"
BeatDetect::BeatDetect(PCM *_pcm)
{
this->pcm=_pcm;
this->vol_instant=0;
this->vol_history=0;
for (unsigned y=0;y<BEAT_HISTORY_LENGTH;y++)
this->vol_buffer[y]=0;
this->beat_buffer_pos=0;
this->bass_instant = 0;
this->bass_history = 0;
for (unsigned y=0;y<BEAT_HISTORY_LENGTH;y++)
this->bass_buffer[y]=0;
this->mid_instant = 0;
this->mid_history = 0;
for (unsigned y=0;y<BEAT_HISTORY_LENGTH;y++)
this->mid_buffer[y]=0;
this->treb_instant = 0;
this->treb_history = 0;
for (unsigned y=0;y<BEAT_HISTORY_LENGTH;y++)
this->treb_buffer[y]=0;
this->treb = 0;
this->mid = 0;
this->bass = 0;
this->vol_old = 0;
this->beatSensitivity = 1.00;
this->treb_att = 0;
this->mid_att = 0;
this->bass_att = 0;
this->vol_att = 0;
this->vol = 0;
}
BeatDetect::~BeatDetect()
{
}
void BeatDetect::reset()
{
this->treb = 0;
this->mid = 0;
this->bass = 0;
this->treb_att = 0;
this->mid_att = 0;
this->bass_att = 0;
this->vol_att = 0;
this->vol_old = 0;
this->vol_instant=0;
}
void BeatDetect::detectFromSamples()
{
vol_old = vol;
bass=0;
mid=0;
treb=0;
vol=0;
// TODO: get sample rate from PCM? Assume 44100
getBeatVals(44100.0f, FFT_LENGTH, pcm->pcmdataL, pcm->pcmdataR);
}
float BeatDetect::getPCMScale()
{
// the constant here just depends on the particulars of getBeatVals(), the
// range of vol_history, and what "looks right".
// larger value means larger, more jagged waveform.
// this is also impacted by beatSensitivity.
return (1.5 / fmax(0.0001f,sqrtf(vol_history)))*beatSensitivity;
}
void BeatDetect::getBeatVals( float samplerate, unsigned fft_length, float *vdataL, float *vdataR )
{
assert( 512==fft_length || 1024==fft_length ); // should be power of 2, expect >= 512
// TODO: compute ranges based on samplerate
// roughly aiming or basee-mid cutoff of 220ish and mid-treb cutoff of 2000ish, if I did my math right
unsigned ranges512[4] = {0, 3 /* 3*441000/512 = =258 */, 23 /* 23*441000/512 = =1981 */ , 200};
unsigned ranges1024[4] = {0, 5 /* 5*44100/1024 == 215 */, 46 /* 46*44100/1024 == 1981 */, 400};
unsigned *ranges = fft_length==1024 ? ranges1024 : ranges512;
bass_instant = 0;
for (unsigned i=ranges[0]+1 ; i<=ranges[1] ; i++)
{
bass_instant += (vdataL[i*2]*vdataL[i*2]) + (vdataR[i*2]*vdataR[i*2]);
}
bass_instant *= 100.0/(ranges[1]-ranges[0]);
bass_history -= bass_buffer[beat_buffer_pos] * (1.0/BEAT_HISTORY_LENGTH);
bass_buffer[beat_buffer_pos] = bass_instant;
bass_history += bass_instant * (1.0/BEAT_HISTORY_LENGTH);
mid_instant = 0;
for (unsigned i=ranges[1]+1 ; i<=ranges[2] ; i++)
{
mid_instant += (vdataL[i*2]*vdataL[i*2]) + (vdataR[i*2]*vdataR[i*2]);
}
mid_instant *= 100.0/(ranges[2]-ranges[1]);
mid_history -= mid_buffer[beat_buffer_pos] * (1.0/BEAT_HISTORY_LENGTH);
mid_buffer[beat_buffer_pos] = mid_instant;
mid_history += mid_instant * (1.0/BEAT_HISTORY_LENGTH);
treb_instant = 0;
for (unsigned i=ranges[2]+1 ; i<=ranges[3] ; i++)
{
treb_instant += (vdataL[i*2]*vdataL[i*2]) + (vdataR[i*2]*vdataR[i*2]);
}
treb_instant *= 90.0/(ranges[3]-ranges[2]);
treb_history -= treb_buffer[beat_buffer_pos] * (1.0/BEAT_HISTORY_LENGTH);
treb_buffer[beat_buffer_pos] = treb_instant;
treb_history += treb_instant * (1.0/BEAT_HISTORY_LENGTH);
vol_instant = (bass_instant + mid_instant + treb_instant) / 3.0f;
vol_history -= (vol_buffer[beat_buffer_pos])* (1.0/BEAT_HISTORY_LENGTH);
vol_buffer[beat_buffer_pos] = vol_instant;
vol_history += vol_instant * (1.0/BEAT_HISTORY_LENGTH);
// fprintf(stderr, "%6.3f %6.2f %6.3f\n", bass_history/vol_history, mid_history/vol_history, treb_history/vol_history);
bass = bass_instant / fmax(0.0001, 1.3 * bass_history + 0.2*vol_history);
mid = mid_instant / fmax(0.0001, 1.3 * mid_history + 0.2*vol_history);
treb = treb_instant / fmax(0.0001, 1.3 * treb_history + 0.2*vol_history);
vol = vol_instant / fmax(0.0001, 1.5f * vol_history);
if ( projectM_isnan( treb ) ) {
treb = 0.0;
}
if ( projectM_isnan( mid ) ) {
mid = 0.0;
}
if ( projectM_isnan( bass ) ) {
bass = 0.0;
}
treb_att = .6f * treb_att + .4f * treb;
mid_att = .6f * mid_att + .4f * mid;
bass_att = .6f * bass_att + .4f * bass;
vol_att = .6f * vol_att + .4f * vol;
// Use beat sensitivity as a multiplier
// 0 is "dead"
// 5 is pretty wild so above that doesn't make sense
bass_att = bass_att * beatSensitivity;
bass = bass * beatSensitivity;
mid_att = mid_att * beatSensitivity;
mid = mid * beatSensitivity;
treb_att = treb_att * beatSensitivity;
treb = treb * beatSensitivity;
vol_att = vol_att * beatSensitivity;
vol = vol * beatSensitivity;
if (bass_att>100) bass_att=100;
if (bass >100) bass=100;
if (mid_att>100) mid_att=100;
if (mid >100) mid=100;
if (treb_att>100) treb_att=100;
if (treb >100) treb=100;
if (vol_att>100) vol_att=100;
if (vol>100) vol=100;
beat_buffer_pos++;
if (beat_buffer_pos>79)
beat_buffer_pos=0;
}