From 10faca9abf4c879a63cdf426ddae338620baf238 Mon Sep 17 00:00:00 2001 From: mbellew Date: Sat, 20 Feb 2021 13:52:17 -0800 Subject: [PATCH] Cleanup PCM interface (#467) * rewrite PCM interface * AutoLeveler * perf - quick check for no equations * cleanup use of BeatDetect.beatSensitivity Co-authored-by: Mischa Spiegelmock --- presets/tests/000-empty.milk | 4 + presets/tests/001-line.milk | 13 + presets/tests/200-wave.milk | 16 +- presets/tests/201-wave.milk | 16 +- presets/tests/202-wave.milk | 16 +- presets/tests/203-wave.milk | 14 +- presets/tests/204-wave.milk | 16 +- presets/tests/205-wave.milk | 14 +- presets/tests/206-wave.milk | 14 +- presets/tests/207-wave.milk | 14 +- presets/tests/210-wave-smooth-00.milk | 26 + presets/tests/210-wave-smooth-01.milk | 26 + presets/tests/210-wave-smooth-100.milk | 26 + presets/tests/210-wave-smooth-80.milk | 26 + presets/tests/210-wave-smooth-90.milk | 25 + presets/tests/210-wave-smooth-99.milk | 26 + presets/tests/250-wavecode.milk | 5 +- presets/tests/251-wavecode-spectrum.milk | 34 + presets/tests/300-beatdetect-bassmidtreb.milk | 1 + .../MilkdropPresetFactory/MilkdropPreset.cpp | 4 + .../MilkdropPresetFactory/PresetFrameIO.cpp | 14 +- src/libprojectM/PCM.cpp | 777 +++++++++++------- src/libprojectM/PCM.hpp | 111 ++- src/libprojectM/Renderer/BeatDetect.cpp | 79 +- src/libprojectM/Renderer/BeatDetect.hpp | 12 +- src/libprojectM/Renderer/MilkdropWaveform.cpp | 71 +- src/libprojectM/Renderer/Waveform.cpp | 50 +- src/libprojectM/TestRunner.cpp | 1 + 28 files changed, 887 insertions(+), 564 deletions(-) create mode 100755 presets/tests/000-empty.milk create mode 100755 presets/tests/001-line.milk create mode 100644 presets/tests/210-wave-smooth-00.milk create mode 100644 presets/tests/210-wave-smooth-01.milk create mode 100644 presets/tests/210-wave-smooth-100.milk create mode 100644 presets/tests/210-wave-smooth-80.milk create mode 100644 presets/tests/210-wave-smooth-90.milk create mode 100644 presets/tests/210-wave-smooth-99.milk mode change 100755 => 100644 presets/tests/250-wavecode.milk create mode 100644 presets/tests/251-wavecode-spectrum.milk diff --git a/presets/tests/000-empty.milk b/presets/tests/000-empty.milk new file mode 100755 index 000000000..c8a7d8bad --- /dev/null +++ b/presets/tests/000-empty.milk @@ -0,0 +1,4 @@ +[preset00] +// test preset defaults, see BuiltinParams.cpp +// most minimal preset to make something visible +wave_r=1 diff --git a/presets/tests/001-line.milk b/presets/tests/001-line.milk new file mode 100755 index 000000000..b8ad4cf5b --- /dev/null +++ b/presets/tests/001-line.milk @@ -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 diff --git a/presets/tests/200-wave.milk b/presets/tests/200-wave.milk index 20ee7e1c0..94671075b 100755 --- a/presets/tests/200-wave.milk +++ b/presets/tests/200-wave.milk @@ -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 + + + diff --git a/presets/tests/201-wave.milk b/presets/tests/201-wave.milk index edcbddc82..dd2f8fb2b 100755 --- a/presets/tests/201-wave.milk +++ b/presets/tests/201-wave.milk @@ -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 diff --git a/presets/tests/202-wave.milk b/presets/tests/202-wave.milk index 3a2ac0d3a..e67378ef2 100755 --- a/presets/tests/202-wave.milk +++ b/presets/tests/202-wave.milk @@ -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 \ No newline at end of file diff --git a/presets/tests/203-wave.milk b/presets/tests/203-wave.milk index b218aea27..615ce02c5 100755 --- a/presets/tests/203-wave.milk +++ b/presets/tests/203-wave.milk @@ -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 diff --git a/presets/tests/204-wave.milk b/presets/tests/204-wave.milk index be687e51b..e44367f95 100755 --- a/presets/tests/204-wave.milk +++ b/presets/tests/204-wave.milk @@ -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 \ No newline at end of file diff --git a/presets/tests/205-wave.milk b/presets/tests/205-wave.milk index 26824fd50..7acc659f1 100755 --- a/presets/tests/205-wave.milk +++ b/presets/tests/205-wave.milk @@ -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 diff --git a/presets/tests/206-wave.milk b/presets/tests/206-wave.milk index 7838eaabe..f7899d37f 100755 --- a/presets/tests/206-wave.milk +++ b/presets/tests/206-wave.milk @@ -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 diff --git a/presets/tests/207-wave.milk b/presets/tests/207-wave.milk index 18f7ba7b1..ff426b028 100755 --- a/presets/tests/207-wave.milk +++ b/presets/tests/207-wave.milk @@ -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 diff --git a/presets/tests/210-wave-smooth-00.milk b/presets/tests/210-wave-smooth-00.milk new file mode 100644 index 000000000..56e9497df --- /dev/null +++ b/presets/tests/210-wave-smooth-00.milk @@ -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; diff --git a/presets/tests/210-wave-smooth-01.milk b/presets/tests/210-wave-smooth-01.milk new file mode 100644 index 000000000..d5dc9760b --- /dev/null +++ b/presets/tests/210-wave-smooth-01.milk @@ -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; diff --git a/presets/tests/210-wave-smooth-100.milk b/presets/tests/210-wave-smooth-100.milk new file mode 100644 index 000000000..65f2f66f3 --- /dev/null +++ b/presets/tests/210-wave-smooth-100.milk @@ -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; diff --git a/presets/tests/210-wave-smooth-80.milk b/presets/tests/210-wave-smooth-80.milk new file mode 100644 index 000000000..5df747c96 --- /dev/null +++ b/presets/tests/210-wave-smooth-80.milk @@ -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; diff --git a/presets/tests/210-wave-smooth-90.milk b/presets/tests/210-wave-smooth-90.milk new file mode 100644 index 000000000..24cd6947c --- /dev/null +++ b/presets/tests/210-wave-smooth-90.milk @@ -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; diff --git a/presets/tests/210-wave-smooth-99.milk b/presets/tests/210-wave-smooth-99.milk new file mode 100644 index 000000000..94d26a9d9 --- /dev/null +++ b/presets/tests/210-wave-smooth-99.milk @@ -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; diff --git a/presets/tests/250-wavecode.milk b/presets/tests/250-wavecode.milk old mode 100755 new mode 100644 index ef3f0429f..f615f259c --- a/presets/tests/250-wavecode.milk +++ b/presets/tests/250-wavecode.milk @@ -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 \ No newline at end of file diff --git a/presets/tests/251-wavecode-spectrum.milk b/presets/tests/251-wavecode-spectrum.milk new file mode 100644 index 000000000..a49b7bbbf --- /dev/null +++ b/presets/tests/251-wavecode-spectrum.milk @@ -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; + diff --git a/presets/tests/300-beatdetect-bassmidtreb.milk b/presets/tests/300-beatdetect-bassmidtreb.milk index 970a25a97..3c8cb961b 100644 --- a/presets/tests/300-beatdetect-bassmidtreb.milk +++ b/presets/tests/300-beatdetect-bassmidtreb.milk @@ -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 diff --git a/src/libprojectM/MilkdropPresetFactory/MilkdropPreset.cpp b/src/libprojectM/MilkdropPresetFactory/MilkdropPreset.cpp index fce094234..feda27775 100755 --- a/src/libprojectM/MilkdropPresetFactory/MilkdropPreset.cpp +++ b/src/libprojectM/MilkdropPresetFactory/MilkdropPreset.cpp @@ -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 diff --git a/src/libprojectM/MilkdropPresetFactory/PresetFrameIO.cpp b/src/libprojectM/MilkdropPresetFactory/PresetFrameIO.cpp index 79d1ac37b..d5c746346 100644 --- a/src/libprojectM/MilkdropPresetFactory/PresetFrameIO.cpp +++ b/src/libprojectM/MilkdropPresetFactory/PresetFrameIO.cpp @@ -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; diff --git a/src/libprojectM/PCM.cpp b/src/libprojectM/PCM.cpp index 2ed8c2a2d..416477d01 100755 --- a/src/libprojectM/PCM.cpp +++ b/src/libprojectM/PCM.cpp @@ -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 #include +#include #include "Common.hpp" #include "wipemalloc.h" @@ -34,371 +34,526 @@ #include "PCM.hpp" #include -int PCM::maxsamples = 2048; -//initPCM(int samples) -// -//Initializes the PCM buffer to -// number of samples specified. -#include -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= 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 -void PCM::addPCMfloat(const float *PCMdata, int samples) + +void PCM::addPCMfloat(const float *PCMdata, size_t samples) { - int i,j; - - for(i=0;iupdateLevel(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; iupdateLevel(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;iupdateLevel(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; iupdateLevel(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; iupdateLevel(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 ; imaxsamples) - 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 = samplesmaxsamples) 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;imaxsamples) 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;imaxsamples) 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;imaxsamples) 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 ; imx ? mx : a + +#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 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 diff --git a/src/libprojectM/PCM.hpp b/src/libprojectM/PCM.hpp index 916da0bfa..14c944e6e 100755 --- a/src/libprojectM/PCM.hpp +++ b/src/libprojectM/PCM.hpp @@ -29,53 +29,100 @@ #ifndef _PCM_H #define _PCM_H +#include #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 */ diff --git a/src/libprojectM/Renderer/BeatDetect.cpp b/src/libprojectM/Renderer/BeatDetect.cpp index f273919a0..eeee81518 100755 --- a/src/libprojectM/Renderer/BeatDetect.cpp +++ b/src/libprojectM/Renderer/BeatDetect.cpp @@ -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] ; i100) bass_att=100; if (bass >100) bass=100; if (mid_att>100) mid_att=100; diff --git a/src/libprojectM/Renderer/BeatDetect.hpp b/src/libprojectM/Renderer/BeatDetect.hpp index adf5afa9f..01d16483e 100755 --- a/src/libprojectM/Renderer/BeatDetect.hpp +++ b/src/libprojectM/Renderer/BeatDetect.hpp @@ -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; diff --git a/src/libprojectM/Renderer/MilkdropWaveform.cpp b/src/libprojectM/Renderer/MilkdropWaveform.cpp index 485378c26..0bec39c5f 100644 --- a/src/libprojectM/Renderer/MilkdropWaveform.cpp +++ b/src/libprojectM/Renderer/MilkdropWaveform.cpp @@ -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;i1) { @@ -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; itexsize*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;igetPCMScale(); + 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(), mult)); - std::transform(&value2[0], &value2[samples_count], &value2[0], bind2nd(std::multiplies(), mult)); -#else - std::transform(&value1[0], &value1[samples_count], &value1[0], std::bind(std::multiplies(), std::placeholders::_1, mult)); - std::transform(&value2[0], &value2[samples_count], &value2[0], std::bind(std::multiplies(), 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; - } +} diff --git a/src/libprojectM/TestRunner.cpp b/src/libprojectM/TestRunner.cpp index 91c5821af..a44048490 100644 --- a/src/libprojectM/TestRunner.cpp +++ b/src/libprojectM/TestRunner.cpp @@ -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;