Use VLC's lighter weight FFT.

This commit is contained in:
casey langen 2016-11-27 08:26:30 -08:00
parent 410bdbc014
commit 08a876edb2
6 changed files with 331 additions and 43 deletions

View File

@ -84,6 +84,7 @@
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="src\fft.cpp" />
<ClCompile Include="src\sqlite\sqlite3.c">
<WarningLevel Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Level1</WarningLevel>
</ClCompile>
@ -91,6 +92,7 @@
<ClCompile Include="src\wcwidth.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\fft.h" />
<ClInclude Include="include\sqlite\sqlite3.h" />
<ClInclude Include="include\sqlite\sqlite3ext.h" />
<ClInclude Include="include\sigslot\sigslot.h" />

View File

@ -25,6 +25,9 @@
<ClCompile Include="src\wcwidth.c">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="src\fft.cpp">
<Filter>src</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\sqlite\sqlite3.h">
@ -42,5 +45,8 @@
<ClInclude Include="include\wcwidth.h">
<Filter>src</Filter>
</ClInclude>
<ClInclude Include="include\fft.h">
<Filter>src</Filter>
</ClInclude>
</ItemGroup>
</Project>

53
src/3rdparty/include/fft.h vendored Normal file
View File

@ -0,0 +1,53 @@
/*****************************************************************************
* fft.h: Headers for iterative implementation of a FFT
*****************************************************************************
* $Id$
*
* Mainly taken from XMMS's code
*
* Authors: Richard Boulton <richard@tartarus.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#pragma once
#define FFT_BUFFER_SIZE_LOG 9
#define FFT_BUFFER_SIZE (1 << FFT_BUFFER_SIZE_LOG)
extern "C" {
/* sound sample - should be an signed 16 bit value */
typedef float sound_sample;
struct _struct_fft_state {
/* Temporary data stores to perform FFT in. */
float real[FFT_BUFFER_SIZE];
float imag[FFT_BUFFER_SIZE];
/* */
unsigned int bitReverse[FFT_BUFFER_SIZE];
/* The next two tables could be made to use less space in memory, since they
* overlap hugely, but hey. */
float sintable[FFT_BUFFER_SIZE / 2];
float costable[FFT_BUFFER_SIZE / 2];
};
/* FFT prototypes */
typedef struct _struct_fft_state fft_state;
fft_state *visual_fft_init(void);
void fft_perform(const sound_sample *input, float *output, fft_state *state);
void fft_close(fft_state *state);
}

210
src/3rdparty/src/fft.cpp vendored Normal file
View File

@ -0,0 +1,210 @@
/*****************************************************************************
* fft.c: Iterative implementation of a FFT
*****************************************************************************
* $Id$
*
* Mainly taken from XMMS's code
*
* Authors: Richard Boulton <richard@tartarus.org>
* Ralph Loader <suckfish@ihug.co.nz>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#include <stdlib.h>
#include <fft.h>
#include <math.h>
#ifndef PI
#ifdef M_PI
#define PI M_PI
#else
#define PI 3.14159265358979323846 /* pi */
#endif
#endif
#define MAX_SIGNED_SHORT 32767
/******************************************************************************
* Local prototypes
*****************************************************************************/
static void fft_prepare(const sound_sample *input, float * re, float * im,
const unsigned int *bitReverse);
static void fft_calculate(float * re, float * im,
const float *costable, const float *sintable );
static void fft_output(const float *re, const float *im, float *output);
static int reverseBits(unsigned int initial);
/*****************************************************************************
* These functions are the ones called externally
*****************************************************************************/
/*
* Initialisation routine - sets up tables and space to work in.
* Returns a pointer to internal state, to be used when performing calls.
* On error, returns NULL.
* The pointer should be freed when it is finished with, by fft_close().
*/
extern fft_state *visual_fft_init(void)
{
fft_state *p_state;
unsigned int i;
p_state = (fft_state*) malloc( sizeof(*p_state) );
if(! p_state )
return NULL;
for(i = 0; i < FFT_BUFFER_SIZE; i++)
{
p_state->bitReverse[i] = reverseBits(i);
}
for(i = 0; i < FFT_BUFFER_SIZE / 2; i++)
{
float j = 2 * PI * i / FFT_BUFFER_SIZE;
p_state->costable[i] = cos(j);
p_state->sintable[i] = sin(j);
}
return p_state;
}
/*
* Do all the steps of the FFT, taking as input sound data (as described in
* sound.h) and returning the intensities of each frequency as floats in the
* range 0 to ((FFT_BUFFER_SIZE / 2) * 32768) ^ 2
*
* The input array is assumed to have FFT_BUFFER_SIZE elements,
* and the output array is assumed to have (FFT_BUFFER_SIZE / 2 + 1) elements.
* state is a (non-NULL) pointer returned by visual_fft_init.
*/
extern void fft_perform(const sound_sample *input, float *output, fft_state *state) {
/* Convert data from sound format to be ready for FFT */
fft_prepare(input, state->real, state->imag, state->bitReverse );
/* Do the actual FFT */
fft_calculate(state->real, state->imag, state->costable, state->sintable);
/* Convert the FFT output into intensities */
fft_output(state->real, state->imag, output);
}
/*
* Free the state.
*/
extern void fft_close(fft_state *state) {
free( state );
}
/*****************************************************************************
* These functions are called from the other ones
*****************************************************************************/
/*
* Prepare data to perform an FFT on
*/
static void fft_prepare( const sound_sample *input, float * re, float * im,
const unsigned int *bitReverse ) {
unsigned int i;
float *p_real = re;
float *p_imag = im;
short v;
/* Get input, in reverse bit order */
for(i = 0; i < FFT_BUFFER_SIZE; i++)
{
*p_real++ = input[bitReverse[i]] * MAX_SIGNED_SHORT;
*p_imag++ = 0;
}
}
/*
* Take result of an FFT and calculate the intensities of each frequency
* Note: only produces half as many data points as the input had.
*/
static void fft_output(const float * re, const float * im, float *output)
{
float *p_output = output;
const float *p_real = re;
const float *p_imag = im;
float *p_end = output + FFT_BUFFER_SIZE / 2;
while(p_output <= p_end)
{
*p_output = (*p_real * *p_real) + (*p_imag * *p_imag);
p_output++; p_real++; p_imag++;
}
/* Do divisions to keep the constant and highest frequency terms in scale
* with the other terms. */
*output /= 4;
*p_end /= 4;
}
/*
* Actually perform the FFT
*/
static void fft_calculate(float * re, float * im, const float *costable, const float *sintable )
{
unsigned int i, j, k;
unsigned int exchanges;
float fact_real, fact_imag;
float tmp_real, tmp_imag;
unsigned int factfact;
/* Set up some variables to reduce calculation in the loops */
exchanges = 1;
factfact = FFT_BUFFER_SIZE / 2;
/* Loop through the divide and conquer steps */
for(i = FFT_BUFFER_SIZE_LOG; i != 0; i--) {
/* In this step, we have 2 ^ (i - 1) exchange groups, each with
* 2 ^ (FFT_BUFFER_SIZE_LOG - i) exchanges
*/
/* Loop through the exchanges in a group */
for(j = 0; j != exchanges; j++) {
/* Work out factor for this exchange
* factor ^ (exchanges) = -1
* So, real = cos(j * PI / exchanges),
* imag = sin(j * PI / exchanges)
*/
fact_real = costable[j * factfact];
fact_imag = sintable[j * factfact];
/* Loop through all the exchange groups */
for(k = j; k < FFT_BUFFER_SIZE; k += exchanges << 1) {
int k1 = k + exchanges;
tmp_real = fact_real * re[k1] - fact_imag * im[k1];
tmp_imag = fact_real * im[k1] + fact_imag * re[k1];
re[k1] = re[k] - tmp_real;
im[k1] = im[k] - tmp_imag;
re[k] += tmp_real;
im[k] += tmp_imag;
}
}
exchanges <<= 1;
factfact >>= 1;
}
}
static int reverseBits(unsigned int initial)
{
unsigned int reversed = 0, loop;
for(loop = 0; loop < FFT_BUFFER_SIZE_LOG; loop++) {
reversed <<= 1;
reversed += (initial & 1);
initial >>= 1;
}
return reversed;
}

View File

@ -35,7 +35,7 @@
#include "pch.hpp"
#include <core/audio/Buffer.h>
#include <fftw3.h>
#include <fft.h>
#define DEBUG 0
@ -56,8 +56,7 @@ Buffer::Buffer(void)
, sampleSize(0)
, internalBufferSize(0)
, sampleRate(44100)
, channels(2)
, fft(nullptr) {
, channels(2) {
}
@ -100,10 +99,8 @@ void Buffer::SetSamples(long samples) {
}
void Buffer::CopyFormat(BufferPtr fromBuffer) {
this->sampleSize = fromBuffer->Samples();
this->channels = fromBuffer->Channels();
this->sampleRate = fromBuffer->SampleRate();
this->ResizeBuffer();
}
void Buffer::ResizeBuffer() {
@ -131,6 +128,21 @@ void Buffer::SetPosition(double position) {
this->position = position;
}
void Buffer::Copy(float* buffer, long samples) {
if (samples > this->internalBufferSize) {
float *newBuffer = new float[samples];
CopyFloat(newBuffer, buffer, samples);
delete this->buffer;
this->buffer = newBuffer;
this->internalBufferSize = samples;
}
else {
CopyFloat(this->buffer, buffer, samples);
}
this->sampleSize = samples;
}
bool Buffer::Append(BufferPtr appendBuffer) {
if (this->SampleRate() == appendBuffer->SampleRate() &&
this->Channels() == appendBuffer->Channels())
@ -176,42 +188,46 @@ bool Buffer::Append(BufferPtr appendBuffer) {
return false;
}
static inline int closestPowerOfTwo(int x) {
/* http://stackoverflow.com/a/2681094 */
x = x | (x >> 1);
x = x | (x >> 2);
x = x | (x >> 4);
x = x | (x >> 8);
x = x | (x >> 16);
return x - (x >> 1);
}
static inline bool isPowerOfTwo(unsigned int x) {
return ((x != 0) && ((x & (~x + 1)) == x));
}
bool Buffer::Fft(float* buffer, int size) {
if (buffer && size > 0 && this->sampleSize > size && isPowerOfTwo(size)) {
int n = closestPowerOfTwo(this->sampleSize);
fftwf_complex *out = (fftwf_complex*) fftw_malloc(sizeof(fftwf_complex) * n);
fftwf_plan plan = fftwf_plan_dft_r2c_1d(n, this->buffer, out, FFTW_MEASURE);
fftwf_execute(plan);
fftwf_destroy_plan(plan);
int w = 0; /* write offset */
int c = n / size; /* bins to average */
for (int i = 0; i < n; i+=c) {
float sum = 0.0f;
for (int j = 0; j < c; j++) {
sum += out[1][i + j];
}
buffer[w++] = sum / c;
}
fftwf_free(out);
return true;
if (this->sampleSize < FFT_BUFFER_SIZE || size != FFT_BUFFER_SIZE) {
return false;
}
return false;
int count = this->sampleSize / FFT_BUFFER_SIZE;
/* de-interlace the audio first */
float* deinterlaced = new float[FFT_BUFFER_SIZE * count];
for (int i = 0; i < count * FFT_BUFFER_SIZE; i++) {
const int to = ((i % this->channels) * FFT_BUFFER_SIZE) + (i / count);
deinterlaced[to] = this->buffer[i];
}
/* if there's more than one set of interlaced data then
allocate a scratch buffer. we'll use this for averaging
the result */
float* scratch = nullptr;
if (count > 1) {
scratch = new float[FFT_BUFFER_SIZE];
}
fft_state* state = visual_fft_init();
/* first FFT will go directly to the output buffer */
fft_perform(this->buffer, buffer, state);
for (int i = 1; i < count; i++) {
fft_perform(&deinterlaced[i * FFT_BUFFER_SIZE], scratch, state);
/* average with the previous read */
for (int j = 0; j < FFT_BUFFER_SIZE; j++) {
buffer[j] = (scratch[j] + buffer[j]) / 2;
}
}
delete[] deinterlaced;
delete[] scratch;
fft_close(state);
return true;
}

View File

@ -50,7 +50,8 @@ namespace musik { namespace core { namespace audio {
public:
static BufferPtr Create();
~Buffer();
virtual ~Buffer();
virtual long SampleRate() const;
virtual void SetSampleRate(long sampleRate);
@ -61,9 +62,10 @@ namespace musik { namespace core { namespace audio {
virtual void SetSamples(long samples);
virtual long Bytes() const;
virtual double Position() const;
void SetPosition(double position);
virtual bool Fft(float* buffer, int size);
void SetPosition(double position);
void Copy(float* buffer, long samples);
bool Append(BufferPtr appendBuffer);
void CopyFormat(BufferPtr fromBuffer);
@ -76,7 +78,6 @@ namespace musik { namespace core { namespace audio {
long sampleRate;
int channels;
double position;
float* fft;
};
} } }