mirror of
https://github.com/projectM-visualizer/projectm.git
synced 2026-03-05 06:55:20 +00:00
* 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.
218 lines
6.4 KiB
C++
Executable File
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;
|
|
}
|
|
|
|
|