mirror of
https://github.com/clangen/musikcube.git
synced 2025-03-14 04:18:36 +00:00
Use VLC's lighter weight FFT.
This commit is contained in:
parent
410bdbc014
commit
08a876edb2
2
src/3rdparty/3rdparty.vcxproj
vendored
2
src/3rdparty/3rdparty.vcxproj
vendored
@ -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" />
|
||||
|
6
src/3rdparty/3rdparty.vcxproj.filters
vendored
6
src/3rdparty/3rdparty.vcxproj.filters
vendored
@ -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
53
src/3rdparty/include/fft.h
vendored
Normal 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
210
src/3rdparty/src/fft.cpp
vendored
Normal 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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
};
|
||||
|
||||
} } }
|
||||
|
Loading…
x
Reference in New Issue
Block a user