parent
6480ebadde
commit
d51bdd6b6a
@ -0,0 +1,232 @@ |
|||||||
|
//
|
||||||
|
// Created by frank on 2021/8/16.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "execute_fft.h" |
||||||
|
|
||||||
|
#include <android/log.h> |
||||||
|
#define LOG_TAG "execute_fft" |
||||||
|
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, \ |
||||||
|
__VA_ARGS__)) |
||||||
|
|
||||||
|
#define SPECTRUM_WIDTH 4.f |
||||||
|
#define NB_BANDS 20 |
||||||
|
#define ROTATION_INCREMENT .1f |
||||||
|
#define BAR_DECREMENT .075f |
||||||
|
#define ROTATION_MAX 20 |
||||||
|
|
||||||
|
static inline void block_CopyProperties(block_t *dst, const block_t *src) |
||||||
|
{ |
||||||
|
dst->i_dts = src->i_dts; |
||||||
|
dst->i_pts = src->i_pts; |
||||||
|
dst->i_flags = src->i_flags; |
||||||
|
dst->i_length = src->i_length; |
||||||
|
dst->i_nb_samples = src->i_nb_samples; |
||||||
|
} |
||||||
|
|
||||||
|
static inline block_t *block_Duplicate(const block_t *p_block) |
||||||
|
{ |
||||||
|
// block_t *p_dup = block_Alloc(p_block->i_buffer);//TODO
|
||||||
|
block_t *p_dup = (block_t*) malloc(sizeof(block_t)); |
||||||
|
if(p_dup == NULL) return NULL; |
||||||
|
p_dup->i_buffer = p_block->i_buffer; |
||||||
|
p_dup->p_buffer = (uint8_t*) malloc(p_block->i_buffer);//TODO
|
||||||
|
|
||||||
|
block_CopyProperties(p_dup, p_block); |
||||||
|
memcpy(p_dup->p_buffer, p_block->p_buffer, p_block->i_buffer); |
||||||
|
|
||||||
|
return p_dup; |
||||||
|
} |
||||||
|
|
||||||
|
/*static*/ int Open(filter_sys_t *p_sys) |
||||||
|
{ |
||||||
|
if (p_sys == NULL) |
||||||
|
return VLC_ENOMEM; |
||||||
|
|
||||||
|
/* Create the object for the thread */ |
||||||
|
p_sys->i_channels = 2;//TODO
|
||||||
|
p_sys->i_prev_nb_samples = 0; |
||||||
|
p_sys->p_prev_s16_buff = NULL; |
||||||
|
|
||||||
|
p_sys->f_rotationAngle = 0; |
||||||
|
p_sys->f_rotationIncrement = ROTATION_INCREMENT; |
||||||
|
|
||||||
|
window_param *w_param = (window_param*) malloc(sizeof(window_param)); |
||||||
|
p_sys->wind_param = *w_param;//TODO
|
||||||
|
|
||||||
|
/* Fetch the FFT window parameters */ |
||||||
|
window_get_param(/*VLC_OBJECT( p_filter )*/NULL, &p_sys->wind_param);//TODO
|
||||||
|
|
||||||
|
/* Create the FIFO for the audio data. */ |
||||||
|
vlc_queue_Init(&p_sys->queue, offsetof (block_t, p_next)); |
||||||
|
p_sys->dead = false; |
||||||
|
|
||||||
|
/* Create the thread */ |
||||||
|
// if (vlc_clone(&p_sys->thread, Thread, p_filter,
|
||||||
|
// VLC_THREAD_PRIORITY_VIDEO)) {
|
||||||
|
// return VLC_ENOMEM;
|
||||||
|
// }
|
||||||
|
|
||||||
|
pthread_create (&p_sys->thread, NULL, Thread, p_sys); |
||||||
|
|
||||||
|
return VLC_SUCCESS; |
||||||
|
} |
||||||
|
|
||||||
|
/*static*/ block_t *filter_audio(filter_sys_t *p_sys, block_t *p_in_buf) |
||||||
|
{ |
||||||
|
vlc_queue_Enqueue(&p_sys->queue, block_Duplicate(p_in_buf)); |
||||||
|
return p_in_buf; |
||||||
|
} |
||||||
|
|
||||||
|
/*static*/ void Close(filter_sys_t *p_filter) |
||||||
|
{ |
||||||
|
filter_sys_t *p_sys = p_filter; |
||||||
|
|
||||||
|
/* Terminate the thread. */ |
||||||
|
vlc_queue_Kill(&p_sys->queue, &p_sys->dead); |
||||||
|
// vlc_join(p_sys->thread, NULL);
|
||||||
|
pthread_join(p_sys->thread, NULL); |
||||||
|
|
||||||
|
free(p_sys->p_prev_s16_buff); |
||||||
|
} |
||||||
|
|
||||||
|
static void *Thread(void *p_data) |
||||||
|
{ |
||||||
|
filter_sys_t *p_sys = (filter_sys_t*)p_data; |
||||||
|
block_t *block; |
||||||
|
|
||||||
|
float height[NB_BANDS] = {0}; |
||||||
|
LOGE("start FFT thread..."); |
||||||
|
while ((block = vlc_queue_DequeueKillable(&p_sys->queue, &p_sys->dead))) |
||||||
|
{ |
||||||
|
LOGE("running FFT transform..."); |
||||||
|
unsigned win_width, win_height; |
||||||
|
|
||||||
|
/* Horizontal scale for 20-band equalizer */ |
||||||
|
const unsigned xscale[] = {0,1,2,3,4,5,6,7,8,11,15,20,27, |
||||||
|
36,47,62,82,107,141,184,255}; |
||||||
|
|
||||||
|
fft_state *p_state = NULL; /* internal FFT data */ |
||||||
|
DEFINE_WIND_CONTEXT(wind_ctx); /* internal window data */ |
||||||
|
|
||||||
|
unsigned i, j; |
||||||
|
float p_output[FFT_BUFFER_SIZE]; /* Raw FFT Result */ |
||||||
|
int16_t p_buffer1[FFT_BUFFER_SIZE]; /* Buffer on which we perform
|
||||||
|
the FFT (first channel) */ |
||||||
|
int16_t p_dest[FFT_BUFFER_SIZE]; /* Adapted FFT result */ |
||||||
|
float *p_buffl = (float*)block->p_buffer; /* Original buffer */ |
||||||
|
|
||||||
|
int16_t *p_buffs; /* int16_t converted buffer */ |
||||||
|
int16_t *p_s16_buff; /* int16_t converted buffer */ |
||||||
|
|
||||||
|
if (!block->i_nb_samples) { |
||||||
|
LOGE("no samples yet..."); |
||||||
|
goto release; |
||||||
|
} |
||||||
|
|
||||||
|
/* Allocate the buffer only if the number of samples change */ |
||||||
|
if (block->i_nb_samples != p_sys->i_prev_nb_samples) |
||||||
|
{ |
||||||
|
free(p_sys->p_prev_s16_buff); |
||||||
|
p_sys->p_prev_s16_buff = malloc(block->i_nb_samples * |
||||||
|
p_sys->i_channels * |
||||||
|
sizeof(int16_t)); |
||||||
|
if (!p_sys->p_prev_s16_buff) |
||||||
|
goto release; |
||||||
|
p_sys->i_prev_nb_samples = block->i_nb_samples; |
||||||
|
} |
||||||
|
p_buffs = p_s16_buff = p_sys->p_prev_s16_buff; |
||||||
|
|
||||||
|
/* Convert the buffer to int16_t */ |
||||||
|
for (i = block->i_nb_samples * p_sys->i_channels; i--;) |
||||||
|
{ |
||||||
|
union {float f; int32_t i;} u; |
||||||
|
|
||||||
|
u.f = *p_buffl + 384.f; |
||||||
|
if (u.i > 0x43c07fff) |
||||||
|
*p_buffs = 32767; |
||||||
|
else if (u.i < 0x43bf8000) |
||||||
|
*p_buffs = -32768; |
||||||
|
else |
||||||
|
*p_buffs = u.i - 0x43c00000; |
||||||
|
|
||||||
|
p_buffl++; p_buffs++; |
||||||
|
} |
||||||
|
p_state = visual_fft_init(); |
||||||
|
if (!p_state) |
||||||
|
{ |
||||||
|
LOGE("unable to initialize FFT transform..."); |
||||||
|
goto release; |
||||||
|
} |
||||||
|
if (!window_init(FFT_BUFFER_SIZE, &p_sys->wind_param, &wind_ctx)) |
||||||
|
{ |
||||||
|
LOGE("unable to initialize FFT window..."); |
||||||
|
goto release; |
||||||
|
} |
||||||
|
p_buffs = p_s16_buff; |
||||||
|
for (i = 0 ; i < FFT_BUFFER_SIZE; i++) |
||||||
|
{ |
||||||
|
p_output[i] = 0; |
||||||
|
p_buffer1[i] = *p_buffs; |
||||||
|
|
||||||
|
p_buffs += p_sys->i_channels; |
||||||
|
if (p_buffs >= &p_s16_buff[block->i_nb_samples * p_sys->i_channels]) |
||||||
|
p_buffs = p_s16_buff; |
||||||
|
} |
||||||
|
window_scale_in_place (p_buffer1, &wind_ctx); |
||||||
|
fft_perform (p_buffer1, p_output, p_state); |
||||||
|
|
||||||
|
for (i = 0; i< FFT_BUFFER_SIZE; ++i) |
||||||
|
p_dest[i] = p_output[i] * (2 ^ 16) |
||||||
|
/ ((FFT_BUFFER_SIZE / 2 * 32768) ^ 2); |
||||||
|
|
||||||
|
for (i = 0 ; i < NB_BANDS; i++) |
||||||
|
{ |
||||||
|
/* Decrease the previous size of the bar. */ |
||||||
|
height[i] -= BAR_DECREMENT; |
||||||
|
if (height[i] < 0) |
||||||
|
height[i] = 0; |
||||||
|
|
||||||
|
int y = 0; |
||||||
|
/* We search the maximum on one scale
|
||||||
|
to determine the current size of the bar. */ |
||||||
|
for (j = xscale[i]; j < xscale[i + 1]; j++) |
||||||
|
{ |
||||||
|
if (p_dest[j] > y) |
||||||
|
y = p_dest[j]; |
||||||
|
} |
||||||
|
/* Calculate the height of the bar */ |
||||||
|
float new_height = y != 0 ? logf(y) * 0.4f : 0; |
||||||
|
height[i] = new_height > height[i] |
||||||
|
? new_height : height[i]; |
||||||
|
} |
||||||
|
|
||||||
|
/* Determine the camera rotation angle. */ |
||||||
|
p_sys->f_rotationAngle += p_sys->f_rotationIncrement; |
||||||
|
if (p_sys->f_rotationAngle <= -ROTATION_MAX) |
||||||
|
p_sys->f_rotationIncrement = ROTATION_INCREMENT; |
||||||
|
else if (p_sys->f_rotationAngle >= ROTATION_MAX) |
||||||
|
p_sys->f_rotationIncrement = -ROTATION_INCREMENT; |
||||||
|
|
||||||
|
/* Render the frame. */ |
||||||
|
// glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
// glPushMatrix();
|
||||||
|
// glRotatef(p_sys->f_rotationAngle, 0, 1, 0);
|
||||||
|
// drawBars(height);
|
||||||
|
// glPopMatrix();
|
||||||
|
|
||||||
|
/* Wait to swapp the frame on time. */ |
||||||
|
// vlc_tick_wait(block->i_pts + (block->i_length / 2));
|
||||||
|
// vlc_gl_Swap(gl);
|
||||||
|
usleep(block->i_pts + (block->i_length / 2)); |
||||||
|
|
||||||
|
release: |
||||||
|
window_close(&wind_ctx); |
||||||
|
fft_close(p_state); |
||||||
|
// block_Release(block);//TODO
|
||||||
|
if (block->p_buffer) free(block->p_buffer); |
||||||
|
free(block); |
||||||
|
} |
||||||
|
|
||||||
|
return NULL; |
||||||
|
} |
@ -0,0 +1,77 @@ |
|||||||
|
//
|
||||||
|
// Created by frank on 2021/8/16.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef PLAYER_CORE_RUN_FFT_H |
||||||
|
#define PLAYER_CORE_RUN_FFT_H |
||||||
|
|
||||||
|
#include "vlc_queue.h" |
||||||
|
#include "fft.h" |
||||||
|
#include "window.h" |
||||||
|
|
||||||
|
#include <math.h> |
||||||
|
#include <unistd.h> |
||||||
|
|
||||||
|
/** No error */ |
||||||
|
#define VLC_SUCCESS (-0) |
||||||
|
/** Unspecified error */ |
||||||
|
#define VLC_EGENERIC (-1) |
||||||
|
/** Not enough memory */ |
||||||
|
#define VLC_ENOMEM (-2) |
||||||
|
|
||||||
|
typedef int64_t vlc_tick_t; |
||||||
|
|
||||||
|
typedef struct block_t block_t; |
||||||
|
|
||||||
|
struct vlc_block_callbacks |
||||||
|
{ |
||||||
|
void (*free)(block_t *); |
||||||
|
}; |
||||||
|
|
||||||
|
struct block_t |
||||||
|
{ |
||||||
|
block_t *p_next; |
||||||
|
|
||||||
|
uint8_t *p_buffer; /**< Payload start */ |
||||||
|
size_t i_buffer; /**< Payload length */ |
||||||
|
uint8_t *p_start; /**< Buffer start */ |
||||||
|
size_t i_size; /**< Buffer total size */ |
||||||
|
|
||||||
|
uint32_t i_flags; |
||||||
|
unsigned i_nb_samples; /* Used for audio */ |
||||||
|
|
||||||
|
vlc_tick_t i_pts; |
||||||
|
vlc_tick_t i_dts; |
||||||
|
vlc_tick_t i_length; |
||||||
|
|
||||||
|
const struct vlc_block_callbacks *cbs; |
||||||
|
}; |
||||||
|
|
||||||
|
typedef struct |
||||||
|
{ |
||||||
|
// vlc_thread_t thread;
|
||||||
|
pthread_t thread; |
||||||
|
|
||||||
|
/* Audio data */ |
||||||
|
vlc_queue_t queue; |
||||||
|
bool dead; |
||||||
|
unsigned i_channels; |
||||||
|
unsigned i_prev_nb_samples; |
||||||
|
int16_t *p_prev_s16_buff; |
||||||
|
|
||||||
|
float f_rotationAngle; |
||||||
|
float f_rotationIncrement; |
||||||
|
|
||||||
|
/* FFT window parameters */ |
||||||
|
window_param wind_param; |
||||||
|
} filter_sys_t; |
||||||
|
|
||||||
|
static void *Thread(void *); |
||||||
|
|
||||||
|
/*static*/ int Open(filter_sys_t *p_sys); |
||||||
|
|
||||||
|
/*static*/ block_t *filter_audio(filter_sys_t *p_sys, block_t *p_in_buf); |
||||||
|
|
||||||
|
/*static*/ void Close(filter_sys_t *p_filter); |
||||||
|
|
||||||
|
#endif //PLAYER_CORE_RUN_FFT_H
|
@ -0,0 +1,206 @@ |
|||||||
|
/*****************************************************************************
|
||||||
|
* fft.c: Iterative implementation of a FFT |
||||||
|
***************************************************************************** |
||||||
|
* |
||||||
|
* 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 |
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
* 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(). |
||||||
|
*/ |
||||||
|
fft_state *visual_fft_init(void) |
||||||
|
{ |
||||||
|
fft_state *p_state; |
||||||
|
unsigned int i; |
||||||
|
|
||||||
|
p_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. |
||||||
|
*/ |
||||||
|
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. |
||||||
|
*/ |
||||||
|
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; |
||||||
|
|
||||||
|
/* Get input, in reverse bit order */ |
||||||
|
for(i = 0; i < FFT_BUFFER_SIZE; i++) |
||||||
|
{ |
||||||
|
*p_real++ = input[bitReverse[i]]; |
||||||
|
*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; |
||||||
|
} |
@ -0,0 +1,55 @@ |
|||||||
|
/*****************************************************************************
|
||||||
|
* fft.h: Headers for iterative implementation of a FFT |
||||||
|
***************************************************************************** |
||||||
|
* |
||||||
|
* 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. |
||||||
|
*****************************************************************************/ |
||||||
|
|
||||||
|
#ifndef VLC_VISUAL_FFT_H_ |
||||||
|
#define VLC_VISUAL_FFT_H_ |
||||||
|
|
||||||
|
#define FFT_BUFFER_SIZE_LOG 9 |
||||||
|
|
||||||
|
#define FFT_BUFFER_SIZE (1 << FFT_BUFFER_SIZE_LOG) |
||||||
|
|
||||||
|
/* sound sample - should be an signed 16 bit value */ |
||||||
|
typedef short int 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); |
||||||
|
|
||||||
|
|
||||||
|
#endif /* include-guard */ |
@ -0,0 +1,155 @@ |
|||||||
|
/*****************************************************************************
|
||||||
|
* queue.c: generic queue (FIFO) |
||||||
|
***************************************************************************** |
||||||
|
* Copyright (C) 2020 Rémi Denis-Courmont |
||||||
|
* |
||||||
|
* This program is free software; you can redistribute it and/or modify it |
||||||
|
* under the terms of the GNU Lesser General Public License as published by |
||||||
|
* the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser 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 <assert.h> |
||||||
|
#include <stdint.h> |
||||||
|
#include <stdlib.h> |
||||||
|
|
||||||
|
#include "vlc_queue.h" |
||||||
|
|
||||||
|
/* Opaque struct type.
|
||||||
|
* |
||||||
|
* ISO C uses the same representation for all pointer-to-struct types. |
||||||
|
* Still different pointer types are not compatible, i.e. cannot alias. |
||||||
|
* So use memcpy() to read/write pointer values. |
||||||
|
*/ |
||||||
|
struct vlc_queue_entry; |
||||||
|
|
||||||
|
static void entry_set(struct vlc_queue_entry **pp, struct vlc_queue_entry *e) |
||||||
|
{ |
||||||
|
memcpy(pp, &e, sizeof (e)); |
||||||
|
} |
||||||
|
|
||||||
|
static struct vlc_queue_entry *entry_get(struct vlc_queue_entry *const *pp) |
||||||
|
{ |
||||||
|
struct vlc_queue_entry *e; |
||||||
|
|
||||||
|
memcpy(&e, pp, sizeof (e)); |
||||||
|
return e; |
||||||
|
} |
||||||
|
|
||||||
|
static struct vlc_queue_entry **next_p(const struct vlc_queue_entry *e, |
||||||
|
ptrdiff_t offset) |
||||||
|
{ |
||||||
|
return (struct vlc_queue_entry **)(((unsigned char *)e) + offset); |
||||||
|
} |
||||||
|
|
||||||
|
static void next_set(struct vlc_queue_entry *e, struct vlc_queue_entry *next, |
||||||
|
ptrdiff_t offset) |
||||||
|
{ |
||||||
|
entry_set(next_p(e, offset), next); |
||||||
|
} |
||||||
|
|
||||||
|
static struct vlc_queue_entry *next_get(const struct vlc_queue_entry *e, |
||||||
|
ptrdiff_t offset) |
||||||
|
{ |
||||||
|
return entry_get(next_p(e, offset)); |
||||||
|
} |
||||||
|
|
||||||
|
void vlc_queue_Init(vlc_queue_t *q, ptrdiff_t next_offset) |
||||||
|
{ |
||||||
|
q->first = NULL; |
||||||
|
q->lastp = &q->first; |
||||||
|
q->next_offset = next_offset; |
||||||
|
pthread_mutex_init(&q->lock, NULL); |
||||||
|
pthread_cond_init(&q->wait, NULL); |
||||||
|
} |
||||||
|
|
||||||
|
void vlc_queue_EnqueueUnlocked(vlc_queue_t *q, void *entry) |
||||||
|
{ |
||||||
|
struct vlc_queue_entry **lastp; |
||||||
|
const ptrdiff_t offset = q->next_offset; |
||||||
|
|
||||||
|
// vlc_mutex_assert(&q->lock);
|
||||||
|
assert(entry_get(q->lastp) == NULL); |
||||||
|
entry_set(q->lastp, entry); |
||||||
|
|
||||||
|
for (lastp = q->lastp; entry != NULL; entry = next_get(entry, offset)) |
||||||
|
lastp = next_p(entry, offset); |
||||||
|
|
||||||
|
q->lastp = lastp; |
||||||
|
vlc_queue_Signal(q); |
||||||
|
} |
||||||
|
|
||||||
|
void *vlc_queue_DequeueUnlocked(vlc_queue_t *q) |
||||||
|
{ |
||||||
|
// vlc_mutex_assert(&q->lock);
|
||||||
|
|
||||||
|
void *entry = q->first; |
||||||
|
const ptrdiff_t offset = q->next_offset; |
||||||
|
|
||||||
|
if (entry != NULL) { |
||||||
|
struct vlc_queue_entry *next = next_get(entry, offset); |
||||||
|
|
||||||
|
next_set(entry, NULL, offset); |
||||||
|
q->first = next; |
||||||
|
|
||||||
|
if (next == NULL) |
||||||
|
q->lastp = &q->first; |
||||||
|
} |
||||||
|
|
||||||
|
return entry; |
||||||
|
} |
||||||
|
|
||||||
|
void *vlc_queue_DequeueAllUnlocked(vlc_queue_t *q) |
||||||
|
{ |
||||||
|
// vlc_mutex_assert(&q->lock);
|
||||||
|
|
||||||
|
void *entry = q->first; |
||||||
|
|
||||||
|
q->first = NULL; |
||||||
|
q->lastp = &q->first; |
||||||
|
|
||||||
|
return entry; |
||||||
|
} |
||||||
|
|
||||||
|
void vlc_queue_Enqueue(vlc_queue_t *q, void *entry) |
||||||
|
{ |
||||||
|
vlc_queue_Lock(q); |
||||||
|
vlc_queue_EnqueueUnlocked(q, entry); |
||||||
|
vlc_queue_Unlock(q); |
||||||
|
} |
||||||
|
|
||||||
|
void *vlc_queue_Dequeue(vlc_queue_t *q) |
||||||
|
{ |
||||||
|
void *entry; |
||||||
|
|
||||||
|
vlc_queue_Lock(q); |
||||||
|
// vlc_testcancel();
|
||||||
|
|
||||||
|
while (vlc_queue_IsEmpty(q)) |
||||||
|
vlc_queue_Wait(q); |
||||||
|
|
||||||
|
entry = vlc_queue_DequeueUnlocked(q); |
||||||
|
vlc_queue_Unlock(q); |
||||||
|
|
||||||
|
return entry; |
||||||
|
} |
||||||
|
|
||||||
|
void *vlc_queue_DequeueAll(vlc_queue_t *q) |
||||||
|
{ |
||||||
|
void *entry; |
||||||
|
|
||||||
|
vlc_queue_Lock(q); |
||||||
|
entry = vlc_queue_DequeueAllUnlocked(q); |
||||||
|
vlc_queue_Unlock(q); |
||||||
|
|
||||||
|
return entry; |
||||||
|
} |
@ -0,0 +1,260 @@ |
|||||||
|
/*****************************************************************************
|
||||||
|
* vlc_queue.h: generic queue functions |
||||||
|
***************************************************************************** |
||||||
|
* Copyright (C) 2020 Rémi Denis-Courmont |
||||||
|
* |
||||||
|
* This program is free software; you can redistribute it and/or modify it |
||||||
|
* under the terms of the GNU Lesser General Public License as published by |
||||||
|
* the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser 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. |
||||||
|
*****************************************************************************/ |
||||||
|
|
||||||
|
#ifndef VLC_QUEUE_H |
||||||
|
#define VLC_QUEUE_H |
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup queue Thread-safe queues (FIFO) |
||||||
|
* @ingroup cext |
||||||
|
* @{ |
||||||
|
* @file vlc_queue.h |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <stdbool.h> |
||||||
|
#include <stdint.h> |
||||||
|
#include <string.h> |
||||||
|
#include <pthread.h> |
||||||
|
|
||||||
|
/**
|
||||||
|
* Opaque type for queue entry. |
||||||
|
*/ |
||||||
|
struct vlc_queue_entry; |
||||||
|
|
||||||
|
/**
|
||||||
|
* Thread-safe queue (a.k.a. FIFO). |
||||||
|
*/ |
||||||
|
typedef struct vlc_queue |
||||||
|
{ |
||||||
|
struct vlc_queue_entry *first; |
||||||
|
struct vlc_queue_entry **lastp; |
||||||
|
ptrdiff_t next_offset; |
||||||
|
// vlc_mutex_t lock;
|
||||||
|
pthread_mutex_t lock; |
||||||
|
// vlc_cond_t wait;
|
||||||
|
pthread_cond_t wait; |
||||||
|
} vlc_queue_t; |
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes a queue. |
||||||
|
* |
||||||
|
* @param queue storage space for the queue |
||||||
|
* @param next_offset offset of the pointer to the next element |
||||||
|
* within a queue entry (as per @c offsetof()) |
||||||
|
*/ |
||||||
|
void vlc_queue_Init(vlc_queue_t *queue, ptrdiff_t next_offset); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup queue_ll Queue internals |
||||||
|
* |
||||||
|
* Low-level queue functions. |
||||||
|
* |
||||||
|
* In some cases, the high-level queue functions do not exactly fit the |
||||||
|
* use case requirements, and it is necessary to access the queue internals. |
||||||
|
* This typically occurs when threads wait for elements to be added to the |
||||||
|
* queue at the same time as some other type of events. |
||||||
|
* @{ |
||||||
|
*/ |
||||||
|
/**
|
||||||
|
* Locks a queue. |
||||||
|
* |
||||||
|
* No more than one thread can lock a queue at any given time, and no other |
||||||
|
* thread can modify the queue while it is locked. |
||||||
|
* Accordingly, if the queue is already locked by another thread, this function |
||||||
|
* waits. |
||||||
|
* |
||||||
|
* Use vlc_queue_Unlock() to release the lock. |
||||||
|
* |
||||||
|
* @warning Recursively locking a single queue is undefined. |
||||||
|
* Also locking more than one queue at a time may lead to lock inversion: |
||||||
|
* mind the locking order! |
||||||
|
*/ |
||||||
|
static inline void vlc_queue_Lock(vlc_queue_t *q) |
||||||
|
{ |
||||||
|
pthread_mutex_lock(&q->lock); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlocks a queue. |
||||||
|
* |
||||||
|
* This releases the lock on a queue, allowing other threads to manipulate the |
||||||
|
* queue. The behaviour is undefined if the calling thread is not holding the |
||||||
|
* queue lock. |
||||||
|
*/ |
||||||
|
static inline void vlc_queue_Unlock(vlc_queue_t *q) |
||||||
|
{ |
||||||
|
pthread_mutex_unlock(&q->lock); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Wakes one thread waiting for a queue entry up. |
||||||
|
*/ |
||||||
|
static inline void vlc_queue_Signal(vlc_queue_t *q) |
||||||
|
{ |
||||||
|
pthread_cond_signal(&q->wait); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Waits for a queue entry. |
||||||
|
* |
||||||
|
* @note This function is a cancellation point. |
||||||
|
* In case of cancellation, the queue will be locked, |
||||||
|
* as is consistent for condition variable semantics. |
||||||
|
* |
||||||
|
* @bug This function should probably not be aware of cancellation. |
||||||
|
*/ |
||||||
|
static inline void vlc_queue_Wait(vlc_queue_t *q) |
||||||
|
{ |
||||||
|
pthread_cond_wait(&q->wait, &q->lock); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Queues an entry (without locking). |
||||||
|
* |
||||||
|
* This function enqueues an entry, or rather a linked-list of entries, in a |
||||||
|
* thread-safe queue, without taking the queue lock. |
||||||
|
* |
||||||
|
* @warning It is assumed that the caller already holds the queue lock; |
||||||
|
* otherwise the behaviour is undefined. |
||||||
|
* |
||||||
|
* @param entry NULL-terminated list of entries to queue |
||||||
|
* (if NULL, this function has no effects) |
||||||
|
*/ |
||||||
|
void vlc_queue_EnqueueUnlocked(vlc_queue_t *, void *entry); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Dequeues the oldest entry (without locking). |
||||||
|
* |
||||||
|
* This function dequeues an entry from a thread-safe queue. It is assumed |
||||||
|
* that the caller already holds the queue lock; otherwise the behaviour is |
||||||
|
* undefined. |
||||||
|
* |
||||||
|
* @warning It is assumed that the caller already holds the queue lock; |
||||||
|
* otherwise the behaviour is undefined. |
||||||
|
* |
||||||
|
* @return the first entry in the queue, or NULL if the queue is empty |
||||||
|
*/ |
||||||
|
void *vlc_queue_DequeueUnlocked(vlc_queue_t *); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Dequeues all entries (without locking). |
||||||
|
* |
||||||
|
* This is equivalent to calling vlc_queue_DequeueUnlocked() repeatedly until |
||||||
|
* the queue is emptied. However this function is much faster than that, as it |
||||||
|
* does not need to update the linked-list pointers. |
||||||
|
* |
||||||
|
* @warning It is assumed that the caller already holds the queue lock; |
||||||
|
* otherwise the behaviour is undefined. |
||||||
|
* |
||||||
|
* @return a linked-list of all entries (possibly NULL if none) |
||||||
|
*/ |
||||||
|
void *vlc_queue_DequeueAllUnlocked(vlc_queue_t *); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a queue is empty (without locking). |
||||||
|
* |
||||||
|
* @warning It is assumed that the caller already holds the queue lock; |
||||||
|
* otherwise the behaviour is undefined. |
||||||
|
* |
||||||
|
* @retval false the queue contains one or more entries |
||||||
|
* @retval true the queue is empty |
||||||
|
*/ |
||||||
|
static inline bool vlc_queue_IsEmpty(const vlc_queue_t *q) |
||||||
|
{ |
||||||
|
return q->first == NULL; |
||||||
|
} |
||||||
|
|
||||||
|
/** @} */ |
||||||
|
|
||||||
|
/**
|
||||||
|
* Queues an entry. |
||||||
|
* |
||||||
|
* This function enqueues an entry, or rather a linked-list of entries, in a |
||||||
|
* thread-safe queue. |
||||||
|
* |
||||||
|
* @param entry list of entries (if NULL, this function has no effects) |
||||||
|
*/ |
||||||
|
void vlc_queue_Enqueue(vlc_queue_t *, void *entry); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Dequeues the oldest entry. |
||||||
|
* |
||||||
|
* This function dequeues an entry from a thread-safe queue. If the queue is |
||||||
|
* empty, it will wait until at least one entry is available. |
||||||
|
* |
||||||
|
* @param offset offset of the next pointer within a queue entry |
||||||
|
* |
||||||
|
* @return the first entry in the queue, or NULL if the queue is empty |
||||||
|
*/ |
||||||
|
void *vlc_queue_Dequeue(vlc_queue_t *); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Dequeues all entries. |
||||||
|
* |
||||||
|
* This is equivalent to calling vlc_queue_Dequeue() repeatedly until the queue |
||||||
|
* is emptied. However this function is much faster than that, as it |
||||||
|
* does not need to update the linked-list pointers. |
||||||
|
* |
||||||
|
* @return a linked-list of all entries (possibly NULL if none) |
||||||
|
*/ |
||||||
|
void *vlc_queue_DequeueAll(vlc_queue_t *); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup queue_killable Killable queues |
||||||
|
* |
||||||
|
* Thread-safe queues with an end flag. |
||||||
|
* |
||||||
|
* @{ |
||||||
|
*/ |
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks a queue ended. |
||||||
|
*/ |
||||||
|
static inline void vlc_queue_Kill(vlc_queue_t *q, |
||||||
|
bool */*restrict*/ tombstone) |
||||||
|
{ |
||||||
|
vlc_queue_Lock(q); |
||||||
|
*tombstone = true; |
||||||
|
vlc_queue_Signal(q); |
||||||
|
vlc_queue_Unlock(q); |
||||||
|
pthread_cond_destroy(&q->wait); |
||||||
|
pthread_mutex_destroy(&q->lock); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Dequeues one entry from a killable queue. |
||||||
|
* |
||||||
|
* @return an entry, or NULL if the queue is empty and has been ended. |
||||||
|
*/ |
||||||
|
static inline void *vlc_queue_DequeueKillable(vlc_queue_t *q, |
||||||
|
bool */*restrict*/ tombstone) |
||||||
|
{ |
||||||
|
void *entry; |
||||||
|
|
||||||
|
vlc_queue_Lock(q); |
||||||
|
while (vlc_queue_IsEmpty(q) && !*tombstone) |
||||||
|
vlc_queue_Wait(q); |
||||||
|
|
||||||
|
entry = vlc_queue_DequeueUnlocked(q); |
||||||
|
vlc_queue_Unlock(q); |
||||||
|
return entry; |
||||||
|
} |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,227 @@ |
|||||||
|
/*****************************************************************************
|
||||||
|
* window.c : Implementation of FFT window routines |
||||||
|
***************************************************************************** |
||||||
|
* Copyright (C) 2014 Ronald Wright |
||||||
|
* |
||||||
|
* Author: Ronald Wright <logiconcepts819@gmail.com> |
||||||
|
* |
||||||
|
* This program is free software; you can redistribute it and/or modify it |
||||||
|
* under the terms of the GNU Lesser General Public License as published by |
||||||
|
* the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser 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 <math.h> |
||||||
|
#include "window.h" |
||||||
|
|
||||||
|
#include <assert.h> |
||||||
|
|
||||||
|
/* Flat top window coefficients */ |
||||||
|
#define FT_A0 1.000f |
||||||
|
#define FT_A1 1.930f |
||||||
|
#define FT_A2 1.290f |
||||||
|
#define FT_A3 0.388f |
||||||
|
#define FT_A4 0.028f |
||||||
|
|
||||||
|
/* Blackman-Harris window coefficients */ |
||||||
|
#define BH_A0 0.35875f |
||||||
|
#define BH_A1 0.48829f |
||||||
|
#define BH_A2 0.14128f |
||||||
|
#define BH_A3 0.01168f |
||||||
|
|
||||||
|
/* Window functions supported by VLC. These are the typical window types used
|
||||||
|
* in spectrum analyzers. */ |
||||||
|
#define NB_WINDOWS 5 |
||||||
|
static const char * const window_list[NB_WINDOWS] = { |
||||||
|
"none", "hann", "flattop", "blackmanharris", "kaiser", |
||||||
|
}; |
||||||
|
static const char * const window_list_text[NB_WINDOWS] = { |
||||||
|
// N_("None"), N_("Hann"), N_("Flat Top"), N_("Blackman-Harris"), N_("Kaiser"),
|
||||||
|
"None", "Hann", "Flat Top", "Blackman-Harris", "Kaiser", |
||||||
|
}; |
||||||
|
|
||||||
|
/*
|
||||||
|
* The modified Bessel function I0(x). See Chapter 6 of the "Numerical Recipes |
||||||
|
* in C: The Art of Scientific Computing" book at |
||||||
|
* http://www.aip.de/groups/soe/local/numres/bookcpdf/c6-6.pdf
|
||||||
|
*/ |
||||||
|
static float bessi0(float x) |
||||||
|
{ |
||||||
|
float ax, ans; |
||||||
|
double y; /* Accumulate polynomials in double precision. */ |
||||||
|
if( ( ax = fabsf( x ) ) < 3.75f ) /* Polynomial fit. */ |
||||||
|
{ |
||||||
|
y = x / 3.75; |
||||||
|
y *= y; |
||||||
|
ans = 1.0 + y * ( 3.5156229 + y * ( 3.0899424 + y * ( 1.2067492 |
||||||
|
+ y * ( 0.2659732 + y * ( 0.360768e-1 |
||||||
|
+ y * 0.45813e-2 ) ) ) ) ); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
y = 3.75 / ax; |
||||||
|
ans = ( exp( ax ) / sqrt( ax ) ) * ( 0.39894228 + y * ( 0.1328592e-1 |
||||||
|
+ y * ( 0.225319e-2 + y * ( -0.157565e-2 + y * ( 0.916281e-2 |
||||||
|
+ y * ( -0.2057706e-1 + y * ( 0.2635537e-1 + y * ( -0.1647633e-1 |
||||||
|
+ y * 0.392377e-2 ) ) ) ) ) ) ) ); |
||||||
|
} |
||||||
|
return ans; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Obtain the window type from the window type variable. |
||||||
|
*/ |
||||||
|
void window_get_param(void * p_aout, window_param * p_param) |
||||||
|
{ |
||||||
|
/* Fetch Kaiser parameter */ |
||||||
|
p_param->f_kaiser_alpha = 1; //TODO var_InheritFloat( p_aout, "effect-kaiser-param" );
|
||||||
|
|
||||||
|
/* Fetch window type */ |
||||||
|
char * psz_preset = "hann"; //TODO var_InheritString( p_aout, "effect-fft-window" );
|
||||||
|
if( !psz_preset ) |
||||||
|
{ |
||||||
|
goto no_preset; |
||||||
|
} |
||||||
|
|
||||||
|
for( int i = 0; i < NB_WINDOWS; i++ ) |
||||||
|
{ |
||||||
|
if( !strcasecmp( psz_preset, window_list[i] ) ) |
||||||
|
{ |
||||||
|
// free( psz_preset );//TODO
|
||||||
|
p_param->wind_type = i; |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
// free( psz_preset );//TODO
|
||||||
|
|
||||||
|
no_preset: |
||||||
|
// msg_Warn( p_aout, "No matching window preset found; using rectangular "
|
||||||
|
// "window (i.e. no window)" );
|
||||||
|
p_param->wind_type = NONE; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialization routine - sets up a lookup table for scaling a sample of data |
||||||
|
* by window data. If the lookup table is successfully allocated, its memory |
||||||
|
* location and its specified size are stored at the specified memory location |
||||||
|
* of the internal context. |
||||||
|
* Returns true if initialization succeeded and returns false otherwise. |
||||||
|
* The internal context should be freed when it is finished with, by |
||||||
|
* window_close(). |
||||||
|
*/ |
||||||
|
bool window_init(int i_buffer_size, window_param * p_param, window_context * p_ctx) |
||||||
|
{ |
||||||
|
float * pf_table = NULL; |
||||||
|
window_type wind_type = p_param->wind_type; |
||||||
|
|
||||||
|
if( wind_type != HANN && wind_type != FLATTOP |
||||||
|
&& wind_type != BLACKMANHARRIS |
||||||
|
&& wind_type != KAISER ) |
||||||
|
{ |
||||||
|
/* Assume a rectangular window (i.e. no window) */ |
||||||
|
i_buffer_size = 0; |
||||||
|
goto exit; |
||||||
|
} |
||||||
|
|
||||||
|
// pf_table = vlc_alloc( i_buffer_size, sizeof( *pf_table ) );
|
||||||
|
pf_table = (float * )malloc( i_buffer_size * sizeof(*pf_table)); |
||||||
|
if( !pf_table ) |
||||||
|
{ |
||||||
|
/* Memory allocation failed */ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
int i_buffer_size_minus_1 = i_buffer_size - 1; |
||||||
|
switch( wind_type ) |
||||||
|
{ |
||||||
|
case HANN: |
||||||
|
/* Hann window */ |
||||||
|
for( int i = 0; i < i_buffer_size; i++ ) |
||||||
|
{ |
||||||
|
float f_val = (float) i / (float) i_buffer_size_minus_1; |
||||||
|
pf_table[i] = 0.5f - 0.5f * cosf( 2.0f * (float) M_PI * f_val ); |
||||||
|
} |
||||||
|
break; |
||||||
|
case FLATTOP: |
||||||
|
/* Flat top window */ |
||||||
|
for( int i = 0; i < i_buffer_size; i++ ) |
||||||
|
{ |
||||||
|
float f_val = (float) i / (float) i_buffer_size_minus_1; |
||||||
|
pf_table[i] = FT_A0 |
||||||
|
- FT_A1 * cosf( 2.0f * (float) M_PI * f_val ) |
||||||
|
+ FT_A2 * cosf( 4.0f * (float) M_PI * f_val ) |
||||||
|
- FT_A3 * cosf( 6.0f * (float) M_PI * f_val ) |
||||||
|
+ FT_A4 * cosf( 8.0f * (float) M_PI * f_val ); |
||||||
|
} |
||||||
|
break; |
||||||
|
case BLACKMANHARRIS: |
||||||
|
/* Blackman-Harris window */ |
||||||
|
for( int i = 0; i < i_buffer_size; i++ ) |
||||||
|
{ |
||||||
|
float f_val = (float) i / (float) i_buffer_size_minus_1; |
||||||
|
pf_table[i] = BH_A0 |
||||||
|
- BH_A1 * cosf( 2.0f * (float) M_PI * f_val ) |
||||||
|
+ BH_A2 * cosf( 4.0f * (float) M_PI * f_val ) |
||||||
|
- BH_A3 * cosf( 6.0f * (float) M_PI * f_val ); |
||||||
|
} |
||||||
|
break; |
||||||
|
case KAISER: |
||||||
|
{ |
||||||
|
/* Kaiser window */ |
||||||
|
float f_pialph = (float) M_PI * p_param->f_kaiser_alpha; |
||||||
|
float f_bessi0_pialph = bessi0( f_pialph ); |
||||||
|
for( int i = 0; i < i_buffer_size; i++ ) |
||||||
|
{ |
||||||
|
float f_val = (float) i / (float) i_buffer_size_minus_1; |
||||||
|
float f_term_to_square = 2.0f * f_val - 1.0f; |
||||||
|
float f_sqd_term = f_term_to_square * f_term_to_square; |
||||||
|
float f_sqr_term = sqrtf( 1.0f - f_sqd_term ); |
||||||
|
pf_table[i] = bessi0( f_pialph * f_sqr_term ) / f_bessi0_pialph; |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
default: |
||||||
|
/* We should not reach here */ |
||||||
|
// vlc_assert_unreachable();
|
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
exit: |
||||||
|
p_ctx->pf_window_table = pf_table; |
||||||
|
p_ctx->i_buffer_size = i_buffer_size; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Perform an in-place scaling of the input buffer by the window data |
||||||
|
* referenced from the specified context. |
||||||
|
*/ |
||||||
|
void window_scale_in_place(int16_t * p_buffer, window_context * p_ctx) |
||||||
|
{ |
||||||
|
for(int i = 0; i < p_ctx->i_buffer_size; i++) |
||||||
|
{ |
||||||
|
p_buffer[i] *= p_ctx->pf_window_table[i]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Free the context. |
||||||
|
*/ |
||||||
|
void window_close(window_context * p_ctx) |
||||||
|
{ |
||||||
|
if(p_ctx->pf_window_table) |
||||||
|
{ |
||||||
|
free(p_ctx->pf_window_table); |
||||||
|
p_ctx->pf_window_table = NULL; |
||||||
|
p_ctx->i_buffer_size = 0; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,69 @@ |
|||||||
|
/*****************************************************************************
|
||||||
|
* window.h : Header for FFT window routines |
||||||
|
***************************************************************************** |
||||||
|
* Copyright (C) 2014 Ronald Wright |
||||||
|
* |
||||||
|
* Author: Ronald Wright <logiconcepts819@gmail.com> |
||||||
|
* |
||||||
|
* This program is free software; you can redistribute it and/or modify it |
||||||
|
* under the terms of the GNU Lesser General Public License as published by |
||||||
|
* the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser 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. |
||||||
|
*****************************************************************************/ |
||||||
|
|
||||||
|
#ifndef VLC_VISUAL_WINDOW_H_ |
||||||
|
#define VLC_VISUAL_WINDOW_H_ |
||||||
|
|
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
#ifndef __cplusplus |
||||||
|
# include <stdbool.h> |
||||||
|
#endif |
||||||
|
|
||||||
|
/* Window type enum */ |
||||||
|
enum _enum_window_type { NONE, HANN, FLATTOP, BLACKMANHARRIS, KAISER }; |
||||||
|
|
||||||
|
/* Window context structure */ |
||||||
|
struct _struct_window_context { |
||||||
|
|
||||||
|
/* Storage for window function values */ |
||||||
|
float * pf_window_table; |
||||||
|
|
||||||
|
/* Buffer size for the window */ |
||||||
|
int i_buffer_size; |
||||||
|
}; |
||||||
|
|
||||||
|
typedef enum _enum_window_type window_type; |
||||||
|
|
||||||
|
/* Window parameter structure */ |
||||||
|
struct _struct_window_param { |
||||||
|
|
||||||
|
/* Window type */ |
||||||
|
window_type wind_type; |
||||||
|
|
||||||
|
/* Kaiser window parameter */ |
||||||
|
float f_kaiser_alpha; |
||||||
|
}; |
||||||
|
|
||||||
|
/* Prototypes for the window function */ |
||||||
|
typedef struct _struct_window_context window_context; |
||||||
|
typedef struct _struct_window_param window_param; |
||||||
|
void window_get_param(void * p_aout, window_param * p_param); |
||||||
|
bool window_init(int i_buffer_size, window_param * p_param, window_context * p_ctx); |
||||||
|
void window_scale_in_place(int16_t * p_buffer, window_context * p_ctx); |
||||||
|
void window_close(window_context * p_ctx); |
||||||
|
|
||||||
|
/* Macro for defining a new window context */ |
||||||
|
#define DEFINE_WIND_CONTEXT(name) window_context name = {NULL, 0} |
||||||
|
|
||||||
|
#endif /* include-guard */ |
Loading…
Reference in new issue