Add: calculate spectrum by running FFT

dev
xufuji456 3 years ago
parent 6480ebadde
commit d51bdd6b6a
  1. 4
      app/CMakeLists.txt
  2. 232
      app/src/main/cpp/visualizer/execute_fft.c
  3. 77
      app/src/main/cpp/visualizer/execute_fft.h
  4. 206
      app/src/main/cpp/visualizer/fft.c
  5. 55
      app/src/main/cpp/visualizer/fft.h
  6. 155
      app/src/main/cpp/visualizer/queue.c
  7. 260
      app/src/main/cpp/visualizer/vlc_queue.h
  8. 227
      app/src/main/cpp/visualizer/window.c
  9. 69
      app/src/main/cpp/visualizer/window.h

@ -34,6 +34,10 @@ add_library( # Sets the name of the library.
src/main/cpp/video_filter.c src/main/cpp/video_filter.c
src/main/cpp/fast_start.c src/main/cpp/fast_start.c
src/main/cpp/ffprobe_cmd.cpp src/main/cpp/ffprobe_cmd.cpp
src/main/cpp/visualizer/fft.c
src/main/cpp/visualizer/queue.c
src/main/cpp/visualizer/execute_fft.c
src/main/cpp/visualizer/window.c
) )
add_library( ffmpeg add_library( ffmpeg

@ -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…
Cancel
Save