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:
w1z7ard
2007-09-21 00:40:45 +00:00
parent d754523a3e
commit 5dbafa9cce
5 changed files with 290 additions and 28 deletions

View File

@ -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

View File

@ -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;
}
}

View File

@ -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;
};

View 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

View File

@ -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