mirror of
https://github.com/projectM-visualizer/projectm.git
synced 2026-03-01 04:55:03 +00:00
mood bar updates
- added very experimental ring buffer - new window size parameter git-svn-id: https://projectm.svn.sourceforge.net/svnroot/projectm/trunk@460 6778bc44-b910-0410-a7a0-be141de4315d
This commit is contained in:
@ -1,7 +1,7 @@
|
||||
PROJECT(projectM)
|
||||
ADD_LIBRARY(projectM SHARED projectM.cpp projectM.hpp PBuffer.cpp PBuffer.hpp InitCond.cpp InitCond.hpp
|
||||
Expr.cpp PCM.cpp Parser.cpp Preset.cpp Common.hpp BeatDetect.cpp PCM.hpp PerPixelEqn.cpp Eval.hpp
|
||||
Param.cpp CustomWave.cpp CustomShape.hpp CustomShape.cpp Param.hpp CustomWave.hpp BeatDetect.hpp console_interface.h
|
||||
RingBuffer.hpp Param.cpp CustomWave.cpp CustomShape.hpp CustomShape.cpp Param.hpp CustomWave.hpp BeatDetect.hpp console_interface.h
|
||||
Func.hpp Func.cpp Eval.cpp wipemalloc.h PerFrameEqn.cpp PerPointEqn.cpp fftsg.cpp console_interface.cpp
|
||||
CValue.hpp Expr.hpp timer.cpp wipemalloc.cpp PerFrameEqn.hpp PerPixelEqn.hpp PerPointEqn.hpp BuiltinFuncs.hpp
|
||||
BuiltinFuncs.cpp compare.h event.h fatal.h fftsg.h timer.h BuiltinParams.hpp BuiltinParams.cpp Preset.hpp Renderer.cpp Renderer.hpp ParamUtils.hpp
|
||||
|
||||
@ -2,8 +2,12 @@
|
||||
// C++ Implementation: MoodBar
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
//
|
||||
// Calculates moodbar values from spectrum frequency data. This code is shamelessly stolen
|
||||
// from the GPL'ed moodbar project / research by Gavin Wood. The biggest difference between
|
||||
// his implementation and mine is the adaptations for "real time" normalization. A ring buffer
|
||||
// is used to remember a fixed window of most recently observed rgb values. All such unnormalized
|
||||
// values are used to calculate a "stretched" normalized set of color intensities.
|
||||
|
||||
// Author: Carmelo Piccione <carmelo.piccione@gmail.com>, (C) 2007
|
||||
//
|
||||
// Copyright: See COPYING file that comes with this distribution
|
||||
@ -19,11 +23,43 @@ extern "C" {
|
||||
#include <cassert>
|
||||
#include "PCM.hpp"
|
||||
|
||||
|
||||
#define SPECTRUM_BAND_FREQ(band, size, rate) \
|
||||
(((float)(band))*((float)(rate))/((float)(size)))
|
||||
|
||||
|
||||
/// Bark band hard coded values stolen directly from moodbar implementation
|
||||
const unsigned int MoodBar::s_bark_bands[] =
|
||||
{ 100, 200, 300, 400, 510, 630, 770, 920,
|
||||
1080, 1270, 1480, 1720, 2000, 2320, 2700, 3150, 3700, 4400,
|
||||
5300, 6400, 7700, 9500, 12000, 15500 };
|
||||
|
||||
|
||||
void MoodBar::standardNormalize(float * rgb) {
|
||||
|
||||
float sum = 0;
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
sum += rgb[i];
|
||||
}
|
||||
|
||||
if (sum == 0)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
rgb[i] /= sum;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MoodBar::resetBuffer() {
|
||||
|
||||
for (int c = 0; c < 3; c++)
|
||||
for (unsigned int i = 0; i < RingBuffer<float>::RING_BUFFER_SIZE; i++)
|
||||
m_ringBuffers[c].append(.5f);
|
||||
|
||||
}
|
||||
|
||||
void MoodBar::calculateMood
|
||||
(float * rgb_left, float * rgb_right, float * rgb_avg) {
|
||||
|
||||
@ -35,37 +71,61 @@ void MoodBar::calculateMood
|
||||
for (i = 0; i < 24; ++i) {
|
||||
m_amplitudes_left[i] = 0.f;
|
||||
m_amplitudes_right[i] = 0.f;
|
||||
|
||||
}
|
||||
|
||||
for (i = 0; i < m_numFreqs; ++i)
|
||||
{
|
||||
real_left = m_pcm->vdataL[2*i]; imag_left = m_pcm->vdataL[2*i + 1];
|
||||
real_right = m_pcm->vdataR[2*i]; imag_right = m_pcm->vdataR[2*i + 1];
|
||||
//std::cerr << "vdataL[2*" << i << "] = " << m_pcm->vdataL[2*i] << std::endl;
|
||||
//std::cerr << "vdataL[2*" << i << "+1] = " << m_pcm->vdataL[2*i+1] << std::endl;
|
||||
|
||||
real_left = m_pcm->pcmdataL[2*i]; imag_left = m_pcm->pcmdataL[2*i + 1];
|
||||
real_right = m_pcm->pcmdataR[2*i]; imag_right = m_pcm->pcmdataR[2*i + 1];
|
||||
|
||||
m_amplitudes_left[m_barkband_table[i]] += sqrtf (real_left*real_left + imag_left*imag_left);
|
||||
m_amplitudes_right[m_barkband_table[i]] += sqrtf (real_right*real_right + imag_right*imag_right);
|
||||
}
|
||||
|
||||
for (i= 0; i < 3; i++) {
|
||||
rgb_left[i] = 0.0;
|
||||
rgb_right[i] = 0.0;
|
||||
}
|
||||
|
||||
for (i = 0; i < 24; ++i) {
|
||||
rgb_left[i/8] += m_amplitudes_left[i] * m_amplitudes_left[i];
|
||||
rgb_right[i/8] += m_amplitudes_right[i] * m_amplitudes_right[i];
|
||||
}
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
rgb_avg[i] = (rgb_left[i] + rgb_right[i]) / 2.0;
|
||||
rgb_avg[i] = sqrtf (rgb_avg[i]);
|
||||
|
||||
rgb_left[i] = sqrtf (rgb_left[i]);
|
||||
rgb_right[i] = sqrtf (rgb_right[i]);
|
||||
|
||||
rgb_avg[i] = (rgb_left[i] + rgb_right[i]) / 2;
|
||||
rgb_avg[i] = sqrtf (rgb_avg[i]);
|
||||
}
|
||||
|
||||
/// @bug verify normalized values
|
||||
std::cerr << "rgb_avg: " << rgb_avg[0] << "," << "," << rgb_avg[1] << "," << rgb_avg[2] << std::endl;
|
||||
|
||||
stretchNormalize(rgb_left);
|
||||
stretchNormalize(rgb_right);
|
||||
stretchNormalize(rgb_avg);
|
||||
|
||||
/// @bug verify normalized m_ringBuffer
|
||||
//standardNormalize(rgb_avg);
|
||||
//standardNormalize(rgb_left);
|
||||
//standardNormalize(rgb_right);
|
||||
|
||||
std::cerr << "rgb_avg: " << rgb_avg[0] << "," << rgb_avg[1] << "," << rgb_avg[2] << std::endl;
|
||||
|
||||
#ifdef ASSERT_MOODBAR
|
||||
for (i = 0; i < 3; i++) {
|
||||
assert(rgb_avg[i] <= 1.0);
|
||||
assert(rgb_avg[i] >= 0.0);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -88,12 +148,134 @@ MoodBar::calcBarkbandTable ()
|
||||
|
||||
for (i = 0; i < m_numFreqs; ++i)
|
||||
{
|
||||
if (barkband < 23 && 1)
|
||||
//(unsigned int) GST_SPECTRUM_BAND_FREQ (i, m_size, m_rate)
|
||||
// >= s_bark_bands[barkband])
|
||||
if (barkband < 23 &&
|
||||
(unsigned int) SPECTRUM_BAND_FREQ (i, m_size, m_rate)
|
||||
>= s_bark_bands[barkband])
|
||||
barkband++;
|
||||
|
||||
m_barkband_table[i] = barkband;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Copied and mod'ed from moodbar source code which also says the following:
|
||||
The normalization code was copied from Gav Wood's Exscalibar
|
||||
* library, normalise.cpp
|
||||
*/
|
||||
void MoodBar::stretchNormalize (float * rgb)
|
||||
{
|
||||
float mini, maxi, tu = 0.f, tb = 0.f;
|
||||
float avgu = 0.f, avgb = 0.f, delta, avg = 0.f;
|
||||
float avguu = 0.f, avgbb = 0.f;
|
||||
unsigned int i;
|
||||
int t = 0;
|
||||
|
||||
// iterate over r,g,b
|
||||
for (int c = 0; c < 3; c++) {
|
||||
|
||||
// Append latest un-normalized value on ring buffer
|
||||
m_ringBuffers[c].append(rgb[c]);
|
||||
|
||||
unsigned int numvals = RingBuffer<float>::RING_BUFFER_SIZE;
|
||||
|
||||
if (numvals == 0)
|
||||
return;
|
||||
|
||||
mini = maxi = m_ringBuffers[c].get();
|
||||
|
||||
// Compute max and min m_ringBuffer of the array
|
||||
for (i = 1; i < numvals; i++)
|
||||
{
|
||||
float _tmpval = m_ringBuffers[c].get();
|
||||
if (_tmpval > maxi)
|
||||
maxi = _tmpval;
|
||||
else if (_tmpval < mini)
|
||||
mini = _tmpval;
|
||||
}
|
||||
|
||||
// Compute array average excluding the maximum and minimum ranges
|
||||
for (i = 0; i < numvals; i++)
|
||||
{
|
||||
float _tmpval = m_ringBuffers[c].get();
|
||||
|
||||
if(_tmpval != mini && _tmpval != maxi)
|
||||
{
|
||||
avg += _tmpval / ((float) numvals);
|
||||
t++;
|
||||
}
|
||||
}
|
||||
|
||||
// Now compute average values if we partition the elements into
|
||||
// two sets segmented by the previously computed average
|
||||
// Again we exclude the max and min elements.
|
||||
for (i = 0; i < numvals; i++)
|
||||
{
|
||||
float _tmpval = m_ringBuffers[c].get();
|
||||
|
||||
if (_tmpval != mini && _tmpval != maxi)
|
||||
{
|
||||
if (_tmpval > avg)
|
||||
{
|
||||
avgu += _tmpval;
|
||||
tu++;
|
||||
}
|
||||
else
|
||||
{
|
||||
avgb += _tmpval;
|
||||
tb++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This normalizes the computations in the previous for loop
|
||||
// so they represent proper averages of their respective sets
|
||||
avgu /= (float) tu;
|
||||
avgb /= (float) tb;
|
||||
|
||||
tu = 0.f;
|
||||
tb = 0.f;
|
||||
|
||||
// Computes two averages. One of m_ringBuffer that are less than previously computer lower bound and
|
||||
// one of m_ringBuffer greater than the previously computed upper bound.
|
||||
// As usual, min and max elements are excluded.
|
||||
for (i = 0; i < numvals; i++) {
|
||||
float _tmpval = m_ringBuffers[c].get();
|
||||
|
||||
|
||||
if (_tmpval != mini && _tmpval != maxi)
|
||||
{
|
||||
if (_tmpval > avgu)
|
||||
{
|
||||
avguu += _tmpval;
|
||||
tu++;
|
||||
}
|
||||
|
||||
else if (_tmpval < avgb)
|
||||
{
|
||||
avgbb += _tmpval;
|
||||
tb++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
avguu /= (float) tu;
|
||||
avgbb /= (float) tb;
|
||||
|
||||
// lost from here- what is theory behind this?
|
||||
mini = fmax (avg + (avgb - avg) * 2.f, avgbb);
|
||||
maxi = fmin (avg + (avgu - avg) * 2.f, avguu);
|
||||
delta = maxi - mini;
|
||||
|
||||
if (delta == 0.f)
|
||||
delta = 1.f;
|
||||
|
||||
// Assign colos to normalized m_ringBufferue of last item in buffer
|
||||
// i = numvals-1;
|
||||
// m_ringBuffers[c].
|
||||
rgb[c] = finite (rgb[c]) ? fmin(1.f, fmax(0.f, (rgb[c] - mini) / delta))
|
||||
: 0.f;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,23 +20,38 @@
|
||||
|
||||
#ifndef _MOODBAR_HPP
|
||||
#define _MOODBAR_HPP
|
||||
|
||||
class PCM;
|
||||
#include "PCM.hpp"
|
||||
#include "RingBuffer.hpp"
|
||||
|
||||
class MoodBar {
|
||||
public:
|
||||
|
||||
MoodBar(unsigned int numFreqs, int size, int rate, PCM * pcm) : m_numFreqs(numFreqs), m_size(size), m_rate(rate), m_pcm(pcm) {
|
||||
|
||||
MoodBar(PCM * pcm) : m_numFreqs(pcm->numsamples/2 + 1), m_size(pcm->numsamples), m_rate(FIXED_SAMPLE_RATE), m_pcm(pcm) {
|
||||
calcBarkbandTable();
|
||||
resetBuffer();
|
||||
|
||||
}
|
||||
|
||||
MoodBar(int rate, PCM * pcm) : m_numFreqs(pcm->numsamples/2 + 1), m_size(pcm->numsamples), m_rate(rate), m_pcm(pcm) {
|
||||
calcBarkbandTable();
|
||||
resetBuffer();
|
||||
|
||||
}
|
||||
|
||||
~MoodBar() { delete(m_barkband_table); }
|
||||
|
||||
/// Calculate rgb mood values for both left and right channels.
|
||||
/// Out should be an array containing numFreqs pairs of real/complex values.
|
||||
/// Uses the pcm instance's latest assignment into its
|
||||
/// pcmL/R data buffers as inputs
|
||||
void calculateMood(float * rgb_left, float * rgb_right, float * rgb_avg);
|
||||
|
||||
private:
|
||||
void resetBuffer() ;
|
||||
|
||||
/// @bug need to find this elsewhere
|
||||
static const int FIXED_SAMPLE_RATE = 44100;
|
||||
|
||||
unsigned int m_numFreqs;
|
||||
int m_size;
|
||||
int m_rate;
|
||||
@ -44,12 +59,15 @@ private:
|
||||
* incoming band is supposed to go in. */
|
||||
void calcBarkbandTable ();
|
||||
PCM * m_pcm;
|
||||
|
||||
void standardNormalize(float * rgb);
|
||||
float m_amplitudes_left[24];
|
||||
float m_amplitudes_right[24];
|
||||
|
||||
|
||||
void stretchNormalize (float * colors);
|
||||
static const unsigned int s_bark_bands[24];
|
||||
|
||||
RingBuffer<float> m_ringBuffers[3];
|
||||
|
||||
unsigned int * m_barkband_table;
|
||||
|
||||
};
|
||||
|
||||
52
src/projectM-engine/RingBuffer.hpp
Normal file
52
src/projectM-engine/RingBuffer.hpp
Normal file
@ -0,0 +1,52 @@
|
||||
#ifndef RING_BUFFER_HPP
|
||||
#define RING_BUFFER_HPP
|
||||
/// Code courtesy of: http://www.osix.net/modules/article/?id=464
|
||||
|
||||
template<typename kind>
|
||||
class RingBuffer {
|
||||
public:
|
||||
static const unsigned long RING_BUFFER_SIZE = 300;
|
||||
private:
|
||||
|
||||
kind buffer[RING_BUFFER_SIZE];
|
||||
unsigned int current_element;
|
||||
public:
|
||||
RingBuffer() : current_element(0) {
|
||||
}
|
||||
|
||||
RingBuffer(const RingBuffer& old_ring_buf) {
|
||||
memcpy(buffer, old_ring_buf.buffer, RING_BUFFER_SIZE*sizeof(kind));
|
||||
current_element = old_ring_buf.current_element;
|
||||
}
|
||||
|
||||
RingBuffer operator = (const RingBuffer& old_ring_buf) {
|
||||
memcpy(buffer, old_ring_buf.buffer, RING_BUFFER_SIZE*sizeof(kind));
|
||||
current_element = old_ring_buf.current_element;
|
||||
}
|
||||
|
||||
~RingBuffer() { }
|
||||
|
||||
void append(kind value) {
|
||||
if(current_element >= RING_BUFFER_SIZE) {
|
||||
current_element = 0;
|
||||
}
|
||||
|
||||
buffer[current_element] = value;
|
||||
|
||||
++current_element;
|
||||
}
|
||||
|
||||
kind get() {
|
||||
if(current_element >= RING_BUFFER_SIZE) {
|
||||
current_element = 0;
|
||||
}
|
||||
|
||||
++current_element;
|
||||
return( buffer[(current_element-1)] );
|
||||
}
|
||||
|
||||
int current() {
|
||||
return (current_element);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
@ -64,7 +64,7 @@ double smoothDuration = 5;
|
||||
//int smoothFrame = 0;
|
||||
int oldFrame = 1;
|
||||
|
||||
DLLEXPORT projectM::projectM(int gx, int gy, int fps, int texsize, int width, int height) :renderer(0), renderTarget(0), smoothFrame(0), beatDetect ( 0 )
|
||||
DLLEXPORT projectM::projectM(int gx, int gy, int fps, int texsize, int width, int height) :renderer(0), renderTarget(0), smoothFrame(0), beatDetect ( 0 ), moodBar(0)
|
||||
|
||||
{
|
||||
projectM_reset();
|
||||
@ -87,6 +87,9 @@ std::cerr << "[projectM] 2" << std::endl;
|
||||
std::cerr << "[projectM] 3" << std::endl;
|
||||
if (beatDetect)
|
||||
delete(beatDetect);
|
||||
if (moodBar)
|
||||
delete(moodBar);
|
||||
|
||||
std::cerr << "[projectM] 4" << std::endl;
|
||||
if (renderTarget)
|
||||
delete(renderTarget);
|
||||
@ -95,7 +98,7 @@ std::cerr << "[projectM] 5" << std::endl;
|
||||
}
|
||||
|
||||
DLLEXPORT projectM::projectM(std::string config_file) :
|
||||
renderer(0), renderTarget(0), smoothFrame(0), beatDetect ( 0 )
|
||||
renderer(0), renderTarget(0), smoothFrame(0), beatDetect ( 0 ), moodBar(0)
|
||||
{
|
||||
projectM_reset();
|
||||
readConfig(config_file);
|
||||
@ -156,19 +159,24 @@ DLLEXPORT void projectM::renderFrame()
|
||||
|
||||
// printf("start:%d at:%d min:%d stop:%d on:%d %d\n",startframe, frame frame-startframe,avgtime, noSwitch,progress);
|
||||
presetInputs.ResetMesh();
|
||||
|
||||
|
||||
|
||||
// printf("%f %d\n",Time,frame);
|
||||
|
||||
|
||||
beatDetect->detectFromSamples();
|
||||
|
||||
#ifndef USE_MOODBAR
|
||||
#define USE_MOODBAR
|
||||
#endif
|
||||
|
||||
#ifdef USE_MOODBAR
|
||||
float rgb[3];
|
||||
moodBar->calculateMood(rgb);
|
||||
presetInputs.mood_r = rgb[0];
|
||||
presetInputs.mood_g = rgb[1];
|
||||
presetInputs.mood_b = rgb[2];
|
||||
float rgb_left[3], rgb_right[3], rgb_avg[3];
|
||||
moodBar->calculateMood(rgb_left, rgb_right, rgb_avg);
|
||||
|
||||
presetInputs.mood_r = rgb_avg[0];
|
||||
presetInputs.mood_g = rgb_avg[1];
|
||||
presetInputs.mood_b = rgb_avg[2];
|
||||
|
||||
#endif
|
||||
|
||||
DWRITE ( "=== vol: %f\tbass: %f\tmid: %f\ttreb: %f ===\n",
|
||||
@ -378,6 +386,8 @@ DLLEXPORT void projectM::projectM_reset()
|
||||
assert(!beatDetect);
|
||||
beatDetect = new BeatDetect();
|
||||
|
||||
moodBar = new MoodBar(beatDetect->pcm);
|
||||
|
||||
/* Preset loading function */
|
||||
initPresetTools();
|
||||
#if 0
|
||||
|
||||
Reference in New Issue
Block a user