mirror of
https://github.com/projectM-visualizer/projectm.git
synced 2025-10-29 19:32:24 +00:00
Cleanup PCM interface (#467)
* rewrite PCM interface * AutoLeveler * perf - quick check for no equations * cleanup use of BeatDetect.beatSensitivity Co-authored-by: Mischa Spiegelmock <me@mish.dev>
This commit is contained in:
parent
b3c3282eb0
commit
10faca9abf
4
presets/tests/000-empty.milk
Executable file
4
presets/tests/000-empty.milk
Executable file
@ -0,0 +1,4 @@
|
||||
[preset00]
|
||||
// test preset defaults, see BuiltinParams.cpp
|
||||
// most minimal preset to make something visible
|
||||
wave_r=1
|
||||
13
presets/tests/001-line.milk
Executable file
13
presets/tests/001-line.milk
Executable file
@ -0,0 +1,13 @@
|
||||
[preset00]
|
||||
// test preset defaults, see BuiltinParams.cpp
|
||||
// minimal preset to make something visible
|
||||
fdecay=0
|
||||
warp=0;
|
||||
nWaveMode=6
|
||||
fWaveScale=1
|
||||
fwavesmoothing=0.01
|
||||
wave_r=1.0
|
||||
wave_g=1.0
|
||||
wave_b=1.0
|
||||
wave_x=0.500000
|
||||
wave_y=0.500000
|
||||
@ -2,19 +2,13 @@
|
||||
per_frame_1000=// simple wave
|
||||
per_frame_1001=// MODE=0 Circle
|
||||
|
||||
fDecay=0.980000
|
||||
fDecay=0
|
||||
nWaveMode=0
|
||||
bMaximizeWaveColor=1
|
||||
fWaveAlpha=4.400000
|
||||
fWaveScale=1.605447
|
||||
fZoomExponent=1.000000
|
||||
zoom=1.000000
|
||||
rot=0.006000
|
||||
warp=0.000000
|
||||
sx=1.000000
|
||||
sy=1.000000
|
||||
wave_r=0.900000
|
||||
wave_g=0.90000
|
||||
wave_b=0.900000
|
||||
wave_r=1
|
||||
wave_x=0.500000
|
||||
wave_y=0.500000
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,20 +1,10 @@
|
||||
[preset00]
|
||||
per_frame_1000=// simple wave
|
||||
per_frame_1001=// MODE=1
|
||||
per_frame_1001=// MODE=1 RadialBlob
|
||||
|
||||
fDecay=0.980000
|
||||
fDecay=0
|
||||
nWaveMode=1
|
||||
bMaximizeWaveColor=1
|
||||
fWaveAlpha=4.400000
|
||||
fWaveScale=1.605447
|
||||
fZoomExponent=1.000000
|
||||
zoom=1.000000
|
||||
rot=0.006000
|
||||
warp=0.000000
|
||||
sx=1.000000
|
||||
sy=1.000000
|
||||
wave_r=0.900000
|
||||
wave_g=0.90000
|
||||
wave_b=0.900000
|
||||
wave_r=1
|
||||
wave_x=0.500000
|
||||
wave_y=0.500000
|
||||
|
||||
@ -2,19 +2,9 @@
|
||||
per_frame_1000=// simple wave
|
||||
per_frame_1001=// MODE=2 Blob2
|
||||
|
||||
fDecay=0.980000
|
||||
fDecay=0
|
||||
nWaveMode=2
|
||||
bMaximizeWaveColor=1
|
||||
fWaveAlpha=4.400000
|
||||
fWaveScale=1.605447
|
||||
fZoomExponent=1.000000
|
||||
zoom=1.000000
|
||||
rot=0.006000
|
||||
warp=0.000000
|
||||
sx=1.000000
|
||||
sy=1.000000
|
||||
wave_r=0.900000
|
||||
wave_g=0.90000
|
||||
wave_b=0.900000
|
||||
wave_r=1
|
||||
wave_x=0.500000
|
||||
wave_y=0.500000
|
||||
wave_y=0.500000
|
||||
@ -2,19 +2,9 @@
|
||||
per_frame_1000=// simple wave
|
||||
per_frame_1001=// MODE=3 Blob3
|
||||
|
||||
fDecay=0.980000
|
||||
fDecay=0
|
||||
nWaveMode=3
|
||||
bMaximizeWaveColor=1
|
||||
fWaveAlpha=4.400000
|
||||
fWaveScale=1.605447
|
||||
fZoomExponent=1.000000
|
||||
zoom=1.000000
|
||||
rot=0.006000
|
||||
warp=0.000000
|
||||
sx=1.000000
|
||||
sy=1.000000
|
||||
wave_r=0.900000
|
||||
wave_g=0.90000
|
||||
wave_b=0.900000
|
||||
wave_r=1
|
||||
wave_x=0.500000
|
||||
wave_y=0.500000
|
||||
|
||||
@ -2,19 +2,9 @@
|
||||
per_frame_1000=// simple wave
|
||||
per_frame_1001=// MODE=4 DerivativeLine
|
||||
|
||||
fDecay=0.980000
|
||||
fDecay=0
|
||||
nWaveMode=4
|
||||
bMaximizeWaveColor=1
|
||||
fWaveAlpha=4.400000
|
||||
fWaveScale=1.605447
|
||||
fZoomExponent=1.000000
|
||||
zoom=1.000000
|
||||
rot=0.006000
|
||||
warp=0.000000
|
||||
sx=1.000000
|
||||
sy=1.000000
|
||||
wave_r=0.900000
|
||||
wave_g=0.90000
|
||||
wave_b=0.900000
|
||||
wave_r=1
|
||||
wave_x=0.500000
|
||||
wave_y=0.500000
|
||||
wave_y=0.500000
|
||||
@ -2,19 +2,9 @@
|
||||
per_frame_1000=// simple wave
|
||||
per_frame_1001=// MODE=5 Blob5
|
||||
|
||||
fDecay=0.980000
|
||||
fDecay=0
|
||||
nWaveMode=5
|
||||
bMaximizeWaveColor=1
|
||||
fWaveAlpha=4.400000
|
||||
fWaveScale=1.605447
|
||||
fZoomExponent=1.000000
|
||||
zoom=1.000000
|
||||
rot=0.006000
|
||||
warp=0.000000
|
||||
sx=1.000000
|
||||
sy=1.000000
|
||||
wave_r=0.900000
|
||||
wave_g=0.90000
|
||||
wave_b=0.900000
|
||||
wave_r=1
|
||||
wave_x=0.500000
|
||||
wave_y=0.500000
|
||||
|
||||
@ -2,19 +2,9 @@
|
||||
per_frame_1000=// simple wave
|
||||
per_frame_1001=// MODE=6 Line
|
||||
|
||||
fDecay=0.980000
|
||||
fDecay=0
|
||||
nWaveMode=6
|
||||
bMaximizeWaveColor=1
|
||||
fWaveAlpha=4.400000
|
||||
fWaveScale=1.605447
|
||||
fZoomExponent=1.000000
|
||||
zoom=1.000000
|
||||
rot=0.006000
|
||||
warp=0.000000
|
||||
sx=1.000000
|
||||
sy=1.000000
|
||||
wave_r=0.900000
|
||||
wave_g=0.90000
|
||||
wave_b=0.900000
|
||||
wave_r=1
|
||||
wave_x=0.500000
|
||||
wave_y=0.500000
|
||||
|
||||
@ -2,19 +2,9 @@
|
||||
per_frame_1000=// simple wave
|
||||
per_frame_1001=// MODE=7 DoubleLine
|
||||
|
||||
fDecay=0.980000
|
||||
fDecay=0
|
||||
nWaveMode=7
|
||||
bMaximizeWaveColor=1
|
||||
fWaveAlpha=4.400000
|
||||
fWaveScale=1.605447
|
||||
fZoomExponent=1.000000
|
||||
zoom=1.000000
|
||||
rot=0.006000
|
||||
warp=0.000000
|
||||
sx=1.000000
|
||||
sy=1.000000
|
||||
wave_r=0.900000
|
||||
wave_g=0.90000
|
||||
wave_b=0.900000
|
||||
wave_r=1
|
||||
wave_x=0.500000
|
||||
wave_y=0.500000
|
||||
|
||||
26
presets/tests/210-wave-smooth-00.milk
Normal file
26
presets/tests/210-wave-smooth-00.milk
Normal file
@ -0,0 +1,26 @@
|
||||
[preset00]
|
||||
per_frame_1000=// simple wave
|
||||
per_frame_1001=// MODE=6 Line
|
||||
|
||||
fDecay=0
|
||||
warp=0.000000
|
||||
wave_r=1
|
||||
|
||||
// MilkdropWaveform
|
||||
nWaveMode=6
|
||||
fWaveSmoothing=0.00
|
||||
wave_x=0.600000
|
||||
wave_y=0.5
|
||||
|
||||
// CustomWaveform
|
||||
wavecode_0_enabled=1
|
||||
wavecode_0_scaling=0.05
|
||||
wavecode_0_smoothing=0
|
||||
wavecode_0_r=0.000000
|
||||
wavecode_0_g=1.000000
|
||||
wavecode_0_b=1.000000
|
||||
wavecode_0_a=1.000000
|
||||
wavecode_0_x=0.5
|
||||
wavecode_0_y=0.4
|
||||
wave_0_per_point1=x=sample;
|
||||
wave_0_per_point2=y=y+value1;
|
||||
26
presets/tests/210-wave-smooth-01.milk
Normal file
26
presets/tests/210-wave-smooth-01.milk
Normal file
@ -0,0 +1,26 @@
|
||||
[preset00]
|
||||
per_frame_1000=// simple wave
|
||||
per_frame_1001=// MODE=6 Line
|
||||
|
||||
fDecay=0
|
||||
warp=0.000000
|
||||
wave_r=1
|
||||
|
||||
// MilkdropWaveform
|
||||
nWaveMode=6
|
||||
fWaveSmoothing=0.01
|
||||
wave_x=0.600000
|
||||
wave_y=0.5
|
||||
|
||||
// CustomWaveform
|
||||
wavecode_0_enabled=1
|
||||
wavecode_0_scaling=0.05
|
||||
wavecode_0_smoothing=0.01
|
||||
wavecode_0_r=0.000000
|
||||
wavecode_0_g=1.000000
|
||||
wavecode_0_b=1.000000
|
||||
wavecode_0_a=1.000000
|
||||
wavecode_0_x=0.5
|
||||
wavecode_0_y=0.4
|
||||
wave_0_per_point1=x=sample;
|
||||
wave_0_per_point2=y=y+value1;
|
||||
26
presets/tests/210-wave-smooth-100.milk
Normal file
26
presets/tests/210-wave-smooth-100.milk
Normal file
@ -0,0 +1,26 @@
|
||||
[preset00]
|
||||
per_frame_1000=// simple wave
|
||||
per_frame_1001=// MODE=6 Line
|
||||
|
||||
fDecay=0
|
||||
warp=0.000000
|
||||
wave_r=1
|
||||
|
||||
// MilkdropWaveform
|
||||
nWaveMode=6
|
||||
fWaveSmoothing=1.00
|
||||
wave_x=0.600000
|
||||
wave_y=0.5
|
||||
|
||||
// CustomWaveform
|
||||
wavecode_0_enabled=1
|
||||
wavecode_0_scaling=0.05
|
||||
wavecode_0_smoothing=1.00
|
||||
wavecode_0_r=0.000000
|
||||
wavecode_0_g=1.000000
|
||||
wavecode_0_b=1.000000
|
||||
wavecode_0_a=1.000000
|
||||
wavecode_0_x=0.5
|
||||
wavecode_0_y=0.4
|
||||
wave_0_per_point1=x=sample;
|
||||
wave_0_per_point2=y=y+value1;
|
||||
26
presets/tests/210-wave-smooth-80.milk
Normal file
26
presets/tests/210-wave-smooth-80.milk
Normal file
@ -0,0 +1,26 @@
|
||||
[preset00]
|
||||
per_frame_1000=// simple wave
|
||||
per_frame_1001=// MODE=6 Line
|
||||
|
||||
fDecay=0
|
||||
warp=0.000000
|
||||
wave_r=1
|
||||
|
||||
// MilkdropWaveform
|
||||
nWaveMode=6
|
||||
fWaveSmoothing=0.80
|
||||
wave_x=0.600000
|
||||
wave_y=0.5
|
||||
|
||||
// CustomWaveform
|
||||
wavecode_0_enabled=1
|
||||
wavecode_0_scaling=0.05
|
||||
wavecode_0_smoothing=0.80
|
||||
wavecode_0_r=0.000000
|
||||
wavecode_0_g=1.000000
|
||||
wavecode_0_b=1.000000
|
||||
wavecode_0_a=1.000000
|
||||
wavecode_0_x=0.5
|
||||
wavecode_0_y=0.4
|
||||
wave_0_per_point1=x=sample;
|
||||
wave_0_per_point2=y=y+value1;
|
||||
25
presets/tests/210-wave-smooth-90.milk
Normal file
25
presets/tests/210-wave-smooth-90.milk
Normal file
@ -0,0 +1,25 @@
|
||||
[preset00]
|
||||
per_frame_1000=// simple wave
|
||||
per_frame_1001=// MODE=6 Line
|
||||
|
||||
fDecay=0
|
||||
warp=0.000000
|
||||
wave_r=1
|
||||
|
||||
// MilkdropWaveform
|
||||
nWaveMode=6
|
||||
fWaveSmoothing=0.90
|
||||
wave_x=0.600000
|
||||
wave_y=0.5
|
||||
|
||||
// CustomWaveform
|
||||
wavecode_0_enabled=1
|
||||
wavecode_0_samples=512
|
||||
wavecode_0_scaling=0.05
|
||||
wavecode_0_smoothing=0.90
|
||||
wavecode_0_r=0.000000
|
||||
wavecode_0_g=1.000000
|
||||
wavecode_0_b=1.000000
|
||||
wavecode_0_a=1.000000
|
||||
wave_0_per_point1=x=sample;
|
||||
wave_0_per_point2=y=0.4+value1;
|
||||
26
presets/tests/210-wave-smooth-99.milk
Normal file
26
presets/tests/210-wave-smooth-99.milk
Normal file
@ -0,0 +1,26 @@
|
||||
[preset00]
|
||||
per_frame_1000=// simple wave
|
||||
per_frame_1001=// MODE=6 Line
|
||||
|
||||
fDecay=0
|
||||
warp=0.000000
|
||||
wave_r=1
|
||||
|
||||
// MilkdropWaveform
|
||||
nWaveMode=6
|
||||
fWaveSmoothing=0.99
|
||||
wave_x=0.600000
|
||||
wave_y=0.5
|
||||
|
||||
// CustomWaveform
|
||||
wavecode_0_enabled=1
|
||||
wavecode_0_scaling=0.05
|
||||
wavecode_0_smoothing=0.99
|
||||
wavecode_0_r=0.000000
|
||||
wavecode_0_g=1.000000
|
||||
wavecode_0_b=1.000000
|
||||
wavecode_0_a=1.000000
|
||||
wavecode_0_x=0.5
|
||||
wavecode_0_y=0.4
|
||||
wave_0_per_point1=x=sample;
|
||||
wave_0_per_point2=y=y+value1;
|
||||
5
presets/tests/250-wavecode.milk
Executable file → Normal file
5
presets/tests/250-wavecode.milk
Executable file → Normal file
@ -9,7 +9,6 @@ fWaveAlpha=4.400000
|
||||
fWaveScale=1.5
|
||||
fZoomExponent=1.000000
|
||||
zoom=1.000000
|
||||
rot=0.006000
|
||||
warp=0.000000
|
||||
sx=1.000000
|
||||
sy=1.000000
|
||||
@ -30,7 +29,7 @@ wavecode_0_r=0.000000
|
||||
wavecode_0_g=1.000000
|
||||
wavecode_0_b=1.000000
|
||||
wavecode_0_a=1.000000
|
||||
wave_0_per_point41=x=x+value1;
|
||||
wave_0_per_point42=y=y+value2;
|
||||
wave_0_per_point41=x=x+value1/2
|
||||
wave_0_per_point42=y=y+value2/2;
|
||||
|
||||
per_frame_1=zoom=1
|
||||
34
presets/tests/251-wavecode-spectrum.milk
Normal file
34
presets/tests/251-wavecode-spectrum.milk
Normal file
@ -0,0 +1,34 @@
|
||||
[preset00]
|
||||
per_frame_1000=// spectrum vs pcm
|
||||
|
||||
fDecay=0
|
||||
warp=0.000000
|
||||
wave_a=0
|
||||
|
||||
// spectrum=0
|
||||
wavecode_0_enabled=1
|
||||
wavecode_0_bspectrum=0
|
||||
wavecode_0_scaling=0.1
|
||||
wavecode_0_r=0.000000
|
||||
wavecode_0_g=1.000000
|
||||
wavecode_0_b=1.000000
|
||||
wavecode_0_a=1.000000
|
||||
wavecode_0_x=0.5
|
||||
wavecode_0_y=0.75
|
||||
wave_0_per_point1=x=sample;
|
||||
wave_0_per_point2=y=y+value1;
|
||||
|
||||
|
||||
// spectrum=1
|
||||
wavecode_1_enabled=1
|
||||
wavecode_1_bSpectrum=1
|
||||
wavecode_1_scaling=1
|
||||
wavecode_1_r=1.000000
|
||||
wavecode_1_g=1.000000
|
||||
wavecode_1_b=1.000000
|
||||
wavecode_1_a=1.000000
|
||||
wavecode_1_x=0.5
|
||||
wavecode_1_y=0.25
|
||||
wave_1_per_point1=x=sample;
|
||||
wave_1_per_point2=y=0.25+value1;
|
||||
|
||||
@ -3,6 +3,7 @@ fDecay=0.75
|
||||
fWarpScale=2.853000
|
||||
fZoomExponent=1.000000
|
||||
warp=0.000000
|
||||
wave_a=0
|
||||
cy=1.0
|
||||
cx=0.5
|
||||
sx=1
|
||||
|
||||
@ -429,6 +429,10 @@ void MilkdropPreset::initialize_PerPixelMeshes()
|
||||
// Evaluates all per-pixel equations
|
||||
void MilkdropPreset::evalPerPixelEqns()
|
||||
{
|
||||
// Quick bail out if there is nothing to do.
|
||||
if (per_pixel_eqn_tree.empty())
|
||||
return;
|
||||
|
||||
if (nullptr == per_pixel_program)
|
||||
{
|
||||
// This is a little forward looking, but if we want to JIT assignments expressions, we might
|
||||
|
||||
@ -19,12 +19,14 @@ PresetInputs::PresetInputs() : PipelineContext()
|
||||
void PresetInputs::update(const BeatDetect & music, const PipelineContext & context) {
|
||||
|
||||
// Reflect new values form the beat detection unit
|
||||
this->bass = music.bass;
|
||||
this->mid = music.mid;
|
||||
this->treb = music.treb;
|
||||
this->bass_att = music.bass_att;
|
||||
this->mid_att = music.mid_att;
|
||||
this->treb_att = music.treb_att;
|
||||
// see https://github.com/projectM-visualizer/projectm/pull/348
|
||||
// beatSensitivity code moved from BeatDetect to here
|
||||
this->bass = music.bass * music.beatSensitivity;
|
||||
this->mid = music.mid * music.beatSensitivity;
|
||||
this->treb = music.treb * music.beatSensitivity;
|
||||
this->bass_att = music.bass_att * music.beatSensitivity;
|
||||
this->mid_att = music.mid_att * music.beatSensitivity;
|
||||
this->treb_att = music.treb_att * music.beatSensitivity;
|
||||
|
||||
// Reflect new values from the pipeline context
|
||||
this->fps = context.fps;
|
||||
|
||||
@ -24,9 +24,9 @@
|
||||
* Takes sound data from wherever and hands it back out.
|
||||
* Returns PCM Data or spectrum data, or the derivative of the PCM data
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "Common.hpp"
|
||||
#include "wipemalloc.h"
|
||||
@ -34,371 +34,526 @@
|
||||
#include "PCM.hpp"
|
||||
#include <cassert>
|
||||
|
||||
int PCM::maxsamples = 2048;
|
||||
|
||||
//initPCM(int samples)
|
||||
//
|
||||
//Initializes the PCM buffer to
|
||||
// number of samples specified.
|
||||
#include <iostream>
|
||||
PCM::PCM() {
|
||||
_initPCM( 2048 );
|
||||
// see https://github.com/projectM-visualizer/projectm/issues/161
|
||||
class AutoLevel
|
||||
{
|
||||
private:
|
||||
double level;
|
||||
// accumulate sample data
|
||||
size_t level_samples;
|
||||
double level_sum;
|
||||
double level_max;
|
||||
double l0,l1,l2;
|
||||
|
||||
#ifdef DEBUG
|
||||
std::cerr << "[PCM] MAX SAMPLES:" << maxsamples << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
void PCM::_initPCM(int samples) {
|
||||
int i;
|
||||
|
||||
waveSmoothing = 0;
|
||||
|
||||
//Allocate memory for PCM data buffer
|
||||
assert(samples == 2048);
|
||||
PCMd = (float **)wipemalloc(2 * sizeof(float *));
|
||||
PCMd[0] = (float *)wipemalloc(samples * sizeof(float));
|
||||
PCMd[1] = (float *)wipemalloc(samples * sizeof(float));
|
||||
|
||||
//maxsamples=samples;
|
||||
newsamples=0;
|
||||
numsamples = maxsamples;
|
||||
|
||||
//Initialize buffers to 0
|
||||
for (i=0;i<samples;i++)
|
||||
public:
|
||||
AutoLevel() : level(0.01),level_samples(0),level_sum(0),level_max(0),l0(-1),l1(-1),l2(-1)
|
||||
{
|
||||
PCMd[0][i]=0;
|
||||
PCMd[1][i]=0;
|
||||
}
|
||||
|
||||
start=0;
|
||||
/*
|
||||
* Here is where we try to do auto volume setting. Doing this here
|
||||
* means that none of the code downstream (waveforms, beatdetect, etc) needs
|
||||
* to worry about it.
|
||||
*
|
||||
* 1) Don't over react to level changes within a song
|
||||
* 2) Ignore silence/gaps
|
||||
*
|
||||
* I don't know if it's necessary to have both sum and max, but that makes
|
||||
* it easier to experiment...
|
||||
*/
|
||||
|
||||
//Allocate FFT workspace
|
||||
// per rdft() documentation
|
||||
// length of ip >= 2+sqrt(n) and length of w == n/2
|
||||
// This is an arbitrary number that helps control
|
||||
// a) how quickly the level can change and
|
||||
// b) keeps code from being affected by how the caller provides data (lot of short buffers, or fewer long buffers)
|
||||
#define AUTOLEVEL_SEGMENT 4096
|
||||
|
||||
double updateLevel(size_t samples, double sum, double max)
|
||||
{
|
||||
if (sum/samples < 0.00001)
|
||||
return level;
|
||||
level_sum += sum;
|
||||
level_max = fmax(level_max,max*1.02);
|
||||
level_samples += samples;
|
||||
if (level_samples >= AUTOLEVEL_SEGMENT || l0 <= 0)
|
||||
{
|
||||
double max_recent = fmax(fmax(l0, l1), fmax(l2, level_max));
|
||||
l0 = l1; l1 = l2; l2 = level_max; level_max *= 0.95;
|
||||
level_sum = level_samples = 0;
|
||||
level = (l0 <= 0) ? max_recent : level * 0.96 + max_recent * 0.04;
|
||||
level = fmax(level,0.0001);
|
||||
}
|
||||
return level;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
PCM::PCM() : start(0), newsamples(0)
|
||||
{
|
||||
leveler = new AutoLevel();
|
||||
|
||||
//Allocate FFT workspace
|
||||
// per rdft() documentation
|
||||
// length of ip >= 2+sqrt(n) and length of w == n/2
|
||||
#if FFT_LENGTH > 1024
|
||||
#error update this code
|
||||
#endif
|
||||
w = (double *)wipemalloc(FFT_LENGTH/2*sizeof(double));
|
||||
ip = (int *)wipemalloc(34 * sizeof(int));
|
||||
ip[0]=0;
|
||||
|
||||
/** PCM data */
|
||||
// this->maxsamples = 2048;
|
||||
// this->numsamples = 0;
|
||||
// this->pcmdataL = NULL;
|
||||
// this->pcmdataR = NULL;
|
||||
|
||||
/** Allocate PCM data structures */
|
||||
pcmdataL=(float *)wipemalloc(this->maxsamples*sizeof(float));
|
||||
pcmdataR=(float *)wipemalloc(this->maxsamples*sizeof(float));
|
||||
w = (double *)wipemalloc(FFT_LENGTH*sizeof(double));
|
||||
// see fftsg.cpp length of ip >= 2+sqrt(n/2)
|
||||
// in this case n=2*FFT_LENGTH, so 34 is big enough to handle FFT_LENGTH=1024
|
||||
ip = (int *)wipemalloc(34 * sizeof(int));
|
||||
ip[0]=0;
|
||||
|
||||
memset(pcmL, 0, sizeof(pcmL));
|
||||
memset(pcmR, 0, sizeof(pcmR));
|
||||
memset(freqL, 0, sizeof(freqL));
|
||||
memset(freqR, 0, sizeof(freqR));
|
||||
memset(spectrumL, 0, sizeof(spectrumL));
|
||||
memset(spectrumR, 0, sizeof(spectrumR));
|
||||
}
|
||||
|
||||
PCM::~PCM() {
|
||||
|
||||
free(pcmdataL);
|
||||
free(pcmdataR);
|
||||
free(w);
|
||||
free(ip);
|
||||
|
||||
free(PCMd[0]);
|
||||
free(PCMd[1]);
|
||||
free(PCMd);
|
||||
|
||||
PCM::~PCM()
|
||||
{
|
||||
delete leveler;
|
||||
free(w);
|
||||
free(ip);
|
||||
}
|
||||
|
||||
#include <iostream>
|
||||
|
||||
void PCM::addPCMfloat(const float *PCMdata, int samples)
|
||||
|
||||
void PCM::addPCMfloat(const float *PCMdata, size_t samples)
|
||||
{
|
||||
int i,j;
|
||||
|
||||
for(i=0;i<samples;i++)
|
||||
float a,sum=0,max=0;
|
||||
for (size_t i=0; i<samples; i++)
|
||||
{
|
||||
j=i+start;
|
||||
size_t j=(i+start)%maxsamples;
|
||||
a=pcmL[j] = PCMdata[i];
|
||||
pcmR[j] = PCMdata[i];
|
||||
sum += fabs(a);
|
||||
max = fmax(max,a);
|
||||
}
|
||||
start = (start+samples)%maxsamples;
|
||||
newsamples += samples;
|
||||
level = leveler->updateLevel(samples, sum, max);
|
||||
}
|
||||
|
||||
if (PCMdata[i] != 0 ) {
|
||||
|
||||
PCMd[0][j%maxsamples] = PCMdata[i];
|
||||
PCMd[1][j%maxsamples] = PCMdata[i];
|
||||
/* NOTE: this method expects total samples, not samples per channel */
|
||||
void PCM::addPCMfloat_2ch(const float *PCMdata, size_t count)
|
||||
{
|
||||
size_t samples = count/2;
|
||||
float a,b,sum=0,max=0;
|
||||
for (size_t i=0; i<samples; i++)
|
||||
{
|
||||
size_t j=(start+i)%maxsamples;
|
||||
a = pcmL[j] = PCMdata[i*2];
|
||||
b = pcmR[j] = PCMdata[i*2+1];
|
||||
sum += fabs(a) + fabs(b);
|
||||
max = fmax(fmax(max,fabs(a)),fabs(b));
|
||||
}
|
||||
start = (start + samples) % maxsamples;
|
||||
newsamples += samples;
|
||||
level = leveler->updateLevel(samples, sum/2, max);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
PCMd[0][j % maxsamples] = 0;
|
||||
PCMd[1][j % maxsamples] = 0;
|
||||
}
|
||||
|
||||
void PCM::addPCM16Data(const short* pcm_data, size_t samples)
|
||||
{
|
||||
float a, b, sum = 0, max = 0;
|
||||
for (size_t i = 0; i < samples; ++i)
|
||||
{
|
||||
size_t j = (i + start) % maxsamples;
|
||||
a = pcmL[j] = (pcm_data[i * 2 + 0] / 16384.0);
|
||||
b = pcmR[j] = (pcm_data[i * 2 + 1] / 16384.0);
|
||||
sum += fabs(a) + fabs(b);
|
||||
max = fmax(fmax(max, a), b);
|
||||
}
|
||||
start = (start + samples) % maxsamples;
|
||||
newsamples += samples;
|
||||
level = leveler->updateLevel(samples, sum/2, max);
|
||||
}
|
||||
|
||||
|
||||
void PCM::addPCM16(const short PCMdata[2][512])
|
||||
{
|
||||
const int samples=512;
|
||||
float a,b,sum=0,max=0;
|
||||
for (size_t i=0;i<samples;i++)
|
||||
{
|
||||
size_t j=(i+start) % maxsamples;
|
||||
a=pcmL[j]=(PCMdata[0][i]/16384.0);
|
||||
b=pcmR[j]=(PCMdata[1][i]/16384.0);
|
||||
sum += fabs(a) + fabs(b);
|
||||
max = fmax(fmax(max,a),b);
|
||||
}
|
||||
start = (start+samples) % maxsamples;
|
||||
newsamples += samples;
|
||||
level = leveler->updateLevel(samples, sum/2, max);
|
||||
}
|
||||
|
||||
|
||||
void PCM::addPCM8(const unsigned char PCMdata[2][1024])
|
||||
{
|
||||
const int samples=1024;
|
||||
float a,b,sum=0,max=0;
|
||||
for (size_t i=0; i<samples; i++)
|
||||
{
|
||||
size_t j= (i+start) % maxsamples;
|
||||
a=pcmL[j]=(((float)PCMdata[0][i] - 128.0) / 64 );
|
||||
b=pcmR[j]=(((float)PCMdata[1][i] - 128.0) / 64 );
|
||||
sum += fabs(a) + fabs(b);
|
||||
max = fmax(fmax(max,a),b);
|
||||
}
|
||||
start = (start + samples) % maxsamples;
|
||||
newsamples += samples;
|
||||
level = leveler->updateLevel(samples, sum/2, max);
|
||||
}
|
||||
|
||||
|
||||
void PCM::addPCM8_512(const unsigned char PCMdata[2][512])
|
||||
{
|
||||
const size_t samples=512;
|
||||
float a,b,sum=0,max=0;
|
||||
for (size_t i=0; i<samples; i++)
|
||||
{
|
||||
size_t j = (i+start) % maxsamples;
|
||||
a=pcmL[j]=(((float)PCMdata[0][i] - 128.0 ) / 64 );
|
||||
b=pcmR[j]=(((float)PCMdata[1][i] - 128.0 ) / 64 );
|
||||
sum += fabs(a) + fabs(b);
|
||||
max = fmax(fmax(max,a),b);
|
||||
}
|
||||
start = (start + samples) % maxsamples;
|
||||
newsamples += samples;
|
||||
level = leveler->updateLevel(samples, sum/2, max);
|
||||
}
|
||||
|
||||
|
||||
// puts sound data requested at provided pointer
|
||||
//
|
||||
// samples is number of PCM samples to return
|
||||
// smoothing is the smoothing coefficient
|
||||
// returned values are normalized from -1 to 1
|
||||
|
||||
void PCM::getPCM(float *data, CHANNEL channel, size_t samples, float smoothing)
|
||||
{
|
||||
assert(channel == 0 || channel == 1);
|
||||
|
||||
if (0==smoothing)
|
||||
{
|
||||
_copyPCM(data, channel, samples);
|
||||
return;
|
||||
}
|
||||
|
||||
start+=samples;
|
||||
start=start%maxsamples;
|
||||
// since we've already got the freq data laying around, let's use that for smoothing
|
||||
_updateFFT();
|
||||
|
||||
newsamples+=samples;
|
||||
if (newsamples>maxsamples) newsamples=maxsamples;
|
||||
numsamples = getPCMnew(pcmdataR,1,0,waveSmoothing,0,0);
|
||||
getPCMnew(pcmdataL,0,0,waveSmoothing,0,1);
|
||||
getPCM(vdataL,FFT_LENGTH,0,1,0,0);
|
||||
getPCM(vdataR,FFT_LENGTH,1,1,0,0);
|
||||
}
|
||||
// copy
|
||||
double freq[FFT_LENGTH*2];
|
||||
double *from = channel==0 ? freqL : freqR;
|
||||
for (int i=0 ; i<FFT_LENGTH*2 ; i++)
|
||||
freq[i] = from[i];
|
||||
|
||||
|
||||
void PCM::addPCMfloat_2ch(const float *PCMdata, int samples)
|
||||
{
|
||||
int i,j;
|
||||
|
||||
for(i=0;i<samples;i+=2)
|
||||
// The visible effects ramp up as you smoothing value gets close to 1.0 (consistent with milkdrop2)
|
||||
if (1==0) // gaussian
|
||||
{
|
||||
j=(i/2)+start;
|
||||
PCMd[0][j%maxsamples] = PCMdata[i];
|
||||
PCMd[1][j%maxsamples] = PCMdata[i+1];
|
||||
// precompute constant:
|
||||
double k = -1.0 / ((1 - smoothing) * (1 - smoothing) * FFT_LENGTH * FFT_LENGTH);
|
||||
for (int i = 1; i < FFT_LENGTH; i++)
|
||||
{
|
||||
float g = pow(2.718281828459045, i * i * k);
|
||||
freq[i * 2] *= g;
|
||||
freq[i * 2 + 1] *= g;
|
||||
}
|
||||
freq[1] *= pow(2.718281828459045, FFT_LENGTH*FFT_LENGTH*k);
|
||||
}
|
||||
else
|
||||
{
|
||||
// butterworth
|
||||
// this might be slightly faster to compute. pow() is expensive
|
||||
double k = 1.0 / ((1 - smoothing) * (1 - smoothing) * FFT_LENGTH * FFT_LENGTH);
|
||||
for (int i = 1; i < FFT_LENGTH; i++)
|
||||
{
|
||||
float b = 1.0 / (1.0 + (i * i * k));
|
||||
freq[i * 2] *= b;
|
||||
freq[i * 2 + 1] *= b;
|
||||
}
|
||||
freq[1] *= 1.0 / (1.0 + (FFT_LENGTH*FFT_LENGTH*k));
|
||||
}
|
||||
|
||||
start+=samples/2;
|
||||
start=start%maxsamples;
|
||||
// inverse fft
|
||||
rdft(FFT_LENGTH*2, -1, freq, ip, w);
|
||||
for (size_t j = 0; j < FFT_LENGTH*2; j++)
|
||||
freq[j] *= 1.0 / FFT_LENGTH;
|
||||
|
||||
newsamples+=samples/2;
|
||||
if (newsamples>maxsamples)
|
||||
newsamples=maxsamples;
|
||||
numsamples = getPCMnew(pcmdataR,1,0,waveSmoothing,0,0);
|
||||
getPCMnew(pcmdataL,0,0,waveSmoothing,0,1);
|
||||
getPCM(vdataL,FFT_LENGTH,0,1,0,0);
|
||||
getPCM(vdataR,FFT_LENGTH,1,1,0,0);
|
||||
// copy out with zero-padding if necessary
|
||||
size_t count = samples<FFT_LENGTH ? samples : FFT_LENGTH;
|
||||
for (size_t i=0 ; i<count ; i++)
|
||||
data[i] = freq[i%(FFT_LENGTH*2)];
|
||||
for (size_t i=count ; i<samples ; i++)
|
||||
data[i] = 0;
|
||||
}
|
||||
|
||||
|
||||
void PCM::addPCM16Data(const short* pcm_data, short samples) {
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < samples; ++i) {
|
||||
j=i+start;
|
||||
PCMd[0][j % maxsamples]=(pcm_data[i * 2 + 0]/16384.0);
|
||||
PCMd[1][j % maxsamples]=(pcm_data[i * 2 + 1]/16384.0);
|
||||
}
|
||||
|
||||
start = (start + samples) % maxsamples;
|
||||
|
||||
newsamples+=samples;
|
||||
if (newsamples>maxsamples) newsamples=maxsamples;
|
||||
numsamples = getPCMnew(pcmdataR,1,0,waveSmoothing,0,0);
|
||||
getPCMnew(pcmdataL,0,0,waveSmoothing,0,1);
|
||||
getPCM(vdataL,FFT_LENGTH,0,1,0,0);
|
||||
getPCM(vdataR,FFT_LENGTH,1,1,0,0);
|
||||
}
|
||||
|
||||
|
||||
void PCM::addPCM16(short PCMdata[2][512])
|
||||
/* NOTE: Still don't have real support for smoothing parameter, but this gets close to the milkdrop2 default look */
|
||||
void PCM::getSpectrum(float *data, CHANNEL channel, size_t samples, float smoothing)
|
||||
{
|
||||
int i,j;
|
||||
int samples=512;
|
||||
assert(channel == 0 || channel == 1);
|
||||
_updateFFT();
|
||||
|
||||
for(i=0;i<samples;i++)
|
||||
{
|
||||
j=i+start;
|
||||
if ( PCMdata[0][i] != 0 && PCMdata[1][i] != 0 ) {
|
||||
PCMd[0][j%maxsamples]=(PCMdata[0][i]/16384.0);
|
||||
PCMd[1][j%maxsamples]=(PCMdata[1][i]/16384.0);
|
||||
} else {
|
||||
PCMd[0][j % maxsamples] = (float)0;
|
||||
PCMd[1][j % maxsamples] = (float)0;
|
||||
}
|
||||
}
|
||||
|
||||
// printf("Added %d samples %d %d %f\n",samples,start,(start+samples)%maxsamples,PCM[0][start+10]);
|
||||
|
||||
start+=samples;
|
||||
start=start%maxsamples;
|
||||
|
||||
newsamples+=samples;
|
||||
if (newsamples>maxsamples) newsamples=maxsamples;
|
||||
|
||||
numsamples = getPCMnew(pcmdataR,1,0,waveSmoothing,0,0);
|
||||
getPCMnew(pcmdataL,0,0,waveSmoothing,0,1);
|
||||
getPCM(vdataL,FFT_LENGTH,0,1,0,0);
|
||||
getPCM(vdataR,FFT_LENGTH,1,1,0,0);
|
||||
float *spectrum = channel == 0 ? spectrumL : spectrumR;
|
||||
if (smoothing == 0)
|
||||
{
|
||||
size_t count = samples <= FFT_LENGTH ? samples : FFT_LENGTH;
|
||||
for (size_t i = 0; i < count; i++)
|
||||
data[i] = spectrum[i];
|
||||
for (size_t i = count; i < samples; i++)
|
||||
data[0] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
float l2 = 0, l1 =0 , c = 0, r1, r2;
|
||||
r1 = spectrum[0]; r2 = spectrum[0+1];
|
||||
for (size_t i = 0; i < samples; i++)
|
||||
{
|
||||
l2 = l1;
|
||||
l1 = c;
|
||||
c = r1;
|
||||
r1 = r2;
|
||||
r2 = (i + 2) >= samples ? 0 : spectrum[i + 2];
|
||||
data[i] = (l2 + 4 * l1 + 6 * c + 4 * r1 + r2) / 16.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PCM::addPCM8( unsigned char PCMdata[2][1024])
|
||||
void PCM::_updateFFT()
|
||||
{
|
||||
int i,j;
|
||||
int samples=1024;
|
||||
|
||||
|
||||
for(i=0;i<samples;i++)
|
||||
{
|
||||
j=i+start;
|
||||
if ( PCMdata[0][i] != 0 && PCMdata[1][i] != 0 ) {
|
||||
PCMd[0][j%maxsamples]=( (float)( PCMdata[0][i] - 128.0 ) / 64 );
|
||||
PCMd[1][j%maxsamples]=( (float)( PCMdata[1][i] - 128.0 ) / 64 );
|
||||
} else {
|
||||
PCMd[0][j % maxsamples] = 0;
|
||||
PCMd[1][j % maxsamples] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// printf("Added %d samples %d %d %f\n",samples,start,(start+samples)%maxsamples,PCM[0][start+10]);
|
||||
|
||||
start+=samples;
|
||||
start=start%maxsamples;
|
||||
|
||||
newsamples+=samples;
|
||||
if (newsamples>maxsamples) newsamples=maxsamples;
|
||||
numsamples = getPCMnew(pcmdataR,1,0,waveSmoothing,0,0);
|
||||
getPCMnew(pcmdataL,0,0,waveSmoothing,0,1);
|
||||
getPCM(vdataL,FFT_LENGTH,0,1,0,0);
|
||||
getPCM(vdataR,FFT_LENGTH,1,1,0,0);
|
||||
if (newsamples > 0)
|
||||
{
|
||||
_updateFFT(0);
|
||||
_updateFFT(1);
|
||||
newsamples = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void PCM::addPCM8_512( const unsigned char PCMdata[2][512])
|
||||
void PCM::_updateFFT(size_t channel)
|
||||
{
|
||||
int i,j;
|
||||
int samples=512;
|
||||
assert(channel == 0 || channel == 1);
|
||||
|
||||
double *freq = channel==0 ? freqL : freqR;
|
||||
_copyPCM(freq, channel, FFT_LENGTH*2);
|
||||
rdft(FFT_LENGTH*2, 1, freq, ip, w);
|
||||
|
||||
for(i=0;i<samples;i++)
|
||||
{
|
||||
j=i+start;
|
||||
if ( PCMdata[0][i] != 0 && PCMdata[1][i] != 0 ) {
|
||||
PCMd[0][j%maxsamples]=( (float)( PCMdata[0][i] - 128.0 ) / 64 );
|
||||
PCMd[1][j%maxsamples]=( (float)( PCMdata[1][i] - 128.0 ) / 64 );
|
||||
} else {
|
||||
PCMd[0][j % maxsamples] = 0;
|
||||
PCMd[1][j % maxsamples] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// printf("Added %d samples %d %d %f\n",samples,start,(start+samples)%maxsamples,PCM[0][start+10]);
|
||||
|
||||
start+=samples;
|
||||
start=start%maxsamples;
|
||||
|
||||
newsamples+=samples;
|
||||
if (newsamples>maxsamples) newsamples=maxsamples;
|
||||
numsamples = getPCMnew(pcmdataR,1,0,waveSmoothing,0,0);
|
||||
getPCMnew(pcmdataL,0,0,waveSmoothing,0,1);
|
||||
getPCM(vdataL,FFT_LENGTH,0,1,0,0);
|
||||
getPCM(vdataR,FFT_LENGTH,1,1,0,0);
|
||||
// compute magnitude data (m^2 actually)
|
||||
float *spectrum = channel==0 ? spectrumL : spectrumR;
|
||||
for (size_t i=1 ; i<FFT_LENGTH ; i++)
|
||||
{
|
||||
double m2 = (freq[i * 2] * freq[i * 2] + freq[i * 2 + 1] * freq[i * 2 + 1]);
|
||||
spectrum[i-1] = m2 * ((double)i)/FFT_LENGTH;
|
||||
}
|
||||
spectrum[FFT_LENGTH-1] = freq[1] * freq[1];
|
||||
}
|
||||
|
||||
|
||||
//puts sound data requested at provided pointer
|
||||
//
|
||||
//samples is number of PCM samples to return
|
||||
//freq = 0 gives PCM data
|
||||
//freq = 1 gives FFT data
|
||||
//smoothing is the smoothing coefficient
|
||||
|
||||
//returned values are normalized from -1 to 1
|
||||
|
||||
void PCM::getPCM(float *PCMdata, int samples, int channel, int freq, float smoothing, int derive)
|
||||
inline double constrain(double a, double mn, double mx)
|
||||
{
|
||||
if (smoothing == 0)
|
||||
{
|
||||
for (int i = 0; i < samples; i++)
|
||||
{
|
||||
int index = start - 1 - i;
|
||||
if (index < 0)
|
||||
index = maxsamples + index;
|
||||
PCMdata[i] = PCMd[channel][index];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int index=start-1;
|
||||
|
||||
if (index<0)
|
||||
index=maxsamples+index;
|
||||
|
||||
PCMdata[0] = PCMd[channel][index];
|
||||
|
||||
for (int i = 1; i < samples; i++)
|
||||
{
|
||||
index = start - 1 - i;
|
||||
if (index < 0)
|
||||
index = maxsamples + index;
|
||||
PCMdata[i] = (1 - smoothing) * PCMd[channel][index] + smoothing * PCMdata[i - 1];
|
||||
}
|
||||
}
|
||||
|
||||
//return derivative of PCM data
|
||||
if (derive)
|
||||
{
|
||||
for(int i=0;i<samples-1;i++)
|
||||
{
|
||||
PCMdata[i]=PCMdata[i]-PCMdata[i+1];
|
||||
}
|
||||
PCMdata[samples-1]=0;
|
||||
}
|
||||
|
||||
//return frequency data instead of PCM (perform FFT)
|
||||
|
||||
if (freq)
|
||||
{
|
||||
// NOTE some presets set bSpectrum=1 and samples!=2^n, not sure what rdft() does with that
|
||||
assert(samples <= 1024);
|
||||
samples = std::min(1024,samples);
|
||||
double temppcm[1024];
|
||||
for (int i=0;i<samples;i++)
|
||||
{temppcm[i]=(double)PCMdata[i];}
|
||||
rdft(samples, 1, temppcm, ip, w);
|
||||
for (int j=0;j<samples;j++)
|
||||
{PCMdata[j]=(float)temppcm[j];}
|
||||
}
|
||||
return a>mx ? mx : a<mn ? mn : a;
|
||||
}
|
||||
|
||||
//getPCMnew
|
||||
//
|
||||
//Like getPCM except it returns all new samples in the buffer
|
||||
//the actual return value is the number of samples, up to maxsamples.
|
||||
//the passed pointer, PCMData, must bee able to hold up to maxsamples
|
||||
|
||||
int PCM::getPCMnew(float *PCMdata, int channel, int freq, float smoothing, int derive, int reset)
|
||||
// pull data from circular buffer
|
||||
void PCM::_copyPCM(float *to, int channel, size_t count)
|
||||
{
|
||||
int i,index;
|
||||
assert(channel == 0 || channel == 1);
|
||||
assert(count < maxsamples);
|
||||
const float *from = channel==0 ? pcmL : pcmR;
|
||||
const double volume = 1.0 / level;
|
||||
for (size_t i=0, pos=start ; i<count ; i++)
|
||||
{
|
||||
if (pos==0)
|
||||
pos = maxsamples;
|
||||
to[i] = from[--pos] * volume;
|
||||
}
|
||||
}
|
||||
|
||||
index=start-1;
|
||||
|
||||
if (index<0) index=maxsamples+index;
|
||||
|
||||
PCMdata[0]=PCMd[channel][index];
|
||||
for(i=1;i<newsamples;i++)
|
||||
{
|
||||
index=start-1-i;
|
||||
if (index<0) index=maxsamples+index;
|
||||
|
||||
PCMdata[i]=(1-smoothing)*PCMd[channel][index]+smoothing*PCMdata[i-1];
|
||||
}
|
||||
|
||||
//return derivative of PCM data
|
||||
if(derive)
|
||||
{
|
||||
for(i=0;i<newsamples-1;i++)
|
||||
{
|
||||
PCMdata[i]=PCMdata[i]-PCMdata[i+1];
|
||||
}
|
||||
PCMdata[newsamples-1]=0;
|
||||
}
|
||||
|
||||
//return frequency data instead of PCM (perform FFT)
|
||||
// if (freq) rdft(samples, 1, PCMdata, ip, w);
|
||||
i=newsamples;
|
||||
if (reset) newsamples=0;
|
||||
|
||||
return i;
|
||||
void PCM::_copyPCM(double *to, int channel, size_t count)
|
||||
{
|
||||
assert(channel == 0 || channel == 1);
|
||||
const float *from = channel==0 ? pcmL : pcmR;
|
||||
const double volume = 1.0 / level;
|
||||
for (size_t i=0, pos=start ; i<count ; i++)
|
||||
{
|
||||
if (pos==0)
|
||||
pos = maxsamples;
|
||||
to[i] = from[--pos] * volume;
|
||||
}
|
||||
}
|
||||
|
||||
//Free stuff
|
||||
void PCM::freePCM() {
|
||||
free(PCMd[0]);
|
||||
free(PCMd[1]);
|
||||
free(PCMd);
|
||||
void PCM::freePCM()
|
||||
{
|
||||
free(ip);
|
||||
free(w);
|
||||
|
||||
PCMd = NULL;
|
||||
ip = NULL;
|
||||
w = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// TESTS
|
||||
|
||||
|
||||
#include "TestRunner.hpp"
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
#include <PresetLoader.hpp>
|
||||
|
||||
#define TEST(cond) if (!verify(__FILE__ ": " #cond,cond)) return false
|
||||
#define TEST2(str,cond) if (!verify(str,cond)) return false
|
||||
|
||||
struct PCMTest : public Test
|
||||
{
|
||||
PCMTest() : Test("PCMTest")
|
||||
{}
|
||||
|
||||
bool eq(float a, float b)
|
||||
{
|
||||
return fabs(a-b) < (fabs(a)+fabs(b) + 1)/1000.0f;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/* smoke test for each addPCM method */
|
||||
bool test_addpcm()
|
||||
{
|
||||
PCM pcm;
|
||||
|
||||
// mono float
|
||||
{
|
||||
const size_t samples = 301;
|
||||
float *data = new float[samples];
|
||||
for (size_t i = 0; i < samples; i++)
|
||||
data[i] = ((float) i) / (samples - 1);
|
||||
for (size_t i = 0; i < 10; i++)
|
||||
pcm.addPCMfloat(data, samples);
|
||||
float *copy = new float[samples];
|
||||
pcm.level = 1.0;
|
||||
pcm._copyPCM(copy, 0, samples);
|
||||
for (size_t i = 0; i < samples; i++)
|
||||
TEST(eq(copy[i],((float)samples - 1 - i) / (samples - 1)));
|
||||
pcm._copyPCM(copy, 1, samples);
|
||||
for (size_t i = 0; i < samples; i++)
|
||||
TEST(eq(copy[i], ((float)samples - 1 - i) / (samples - 1)));
|
||||
free(data);
|
||||
free(copy);
|
||||
}
|
||||
|
||||
// float_2ch
|
||||
{
|
||||
const size_t samples = 301;
|
||||
float *data = new float[samples*2];
|
||||
for (size_t i = 0; i < samples; i++)
|
||||
{
|
||||
data[i*2] = ((float) i) / (samples - 1);
|
||||
data[i*2+1] = 1.0-data[i*2] ;
|
||||
}
|
||||
for (size_t i = 0; i < 10; i++)
|
||||
pcm.addPCMfloat_2ch(data, samples*2);
|
||||
float *copy0 = new float[samples];
|
||||
float *copy1 = new float[samples];
|
||||
pcm.level = 1;
|
||||
pcm._copyPCM(copy0, 0, samples);
|
||||
pcm._copyPCM(copy1, 1, samples);
|
||||
for (size_t i = 0; i < samples; i++)
|
||||
TEST(eq(1.0,copy0[i]+copy1[i]));
|
||||
free(data);
|
||||
free(copy0);
|
||||
free(copy1);
|
||||
}
|
||||
|
||||
// void PCM::addPCM16Data(const short* pcm_data, size_t samples)
|
||||
// void PCM::addPCM16(const short PCMdata[2][512])
|
||||
// void PCM::addPCM8(const unsigned char PCMdata[2][1024])
|
||||
// void PCM::addPCM8_512(const unsigned char PCMdata[2][512])
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool test_fft()
|
||||
{
|
||||
PCM pcm;
|
||||
|
||||
// low frequency
|
||||
{
|
||||
const size_t samples = 1024;
|
||||
float *data = new float[samples * 2];
|
||||
for (size_t i = 0; i < samples; i++)
|
||||
{
|
||||
float f = 2 * 3.141592653589793 * ((double) i) / (samples - 1);
|
||||
data[i * 2] = sin(f);
|
||||
data[i * 2 + 1] = sin(f + 1.0); // out of phase
|
||||
}
|
||||
pcm.addPCMfloat_2ch(data, samples * 2);
|
||||
pcm.addPCMfloat_2ch(data, samples * 2);
|
||||
float *freq0 = new float[FFT_LENGTH];
|
||||
float *freq1 = new float[FFT_LENGTH];
|
||||
pcm.level = 1.0;
|
||||
pcm.getSpectrum(freq0, CHANNEL_0, FFT_LENGTH, 0.0);
|
||||
pcm.getSpectrum(freq1, CHANNEL_1, FFT_LENGTH, 0.0);
|
||||
// freq0 and freq1 should be equal
|
||||
for (size_t i = 0; i < FFT_LENGTH; i++)
|
||||
TEST(eq(freq0[i], freq1[i]));
|
||||
TEST(freq0[0] > 500);
|
||||
for (size_t i = 1; i < FFT_LENGTH; i++)
|
||||
TEST(freq0[i] < 0.1);
|
||||
free(data);
|
||||
free(freq0);
|
||||
free(freq1);
|
||||
}
|
||||
|
||||
// high frequency
|
||||
{
|
||||
const size_t samples = 2;
|
||||
float data[4] = {1.0,0.0,0.0,1.0};
|
||||
for (size_t i = 0; i < 1024; i++)
|
||||
pcm.addPCMfloat_2ch(data, samples * 2);
|
||||
float *freq0 = new float[FFT_LENGTH];
|
||||
float *freq1 = new float[FFT_LENGTH];
|
||||
pcm.level = 1.0;
|
||||
pcm.getSpectrum(freq0, CHANNEL_0, FFT_LENGTH, 0.0);
|
||||
pcm.getSpectrum(freq1, CHANNEL_1, FFT_LENGTH, 0.0);
|
||||
// freq0 and freq1 should be equal
|
||||
for (size_t i = 0; i < FFT_LENGTH; i++)
|
||||
TEST(eq(freq0[i], freq1[i]));
|
||||
for (size_t i=0 ; i<FFT_LENGTH-1 ; i++)
|
||||
TEST(0==freq0[i]);
|
||||
TEST(freq0[FFT_LENGTH-1] > 100000);
|
||||
free(freq0);
|
||||
free(freq1);
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool test() override
|
||||
{
|
||||
TEST(test_addpcm());
|
||||
TEST(test_fft());
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Test* PCM::test()
|
||||
{
|
||||
return new PCMTest();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
Test* PCM::test()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@ -29,53 +29,100 @@
|
||||
#ifndef _PCM_H
|
||||
#define _PCM_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "dlldefs.h"
|
||||
|
||||
|
||||
// 1024 is more computationally intensive, but maybe better at detecting lower bass
|
||||
#define FFT_LENGTH 1024
|
||||
// FFT_LENGTH is number of magnitude values available from getSpectrum().
|
||||
// Internally this is generated using 2xFFT_LENGTH samples per channel.
|
||||
#define FFT_LENGTH 512
|
||||
class Test;
|
||||
class AutoLevel;
|
||||
|
||||
enum CHANNEL
|
||||
{
|
||||
CHANNEL_L = 0,
|
||||
CHANNEL_0 = 0,
|
||||
CHANNEL_R = 1,
|
||||
CHANNEL_1 = 1
|
||||
};
|
||||
|
||||
class
|
||||
#ifdef WIN32
|
||||
DLLEXPORT
|
||||
#endif
|
||||
PCM {
|
||||
PCM
|
||||
{
|
||||
public:
|
||||
float **PCMd;
|
||||
int start;
|
||||
/* maximum number of sound samples that are actually stored. */
|
||||
static const size_t maxsamples=2048;
|
||||
|
||||
/** Use wave smoothing */
|
||||
float waveSmoothing;
|
||||
|
||||
int *ip;
|
||||
double *w;
|
||||
int newsamples;
|
||||
|
||||
int numsamples; //size of new PCM info
|
||||
float *pcmdataL; //holder for most recent pcm data
|
||||
float *pcmdataR; //holder for most recent pcm data
|
||||
|
||||
/** PCM data */
|
||||
float vdataL[FFT_LENGTH]; //holders for FFT data (spectrum)
|
||||
float vdataR[FFT_LENGTH];
|
||||
|
||||
static int maxsamples;
|
||||
PCM();
|
||||
~PCM();
|
||||
void addPCMfloat(const float *PCMdata, int samples);
|
||||
void addPCMfloat_2ch(const float *PCMdata, int samples);
|
||||
void addPCM16(short [2][512]);
|
||||
void addPCM16Data(const short* pcm_data, short samples);
|
||||
void addPCM8( unsigned char [2][1024]);
|
||||
void addPCM8_512( const unsigned char [2][512]);
|
||||
void getPCM(float *data, int samples, int channel, int freq, float smoothing, int derive);
|
||||
void freePCM();
|
||||
int getPCMnew(float *PCMdata, int channel, int freq, float smoothing, int derive,int reset);
|
||||
|
||||
void addPCMfloat( const float *PCMdata, size_t samples );
|
||||
void addPCMfloat_2ch( const float *PCMdata, size_t count );
|
||||
void addPCM16( const short [2][512] );
|
||||
void addPCM16Data( const short* pcm_data, size_t samples );
|
||||
void addPCM8( const unsigned char [2][1024] );
|
||||
void addPCM8_512( const unsigned char [2][512] );
|
||||
|
||||
/**
|
||||
* PCM data
|
||||
* When smoothing=0 is copied directly from PCM buffers. smoothing=1.0 is almost a straight line.
|
||||
* The returned data will 'wrap' if more than maxsamples are requested.
|
||||
*/
|
||||
void getPCM(float *data, CHANNEL channel, size_t samples, float smoothing);
|
||||
|
||||
/** Spectrum data
|
||||
* Smoothing is not fully implemented, only none (smoothing==0) or a little (smoothing!=0).
|
||||
* The returned data will be zero padded if more than FFT_LENGTH values are requested
|
||||
*/
|
||||
void getSpectrum(float *data, CHANNEL channel, size_t samples, float smoothing);
|
||||
|
||||
static Test* test();
|
||||
|
||||
private:
|
||||
void _initPCM(int maxsamples);
|
||||
// mem-usage:
|
||||
// pcmd 2x2048*4b = 16K
|
||||
// vdata 2x512x2*8b = 16K
|
||||
// spectrum 2x512*4b = 4k
|
||||
// w = 512*8b = 4k
|
||||
|
||||
};
|
||||
// circular PCM buffer
|
||||
// adjust "volume" of PCM data as we go, this simplifies everything downstream...
|
||||
// normalize to range [-1.0,1.0]
|
||||
float pcmL[maxsamples];
|
||||
float pcmR[maxsamples];
|
||||
int start;
|
||||
size_t newsamples;
|
||||
|
||||
// raw FFT data
|
||||
double freqL[FFT_LENGTH*2];
|
||||
double freqR[FFT_LENGTH*2];
|
||||
// magnitude data
|
||||
float spectrumL[FFT_LENGTH];
|
||||
float spectrumR[FFT_LENGTH];
|
||||
|
||||
// for FFT library
|
||||
int *ip;
|
||||
double *w;
|
||||
|
||||
void freePCM();
|
||||
|
||||
// copy data out of the circular PCM buffer
|
||||
void _copyPCM(float *PCMdata, int channel, size_t count);
|
||||
void _copyPCM(double *PCMdata, int channel, size_t count);
|
||||
|
||||
// update FFT data if new samples are available.
|
||||
void _updateFFT();
|
||||
void _updateFFT(size_t channel);
|
||||
|
||||
friend class PCMTest;
|
||||
|
||||
// state for tracking audio level
|
||||
double level;
|
||||
class AutoLevel *leveler;
|
||||
};
|
||||
|
||||
#endif /** !_PCM_H */
|
||||
|
||||
@ -104,60 +104,41 @@ void BeatDetect::detectFromSamples()
|
||||
treb=0;
|
||||
vol=0;
|
||||
|
||||
// TODO: get sample rate from PCM? Assume 44100
|
||||
getBeatVals(44100.0f, FFT_LENGTH, pcm->pcmdataL, pcm->pcmdataR);
|
||||
float vdataL[FFT_LENGTH];
|
||||
float vdataR[FFT_LENGTH];
|
||||
pcm->getSpectrum(vdataL, CHANNEL_0, FFT_LENGTH, 0.0);
|
||||
pcm->getSpectrum(vdataR, CHANNEL_1, FFT_LENGTH, 0.0);
|
||||
|
||||
// OK, we're not really using this number 44.1 anywhere
|
||||
// This is more of a nod to the fact that if the actually data rate is REALLY different
|
||||
// then in theory the bass/mid/treb ranges should be adjusted.
|
||||
// In practice, I doubt it would adversely affect the actually display very much
|
||||
getBeatVals(44100.0f, FFT_LENGTH, vdataL, vdataR);
|
||||
}
|
||||
|
||||
|
||||
|
||||
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
|
||||
assert(fft_length >= 256);
|
||||
unsigned ranges[4] = {0, 3, 23, 255};
|
||||
|
||||
// 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_instant=0;
|
||||
for (unsigned i=ranges[0] ; i<ranges[1] ; i++)
|
||||
bass_instant += vdataL[i] + vdataR[i];
|
||||
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_instant=0;
|
||||
for (unsigned i=ranges[1] ; i<ranges[2] ; i++)
|
||||
mid_instant += vdataL[i] + vdataR[i];
|
||||
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]);
|
||||
for (unsigned i=ranges[2] ; i<ranges[3] ; i++)
|
||||
treb_instant += vdataL[i] + vdataR[i];
|
||||
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);
|
||||
@ -168,10 +149,10 @@ void BeatDetect::getBeatVals( float samplerate, unsigned fft_length, float *vdat
|
||||
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);
|
||||
bass = bass_instant / fmax(0.0001, bass_history);
|
||||
mid = mid_instant / fmax(0.0001, mid_history);
|
||||
treb = treb_instant / fmax(0.0001, treb_history);
|
||||
vol = vol_instant / fmax(0.0001, vol_history);
|
||||
|
||||
if ( projectM_isnan( treb ) ) {
|
||||
treb = 0.0;
|
||||
@ -188,18 +169,6 @@ void BeatDetect::getBeatVals( float samplerate, unsigned fft_length, float *vdat
|
||||
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;
|
||||
|
||||
@ -44,11 +44,14 @@
|
||||
class DLLEXPORT BeatDetect
|
||||
{
|
||||
public:
|
||||
float treb ;
|
||||
// Does this really belong here? maybe belongs on projectM.Settings?
|
||||
float beatSensitivity;
|
||||
|
||||
float treb ;
|
||||
float mid ;
|
||||
float bass ;
|
||||
float vol_old ;
|
||||
float beatSensitivity;
|
||||
|
||||
float treb_att ;
|
||||
float mid_att ;
|
||||
float bass_att ;
|
||||
@ -67,7 +70,10 @@ class DLLEXPORT BeatDetect
|
||||
// getPCMScale() was added to address https://github.com/projectM-visualizer/projectm/issues/161
|
||||
// Returning 1.0 results in using the raw PCM data, which can make the presets look pretty unresponsive
|
||||
// if the application volume is low.
|
||||
float getPCMScale();
|
||||
float getPCMScale()
|
||||
{
|
||||
return beatSensitivity;
|
||||
}
|
||||
|
||||
private:
|
||||
int beat_buffer_pos;
|
||||
|
||||
@ -35,14 +35,11 @@ void MilkdropWaveform::InitVertexAttrib() {
|
||||
|
||||
void MilkdropWaveform::Draw(RenderContext &context)
|
||||
{
|
||||
// wavearray is 2048 so don't exceed that
|
||||
// TODO use named constant
|
||||
if (samples > 2048)
|
||||
samples = 2048;
|
||||
if (samples > (int)PCM::maxsamples)
|
||||
samples = (int)PCM::maxsamples;
|
||||
// NOTE MilkdropWaveform does not have a "samples" parameter
|
||||
// so this member variable is just being set in WaveformMath and used here
|
||||
|
||||
WaveformMath(context);
|
||||
assert(samples<=512);
|
||||
|
||||
for (int waveno=1 ; waveno<=(two_waves?2:1) ; waveno++)
|
||||
{
|
||||
@ -192,10 +189,22 @@ void MilkdropWaveform::MaximizeColors(RenderContext &context)
|
||||
|
||||
void MilkdropWaveform::WaveformMath(RenderContext &context)
|
||||
{
|
||||
const float *pcmdataR = context.beatDetect->pcm->pcmdataR;
|
||||
const float *pcmdataL = context.beatDetect->pcm->pcmdataL;
|
||||
// scale PCM data based on vol_history to make it more or less independent of the application output volume
|
||||
// NOTE: these buffer sizes are based on the MilkdropWaveformMode implementation later in this method.
|
||||
float pcmdataL[512];
|
||||
float pcmdataR[512];
|
||||
context.beatDetect->pcm->getPCM(pcmdataL, CHANNEL_0, 512, smoothing);
|
||||
context.beatDetect->pcm->getPCM(pcmdataR, CHANNEL_1, 512, smoothing);
|
||||
|
||||
// tie size of waveform to beatSensitivity
|
||||
const float vol_scale = context.beatDetect->getPCMScale();
|
||||
if (vol_scale != 1.0)
|
||||
{
|
||||
for (int i=0 ; i<512 ; i++)
|
||||
{
|
||||
pcmdataL[i] *= vol_scale;
|
||||
pcmdataR[i] *= vol_scale;
|
||||
}
|
||||
}
|
||||
|
||||
float r2, theta;
|
||||
|
||||
@ -219,18 +228,18 @@ void MilkdropWaveform::WaveformMath(RenderContext &context)
|
||||
rot = 0;
|
||||
aspectScale=1.0;
|
||||
|
||||
samples = context.beatDetect->pcm->numsamples;
|
||||
samples = 512;
|
||||
|
||||
float inv_nverts_minus_one = 1.0f/(float)(samples);
|
||||
|
||||
float last_value = vol_scale * (pcmdataR[samples-1]+pcmdataL[samples-1]);
|
||||
float first_value = vol_scale * (pcmdataR[0]+pcmdataL[0]);
|
||||
float last_value = pcmdataR[samples-1]+pcmdataL[samples-1];
|
||||
float first_value = pcmdataR[0]+pcmdataL[0];
|
||||
float offset = first_value-last_value;
|
||||
|
||||
for ( int i=0;i<samples;i++)
|
||||
{
|
||||
|
||||
float value = vol_scale * (pcmdataR[i]+pcmdataL[i]);
|
||||
float value = pcmdataR[i]+pcmdataL[i];
|
||||
value += offset * (i/(float)samples);
|
||||
|
||||
r2=(0.5f + 0.4f*.12f*value*scale + mystery)*.5f;
|
||||
@ -250,8 +259,8 @@ void MilkdropWaveform::WaveformMath(RenderContext &context)
|
||||
samples = 512-32;
|
||||
for ( int i=0;i<512-32;i++)
|
||||
{
|
||||
theta=vol_scale*pcmdataL[i+32]*0.06f*scale * 1.57f + context.time*2.3f;
|
||||
r2=(0.53f + 0.43f*vol_scale*pcmdataR[i]*0.12f*scale+ mystery)*.5f;
|
||||
theta=pcmdataL[i+32]*0.06f*scale * 1.57f + context.time*2.3f;
|
||||
r2=(0.53f + 0.43f*pcmdataR[i]*0.12f*scale+ mystery)*.5f;
|
||||
|
||||
wavearray[i][0]=(r2*cos(theta)*(context.aspectCorrect ? context.aspectRatio : 1.0f)+x);
|
||||
wavearray[i][1]=(r2*sin(theta)+temp_y);
|
||||
@ -267,8 +276,8 @@ void MilkdropWaveform::WaveformMath(RenderContext &context)
|
||||
|
||||
for ( int i=0;i<512-32;i++)
|
||||
{
|
||||
wavearray[i][0]=(vol_scale*pcmdataR[i]*scale*0.5f*(context.aspectCorrect ? context.aspectRatio : 1.0f) + x);
|
||||
wavearray[i][1]=(vol_scale*pcmdataL[i+32]*scale*0.5f + temp_y);
|
||||
wavearray[i][0]=(pcmdataR[i]*scale*0.5f*(context.aspectCorrect ? context.aspectRatio : 1.0f) + x);
|
||||
wavearray[i][1]=(pcmdataL[i+32]*scale*0.5f + temp_y);
|
||||
}
|
||||
|
||||
break;
|
||||
@ -282,8 +291,8 @@ void MilkdropWaveform::WaveformMath(RenderContext &context)
|
||||
|
||||
for ( int i=0;i<512-32;i++)
|
||||
{
|
||||
wavearray[i][0]=(vol_scale*pcmdataR[i] * scale*0.5f + x);
|
||||
wavearray[i][1]=( (vol_scale*pcmdataL[i+32]*scale*0.5f + temp_y));
|
||||
wavearray[i][0]=(pcmdataR[i] * scale*0.5f + x);
|
||||
wavearray[i][1]=( (pcmdataL[i+32]*scale*0.5f + temp_y));
|
||||
}
|
||||
|
||||
break;
|
||||
@ -301,8 +310,8 @@ void MilkdropWaveform::WaveformMath(RenderContext &context)
|
||||
for (int i=0; i<512-32; i++)
|
||||
{
|
||||
xx[i] = -1.0f + 2.0f*(i/(512.0f-32.0f)) + x;
|
||||
yy[i] = 0.4f* vol_scale*pcmdataL[i]*0.47f*scale + temp_y;
|
||||
xx[i] += 0.4f* vol_scale*pcmdataR[i]*0.44f*scale;
|
||||
yy[i] = 0.4f* pcmdataL[i]*0.47f*scale + temp_y;
|
||||
xx[i] += 0.4f* pcmdataR[i]*0.44f*scale;
|
||||
|
||||
if (i>1)
|
||||
{
|
||||
@ -311,7 +320,8 @@ void MilkdropWaveform::WaveformMath(RenderContext &context)
|
||||
}
|
||||
wavearray[i][0]=xx[i];
|
||||
wavearray[i][1]=yy[i];
|
||||
} }
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Blob5://EXPERIMENTAL
|
||||
@ -325,8 +335,8 @@ void MilkdropWaveform::WaveformMath(RenderContext &context)
|
||||
|
||||
for ( int i=0;i<512-32;i++)
|
||||
{
|
||||
float x0 = (vol_scale*pcmdataR[i]*vol_scale*pcmdataL[i+32] + vol_scale*pcmdataL[i+32]*vol_scale*pcmdataR[i]);
|
||||
float y0 = (vol_scale*pcmdataR[i]*vol_scale*pcmdataR[i] - vol_scale*pcmdataL[i+32]*vol_scale*pcmdataL[i+32]);
|
||||
float x0 = (pcmdataR[i]*pcmdataL[i+32] + pcmdataL[i+32]*pcmdataR[i]);
|
||||
float y0 = (pcmdataR[i]*pcmdataR[i] - pcmdataL[i+32]*pcmdataL[i+32]);
|
||||
wavearray[i][0]=((x0*cos_rot - y0*sin_rot)*scale*0.5f*(context.aspectCorrect ? context.aspectRatio : 1.0f) + x);
|
||||
wavearray[i][1]=( (x0*sin_rot + y0*cos_rot)*scale*0.5f + temp_y);
|
||||
}
|
||||
@ -336,16 +346,16 @@ void MilkdropWaveform::WaveformMath(RenderContext &context)
|
||||
|
||||
|
||||
wave_x_temp=-2*0.4142f*(fabs(fabs(mystery)-.5f)-.5f);
|
||||
samples = 512;
|
||||
|
||||
rot = -mystery*90;
|
||||
aspectScale =1.0f+wave_x_temp;
|
||||
wave_x_temp=-1*(x-1.0f);
|
||||
samples = context.beatDetect->pcm->numsamples;
|
||||
|
||||
for ( int i=0; i<samples; i++)
|
||||
for ( int i=0; i< samples;i++)
|
||||
{
|
||||
wavearray[i][0]=i/(float) samples;
|
||||
wavearray[i][1]=vol_scale*pcmdataR[i]*.04f*scale+wave_x_temp;
|
||||
wavearray[i][1]=pcmdataR[i]*.04f*scale+wave_x_temp;
|
||||
}
|
||||
// printf("%f %f\n",renderTarget->texsize*wave_y_temp,wave_y_temp);
|
||||
|
||||
@ -359,8 +369,7 @@ void MilkdropWaveform::WaveformMath(RenderContext &context)
|
||||
rot = -mystery*90;
|
||||
aspectScale =1.0f+wave_x_temp;
|
||||
|
||||
|
||||
samples = context.beatDetect->pcm->numsamples;
|
||||
samples = 512;
|
||||
two_waves = true;
|
||||
|
||||
const float y_adj = y*y*.5f;
|
||||
@ -370,13 +379,13 @@ void MilkdropWaveform::WaveformMath(RenderContext &context)
|
||||
for ( int i=0;i<samples;i++)
|
||||
{
|
||||
wavearray[i][0]=i/((float) samples);
|
||||
wavearray[i][1]= vol_scale*pcmdataL[i]*.04f*scale+(wave_y_temp+y_adj);
|
||||
wavearray[i][1]= pcmdataL[i]*.04f*scale+(wave_y_temp+y_adj);
|
||||
}
|
||||
|
||||
for ( int i=0;i<samples;i++)
|
||||
{
|
||||
wavearray2[i][0]=i/((float) samples);
|
||||
wavearray2[i][1]=vol_scale*pcmdataR[i]*.04f*scale+(wave_y_temp-y_adj);
|
||||
wavearray2[i][1]=pcmdataR[i]*.04f*scale+(wave_y_temp-y_adj);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@ -19,9 +19,8 @@
|
||||
typedef float floatPair[2];
|
||||
|
||||
Waveform::Waveform(int _samples)
|
||||
: RenderItem(),samples(_samples), points(_samples), pointContext(_samples)
|
||||
: RenderItem(), samples(_samples), points(_samples), pointContext(_samples)
|
||||
{
|
||||
|
||||
spectrum = false; /* spectrum data or pcm data */
|
||||
dots = false; /* draw wave as dots or lines */
|
||||
thick = false; /* draw thicker lines */
|
||||
@ -43,37 +42,38 @@ void Waveform::InitVertexAttrib() {
|
||||
}
|
||||
|
||||
void Waveform::Draw(RenderContext &context)
|
||||
{
|
||||
{
|
||||
// scale PCM data based on vol_history to make it more or less independent of the application output volume
|
||||
const float vol_scale = context.beatDetect->getPCMScale();
|
||||
const float vol_scale = context.beatDetect->getPCMScale();
|
||||
|
||||
// Make sure samples<=points.size(). We could reallocate points, but 512 is probably big enough.
|
||||
size_t samples_count = this->samples;
|
||||
if (samples_count > this->points.size())
|
||||
samples_count = this->points.size();
|
||||
// Make sure samples<=points.size(). We could reallocate points, but 512 is probably big enough.
|
||||
size_t samples_count = this->samples;
|
||||
if (samples_count > this->points.size())
|
||||
samples_count = this->points.size();
|
||||
|
||||
float *value1 = new float[samples_count];
|
||||
float *value2 = new float[samples_count];
|
||||
context.beatDetect->pcm->getPCM( value1, samples_count, 0, spectrum, smoothing, 0);
|
||||
context.beatDetect->pcm->getPCM( value2, samples_count, 1, spectrum, smoothing, 0);
|
||||
|
||||
float mult = scaling*( spectrum ? 0.015f :1.0f);
|
||||
#ifdef WIN32
|
||||
std::transform(&value1[0], &value1[samples_count], &value1[0], bind2nd(std::multiplies<float>(), mult));
|
||||
std::transform(&value2[0], &value2[samples_count], &value2[0], bind2nd(std::multiplies<float>(), mult));
|
||||
#else
|
||||
std::transform(&value1[0], &value1[samples_count], &value1[0], std::bind(std::multiplies<float>(), std::placeholders::_1, mult));
|
||||
std::transform(&value2[0], &value2[samples_count], &value2[0], std::bind(std::multiplies<float>(), std::placeholders::_1, mult));
|
||||
#endif /** WIN32 */
|
||||
float *value1 = new float[samples_count];
|
||||
float *value2 = new float[samples_count];
|
||||
if (spectrum)
|
||||
{
|
||||
// TODO support smoothing parameter for getSpectrum()
|
||||
context.beatDetect->pcm->getSpectrum( value1, CHANNEL_0, samples_count, 1.0 );
|
||||
context.beatDetect->pcm->getSpectrum( value2, CHANNEL_1, samples_count, 1.0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
context.beatDetect->pcm->getPCM( value1, CHANNEL_0, samples_count, smoothing );
|
||||
context.beatDetect->pcm->getPCM( value2, CHANNEL_1, samples_count, smoothing );
|
||||
}
|
||||
|
||||
const float mult = scaling * vol_scale * (spectrum ? 0.005f : 1.0f);
|
||||
WaveformContext waveContext(samples_count, context.beatDetect);
|
||||
|
||||
for(size_t x=0;x< samples_count;x++)
|
||||
for (size_t x=0;x< samples_count;x++)
|
||||
{
|
||||
waveContext.sample = x/(float)(samples_count - 1);
|
||||
waveContext.sample_int = x;
|
||||
waveContext.left = vol_scale * value1[x];
|
||||
waveContext.right = vol_scale * value2[x];
|
||||
waveContext.left = value1[x] * mult;
|
||||
waveContext.right = value2[x] * mult;
|
||||
|
||||
points[x] = PerPoint(points[x],waveContext);
|
||||
}
|
||||
@ -129,4 +129,4 @@ void Waveform::Draw(RenderContext &context)
|
||||
|
||||
delete[] value1;
|
||||
delete[] value2;
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,6 +19,7 @@ bool TestRunner::run()
|
||||
tests.push_back(Param::test());
|
||||
tests.push_back(Parser::test());
|
||||
tests.push_back(Expr::test());
|
||||
tests.push_back(PCM::test());
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user